Merge remote-tracking branch 'origin/main' into maintainer/pr-517-tests
This commit is contained in:
commit
037e321a79
5 files changed
+197
-33
No files matched your search
Vendored
+18
-16
@@ -4193,13 +4193,13 @@ if (typeof Response === 'undefined') {
|
||||
}
|
||||
|
||||
if (!Element.prototype.replaceWith) {
|
||||
// _convertNodes turns any non-node argument (numbers, booleans, null, …) into
|
||||
// a Text node via String(n), matching the spec and append()/prepend(); the
|
||||
// old `typeof n === 'string'` check corrupted insert_before for other types.
|
||||
Element.prototype.replaceWith = function(...nodes) {
|
||||
const parent = this.parentNode;
|
||||
if (!parent) return;
|
||||
for (const n of nodes) {
|
||||
if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), this);
|
||||
else parent.insertBefore(n, this);
|
||||
}
|
||||
for (const n of _convertNodes(nodes)) parent.insertBefore(n, this);
|
||||
parent.removeChild(this);
|
||||
};
|
||||
_markNative(Element.prototype.replaceWith);
|
||||
@@ -4208,10 +4208,7 @@ if (!Element.prototype.before) {
|
||||
Element.prototype.before = function(...nodes) {
|
||||
const parent = this.parentNode;
|
||||
if (!parent) return;
|
||||
for (const n of nodes) {
|
||||
if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), this);
|
||||
else parent.insertBefore(n, this);
|
||||
}
|
||||
for (const n of _convertNodes(nodes)) parent.insertBefore(n, this);
|
||||
};
|
||||
_markNative(Element.prototype.before);
|
||||
}
|
||||
@@ -4220,10 +4217,7 @@ if (!Element.prototype.after) {
|
||||
const parent = this.parentNode;
|
||||
if (!parent) return;
|
||||
const ref = this.nextSibling;
|
||||
for (const n of nodes) {
|
||||
if (typeof n === 'string') parent.insertBefore(document.createTextNode(n), ref);
|
||||
else parent.insertBefore(n, ref);
|
||||
}
|
||||
for (const n of _convertNodes(nodes)) parent.insertBefore(n, ref);
|
||||
};
|
||||
_markNative(Element.prototype.after);
|
||||
}
|
||||
@@ -5465,11 +5459,16 @@ globalThis.XMLSerializer = class XMLSerializer {
|
||||
};
|
||||
globalThis.performance = globalThis.performance || {
|
||||
now: (function() {
|
||||
var _lastMs = -1, _sub = 0;
|
||||
// Monotonically non-decreasing: return the wall-clock offset, but never a
|
||||
// value below the last one. Equal readings are allowed, and avoiding a
|
||||
// synthetic per-call increment keeps tight loops from advancing the clock
|
||||
// faster than real elapsed time.
|
||||
var _last = -Infinity;
|
||||
return function() {
|
||||
var ms = Date.now() - (globalThis.performance.timeOrigin || 0);
|
||||
if (ms !== _lastMs) { _lastMs = ms; _sub = 0; } else { _sub += 0.1; }
|
||||
return ms + _sub;
|
||||
if (ms < _last) return _last;
|
||||
_last = ms;
|
||||
return _last;
|
||||
};
|
||||
})(),
|
||||
mark(){}, measure(){},
|
||||
@@ -5718,7 +5717,10 @@ globalThis.atob = globalThis.atob || ((s) => { const c="ABCDEFGHIJKLMNOPQRSTUVWX
|
||||
const stack = [{state: null, url: undefined}]; // initial entry; url=undefined means "use document URL"
|
||||
let idx = 0;
|
||||
const resolveOrFallback = (url) => {
|
||||
if (url === null || url === undefined) return undefined;
|
||||
// A missing url (pushState/replaceState called with < 3 args) keeps the
|
||||
// current document URL per the HTML spec — capture it so the entry does not
|
||||
// reset location back to the original document URL.
|
||||
if (url === null || url === undefined) return __currentUrl();
|
||||
try { return new URL(String(url), __currentUrl()).href; } catch (e) { return String(url); }
|
||||
};
|
||||
const applyVirtual = () => {
|
||||
|
||||
@@ -1346,6 +1346,75 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn performance_now_is_monotonic_under_bursty_calls() {
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
// Hammer performance.now() so many calls land in the same millisecond and
|
||||
// the wall clock rolls over repeatedly; the value must never go backwards.
|
||||
let violations = rt
|
||||
.evaluate(
|
||||
"(function(){var prev=-Infinity, bad=0; for(var i=0;i<500000;i++){var t=performance.now(); if(t<prev) bad++; prev=t;} return bad;})()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(violations.as_f64(), Some(0.0), "performance.now() went backwards");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn performance_now_does_not_outrun_elapsed_time() {
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
let lead = rt
|
||||
.evaluate(
|
||||
"(function(){for(var i=0;i<500000;i++)performance.now(); return performance.now()-(Date.now()-performance.timeOrigin);})()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
lead.as_f64().unwrap() <= 1.0,
|
||||
"performance.now() advanced ahead of elapsed time: {lead}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn childnode_helpers_coerce_non_string_primitives_to_text() {
|
||||
let mut rt = setup_runtime(r#"<html><body><div id="p"><span id="t">x</span></div></body></html>"#);
|
||||
let before = rt
|
||||
.evaluate("(function(){var t=document.getElementById('t'); t.before(5); return t.previousSibling ? t.previousSibling.textContent : 'NULL';})()")
|
||||
.unwrap();
|
||||
assert_eq!(before, serde_json::json!("5"));
|
||||
let after = rt
|
||||
.evaluate("(function(){var t=document.getElementById('t'); t.after(true); return t.nextSibling ? t.nextSibling.textContent : 'NULL';})()")
|
||||
.unwrap();
|
||||
assert_eq!(after, serde_json::json!("true"));
|
||||
let replaced = rt
|
||||
.evaluate("(function(){var t=document.getElementById('t'); t.replaceWith(42); return document.getElementById('p').textContent;})()")
|
||||
.unwrap();
|
||||
assert!(
|
||||
replaced.as_str().unwrap().contains("42"),
|
||||
"replaceWith(42) should leave text '42': {replaced}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_state_without_url_preserves_current_location() {
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
let path = rt
|
||||
.evaluate(
|
||||
"(function(){history.pushState({}, '', '/dashboard'); history.replaceState({scroll:1}); return location.pathname;})()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(path, serde_json::json!("/dashboard"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_state_without_url_preserves_current_location() {
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
let path = rt
|
||||
.evaluate(
|
||||
"(function(){history.pushState({}, '', '/a'); history.pushState({b:1}); return location.pathname;})()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(path, serde_json::json!("/a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_document_title() {
|
||||
let mut rt = setup_runtime("<html><head><title>Test</title></head><body></body></html>");
|
||||
|
||||
Reference in new issue
Block a user