test(document): cover child input-stream readiness

This commit is contained in:
ldm0
2026-10-21 06:11:29 +08:00
committed by Donough Liu
parent 86a078d18d
commit a86afb442e
2 changed files with 129 additions and 0 deletions
+3
View File
@@ -6728,6 +6728,9 @@ mod document_write_errors;
#[path = "scripts/child_markup_insertion.rs"]
mod child_markup_insertion;
#[path = "scripts/child_input_stream_readiness.rs"]
mod child_input_stream_readiness;
#[path = "scripts/document_close.rs"]
mod document_close;
@@ -0,0 +1,126 @@
use super::*;
async fn child_stream_readiness_probe(operation: &str) -> Result<serde_json::Value> {
let server = FixtureServer::spawn().await?;
let browser = Browser::new(AppConfig::default())?;
let mut page = browser.fetch(&server.url("/static")).await?;
let expression = r#"(async () => {
const frame = await new Promise(resolve => {
const frame = document.createElement('iframe');
frame.onload = () => resolve(frame);
frame.src = '/static';
document.body.append(frame);
});
frame.onload = null;
const doc = frame.contentDocument;
const win = frame.contentWindow;
const oldBody = doc.body;
let oldEvents = 0;
doc.onreadystatechange = () => ++oldEvents;
doc.addEventListener('readystatechange', () => ++oldEvents);
win.addEventListener('readystatechange', () => ++oldEvents, true);
oldBody.addEventListener('old-listener-probe', () => ++oldEvents);
const operation = OPERATION;
let opened = null;
if (operation === 'open' || operation === 'reopen') {
const same = doc.open() === doc;
opened = {same, readyState: doc.readyState, children: doc.childNodes.length};
}
const markup = '<p id="written">new stream</p><script>' +
'window.streamScriptReadyState = document.readyState;' + '<' + '/script>';
if (operation === 'writeln') doc.writeln(markup);
else doc.write(markup);
let reopened = null;
if (operation === 'reopen') {
doc.addEventListener('readystatechange', () => ++oldEvents);
const same = doc.open() === doc;
reopened = {same, readyState: doc.readyState, children: doc.childNodes.length};
doc.write(markup);
}
oldBody.dispatchEvent(new Event('old-listener-probe'));
const during = {readyState: doc.readyState,
scriptReadyState: win.streamScriptReadyState,
sameDocument: doc === frame.contentDocument,
text: doc.getElementById('written').textContent,
handlerCleared: doc.onreadystatechange === null,
trailingNewline: doc.body.lastChild.nodeType === Node.TEXT_NODE &&
doc.body.lastChild.data === '\n'};
const states = [];
doc.addEventListener('readystatechange', () => states.push(doc.readyState));
const finished = await new Promise(resolve => {
win.addEventListener('load', () => resolve({readyState: doc.readyState,
states, oldEvents, parentReadyState: document.readyState}), {once: true});
doc.close();
});
frame.remove();
return JSON.stringify({opened, reopened, during, finished});
})()"#
.replace("OPERATION", &serde_json::to_string(operation)?);
let result = page
.evaluate_runtime_expression_with_await_async(&expression, true)
.await?;
server.shutdown().await;
serde_json::from_str(
result["value"]
.as_str()
.expect("child stream readiness probe"),
)
.map_err(Into::into)
}
fn assert_child_stream_readiness(result: &serde_json::Value, newline: bool) {
assert_eq!(
result["during"],
serde_json::json!({
"readyState": "loading", "scriptReadyState": "loading", "sameDocument": true,
"text": "new stream", "handlerCleared": true, "trailingNewline": newline
}),
"{result}"
);
assert_eq!(
result["finished"],
serde_json::json!({
"readyState": "complete", "states": ["interactive", "complete"],
"oldEvents": 0, "parentReadyState": "complete"
}),
"{result}"
);
}
#[tokio::test(flavor = "multi_thread")]
async fn child_document_open_resets_readiness_after_erasing_listeners() -> Result<()> {
let result = child_stream_readiness_probe("open").await?;
assert_eq!(
result["opened"],
serde_json::json!({"same": true, "readyState": "loading", "children": 0}),
"{result}"
);
assert_child_stream_readiness(&result, false);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_document_write_implicit_open_resets_readiness() -> Result<()> {
let result = child_stream_readiness_probe("write").await?;
assert_child_stream_readiness(&result, false);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_document_writeln_implicit_open_resets_readiness() -> Result<()> {
let result = child_stream_readiness_probe("writeln").await?;
assert_child_stream_readiness(&result, true);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn child_document_open_replaces_an_already_loading_stream() -> Result<()> {
let result = child_stream_readiness_probe("reopen").await?;
assert_eq!(
result["reopened"],
serde_json::json!({"same": true, "readyState": "loading", "children": 0}),
"{result}"
);
assert_child_stream_readiness(&result, false);
Ok(())
}