mirror of
https://github.com/lexmount/moli.git
synced 2026-09-23 08:01:28 +00:00
Stream remaining original loaders, synchronous XHR, manifest and popup resources with their real identities and metadata. Remove obsolete cross-layer ownership entries and preserve exact command routes for error replies.
6721 lines
218 KiB
Rust
6721 lines
218 KiB
Rust
#[path = "scripts/child_script_text.rs"]
|
|
mod child_script_text;
|
|
#[path = "scripts/integrity.rs"]
|
|
mod integrity;
|
|
|
|
use moli_test_support as support;
|
|
|
|
use anyhow::Result;
|
|
use moli_core::{
|
|
runtime::{Browser, BrowserConfig as AppConfig, RenderedDomWaitUntil},
|
|
testing::JsValueSnapshot,
|
|
};
|
|
use support::FixtureServer;
|
|
use tokio::time::Duration;
|
|
|
|
fn diagnostic_global<'a>(
|
|
page: &'a moli_core::page::Page,
|
|
name: &str,
|
|
) -> Option<&'a JsValueSnapshot> {
|
|
page.script_execution().global(name)
|
|
}
|
|
|
|
fn diagnostic_global_string<'a>(page: &'a moli_core::page::Page, name: &str) -> Option<&'a str> {
|
|
match diagnostic_global(page, name) {
|
|
Some(JsValueSnapshot::String(value)) => Some(value.as_str()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn assert_order_contains(order: &str, marker: &str) {
|
|
assert!(
|
|
order.split(',').any(|part| part == marker),
|
|
"expected order to contain `{marker}`, got `{order}`"
|
|
);
|
|
}
|
|
|
|
fn assert_order_before(order: &str, before: &str, after: &str) {
|
|
let parts: Vec<_> = order.split(',').collect();
|
|
let before_index = parts
|
|
.iter()
|
|
.position(|part| *part == before)
|
|
.unwrap_or_else(|| panic!("expected order to contain `{before}`, got `{order}`"));
|
|
let after_index = parts
|
|
.iter()
|
|
.position(|part| *part == after)
|
|
.unwrap_or_else(|| panic!("expected order to contain `{after}`, got `{order}`"));
|
|
assert!(
|
|
before_index < after_index,
|
|
"expected `{before}` before `{after}`, got `{order}`"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn parse_time_inline_classic_scripts_observe_partial_dom_during_initial_parse() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-inline-classic"))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-inline-before-late=\"missing\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-inline-after-late=\"seen\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-inline-trace=\"inline-1\"")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_external_classic_scripts_observe_partial_dom_during_initial_parse() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-external-classic"))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-external-before-late=\"missing\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-external-trace=\"external\"")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_external_script_src_uses_document_base_url() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/script-src-base-alpha/page"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "scriptSrcBaseResult"),
|
|
Some(&JsValueSnapshot::String("beta".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_defer_classic_scripts_still_run_post_parse_before_domcontentloaded()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-defer-classic"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDeferSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDeferSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_scripts_can_run_during_parse_before_late_dom_and_domcontentloaded()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("<div id=\"late\">late</div>"),
|
|
"late DOM after the async handoff must remain visible in the final page HTML"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_scripts_can_run_between_parser_chunks_before_late_dom()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = tokio::time::timeout(
|
|
Duration::from_secs(5),
|
|
browser.fetch(&server.url("/compat/parse-time-async-classic-chunked")),
|
|
)
|
|
.await
|
|
.expect("async script must execute and release the pending HTML tail")?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawTail"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("<div id=\"late\">late</div>"),
|
|
"the acknowledged HTML tail must reach the final DOM"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_slow_classic_script_does_not_block_later_streaming_chunks() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-slow-chunked-tail"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "slowChunkDclOrder"),
|
|
Some(&JsValueSnapshot::String("defer,dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "slowChunkFinalOrder"),
|
|
Some(&JsValueSnapshot::String("defer,dcl,slow-async".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_scripts_can_run_inside_single_outer_chunk_via_parse_pump()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-pumped"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_slow_async_classic_scripts_can_remain_after_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-slow"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSlowSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSlowSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_scripts_keep_microtasks_between_page_task_turns() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-task-turns"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "asyncTaskOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"first-script,first-microtask,second-script".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_first_task_turn_stays_before_late_dom_and_domcontentloaded()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-task-turn-visibility"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "asyncTaskVisibilityOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"first-script,first-microtask,second-script".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "asyncTaskVisibilityFirstSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "asyncTaskVisibilityFirstSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_post_parse_turns_keep_microtasks_and_stay_before_dcl()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-post-parse-turns"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "postParseAsyncTaskOrderResult"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_fast_vs_slow_post_parse_claims_split_at_dcl_boundary()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let fast_page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-post-parse-turns"))
|
|
.await?;
|
|
let slow_page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-post-parse-slow-second"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&fast_page, "postParseAsyncTaskOrderResult"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&slow_page, "postParseSlowDclOrder"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&slow_page, "postParseSlowFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,first-script,first-microtask,second-script,second-after-dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_async_classic_post_parse_slow_second_falls_back_after_dcl() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-post-parse-slow-second"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "postParseSlowDclOrder"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "postParseSlowFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,first-script,first-microtask,second-script,second-after-dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_defer_and_module_scripts_share_one_document_ordered_phase() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-defer-module-order"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "deferLikeDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-left,defer-left-microtask,module,module-microtask,defer-right,defer-right-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn final_parser_classic_terminal_follows_script_reaction_and_precedes_dcl() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-final-classic-terminal-before-dcl"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "finalClassicTerminalDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"script-body,script-body-microtask,script-load,script-load-microtask,dcl".to_owned()
|
|
)),
|
|
"classic evaluation reactions must settle before its terminal body, and the terminal task must settle before exact DCL"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "finalClassicTerminalTimerAtDcl"),
|
|
Some(&JsValueSnapshot::Bool(false)),
|
|
"ordinary timer arbitration must not open between parser completion and exact DCL"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn final_parser_module_terminal_microtask_precedes_dcl_without_timer_arbitration()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-final-module-terminal-before-dcl"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "finalModuleTerminalDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-body,module-body-microtask,module-load,module-load-microtask,dcl".to_owned()
|
|
)),
|
|
"the final parser module task must finish its terminal reactions before exact DCL"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "finalModuleTerminalTimerAtDcl"),
|
|
Some(&JsValueSnapshot::Bool(false)),
|
|
"ordinary timer arbitration must not open between parser completion and exact DCL"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_blocking_external_script_waits_for_blocking_stylesheet() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-parser-blocking-external"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_blocking_external_document_write_keeps_insertion_point_after_blocking_stylesheet()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-parser-blocking-document-write"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserWriteBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserWriteSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetParserWriteResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ran|no-late|written-during-script|before-late".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn defer_script_waits_for_blocking_stylesheet_before_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-defer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetDeferBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetDeferSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetDeferSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_script_waits_for_blocking_stylesheet_before_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetModuleBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetModuleSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetModuleSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetModuleDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module,module-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn phase_two_custom_element_upgrade_stylesheet_does_not_block_parser_owned_defer()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/phase-two-upgrade-runtime-style-defer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "phaseTwoUpgradeDeferSawLoaded"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "phaseTwoUpgradeDeferSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
match diagnostic_global(&page, "phaseTwoUpgradeDeferDclOrder") {
|
|
Some(JsValueSnapshot::String(order)) => {
|
|
assert!(order.starts_with("connected,defer"));
|
|
}
|
|
other => panic!("unexpected defer order snapshot: {other:?}"),
|
|
}
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn phase_two_custom_element_upgrade_stylesheet_does_not_block_parser_owned_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/phase-two-upgrade-runtime-style-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "phaseTwoUpgradeModuleSawLoaded"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "phaseTwoUpgradeModuleSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
match diagnostic_global(&page, "phaseTwoUpgradeModuleDclOrder") {
|
|
Some(JsValueSnapshot::String(order)) => {
|
|
assert!(order.starts_with("connected,module,module-microtask"));
|
|
}
|
|
other => panic!("unexpected module order snapshot: {other:?}"),
|
|
}
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_blocking_external_script_waits_for_parser_created_style_import() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/blocking-stylesheet-parser-created-style-import-parser-blocking-external",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportParserBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportParserSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportParserSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_script_waits_for_parser_created_style_import_before_domcontentloaded() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-parser-created-style-import-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportModuleBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportModuleSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportModuleSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStyleImportModuleDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module,module-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn domcontentloaded_is_indirectly_delayed_by_blocking_stylesheet_gated_defer_work()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-defer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetDeferDclBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetDeferDclOrder"),
|
|
Some(&JsValueSnapshot::String("defer,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn alternate_stylesheet_does_not_block_parser_blocking_script() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/blocking-stylesheet-alternate-non-blocking"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetAlternateBlocked"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetAlternateSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "blockingStylesheetAlternateSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_stylesheet_load_waits_for_fetch_completion() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-stylesheet-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedStylesheetLoadBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedStylesheetLoadFinalOrder"),
|
|
Some(&JsValueSnapshot::String("dcl,style-load".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_stylesheet_load_mutation_syncs_back_into_parser_before_later_script()
|
|
-> Result<()> {
|
|
// Runtime-inserted stylesheets are intentionally not parser/script-blocking
|
|
// stylesheets under HTML's parser-created stylesheet rule. The fixture keeps
|
|
// the runtime stylesheet fast and the parser-owned stylesheet slow, so this
|
|
// test covers only the renderer/parser snapshot boundary: a mutation made by
|
|
// an already-fired runtime stylesheet load handler must be visible before
|
|
// the later parser-inserted script executes.
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-stylesheet-load-syncs-parser-snapshot"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedStyleSnapshotSawMarker"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedStyleSnapshotSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedStyleSnapshotParserSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_stylesheet_load_can_trigger_location_replace() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-stylesheet-load-triggers-location-replace"))
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(page.final_url().query(), Some("from=style-load"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=style-load")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_stylesheet_href_mutation_uses_fresh_fetch_for_parser_blocking_progress()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-stylesheet-href-mutation-uses-fresh-fetch"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedHrefMutationParserNotBlocked"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInsertedHrefMutationSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_preload_and_modulepreload_do_not_overtake_later_parser_script()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-preload-and-modulepreload-parser-progress"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimePreloadProgressParserSawLate"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimePreloadProgressParserSawPreloadLoad"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimePreloadProgressParserSawModulepreloadLoad"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_module_graph_waits_for_modulepreloaded_static_dependency() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/modulepreload-shared-static-dependency"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadSharedError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadSharedValue"),
|
|
Some(&JsValueSnapshot::String("leaf-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadSharedSawLeafBeforeMid"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_order_contains(
|
|
diagnostic_global_string(&page, "modulepreloadSharedFinalOrder").unwrap_or(""),
|
|
"root",
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_module_graph_waits_for_duplicate_modulepreloaded_static_dependency() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/modulepreload-duplicate-shared-static-dependency"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadDuplicateError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadDuplicateValue"),
|
|
Some(&JsValueSnapshot::String("shared-ok|shared-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadDuplicateLeafEvalCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "modulepreloadDuplicateFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "leaf", "parent-a");
|
|
assert_order_before(final_order, "leaf", "parent-b");
|
|
assert_order_before(final_order, "parent-a", "root");
|
|
assert_order_before(final_order, "parent-b", "root");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn duplicate_parser_module_root_evaluates_once_but_each_script_finishes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/duplicate-module-root-eval"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "duplicateModuleRootError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "duplicateModuleRootEvalCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "duplicateModuleRootFinalOrder").unwrap_or("");
|
|
assert_order_contains(final_order, "root");
|
|
assert_order_contains(final_order, "root-microtask");
|
|
assert_order_contains(final_order, "load-a");
|
|
assert_order_contains(final_order, "load-b");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn duplicate_parser_module_root_with_nested_dependencies_evaluates_once() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/duplicate-module-root-with-nested-dependencies"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "duplicateNestedModuleFinalLog"),
|
|
Some(&JsValueSnapshot::String(
|
|
"this-undefined,this-nested".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_top_level_fetch_and_mime_errors_dispatch_script_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-top-level-fetch-and-mime-errors"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTopLevelFailureFinalEvents"),
|
|
Some(&JsValueSnapshot::String(
|
|
"missing-error,mime-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_module_graph_waits_for_modulepreloaded_reused_parent_pending_child() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/modulepreload-reused-parent-pending-child"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadReusedParentError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadReusedParentValue"),
|
|
Some(&JsValueSnapshot::String("child-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "modulepreloadReusedParentSawChildBeforeParent"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "modulepreloadReusedParentFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "child", "parent");
|
|
assert_order_before(final_order, "parent", "root");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn modulepreloaded_css_module_feeds_declarative_shadow_adopted_stylesheets() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/declarative-shadow-adopted-stylesheets-modulepreload"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(3),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "shadowModulepreloadResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"1|1|1|span { color: blue; }|true|rgb(0, 0, 255)|rgb(0, 0, 255)".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn css_dynamic_import_fills_declarative_shadow_adopted_stylesheet_placeholder() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/declarative-shadow-adopted-stylesheets-dynamic-import"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(3),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "shadowCssImportResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"true|true|1|span { color: blue; }|rgb(0, 0, 255)".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn phase_two_shared_blocking_stylesheet_keeps_parser_owned_defer_phase_order() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/phase-two-shared-blocking-stylesheet-defer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "sharedBlockingDeferBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "sharedBlockingDeferOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-left,defer-left-microtask,defer-right,defer-right-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn phase_two_shared_blocking_stylesheet_keeps_parser_owned_module_phase_order() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/phase-two-shared-blocking-stylesheet-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "sharedBlockingModuleBlockedEnough"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "sharedBlockingModuleOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-left,module-left-microtask,module-right,module-right-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_does_not_wait_for_runtime_inserted_blocking_stylesheet() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-waits-for-runtime-inserted-stylesheet"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicBlockingStylesheetSawStyleLoaded"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicBlockingStylesheetSawLate"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicBlockingStylesheetSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicBlockingStylesheetSawLoad"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicBlockingStylesheetFinalOrder"),
|
|
Some(&JsValueSnapshot::String("dcl,dynamic,load".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_async_script_can_overtake_slower_in_order_script() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-async-overtakes-in-order"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicScriptTaxonomyOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"async-fast,in-order-slow".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_in_order_scripts_keep_append_order_when_front_is_slow() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-in-order-preserves-order"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"in-order-slow,in-order-fast".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"in-order-slow,in-order-fast,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_dynamic_script_load_waits_until_parser_progress_restores_state() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-dynamic-script-load-after-parser-progress"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicRestored"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicSaw"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicUnsafeMarker"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"clobber-script,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"clobber-script,restore-inline,dynamic-script,dynamic-load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicLastError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert!(
|
|
!page
|
|
.script_execution()
|
|
.lifecycle_errors()
|
|
.iter()
|
|
.any(|message| message.contains("invokeApps")
|
|
|| message.contains("Cannot read properties of undefined")),
|
|
"unexpected lifecycle errors: {:?}",
|
|
page.script_execution().lifecycle_errors()
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_dynamic_script_error_waits_until_parser_progress_restores_state() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-dynamic-script-error-after-parser-progress"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicErrorRestored"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicErrorSaw"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicErrorRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"clobber-script,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeDynamicErrorFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"clobber-script,restore-inline,dynamic-error".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_inserted_inline_script_does_not_dispatch_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-inserted-inline-script-does-not-dispatch-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInlineLoadSaw"),
|
|
Some(&JsValueSnapshot::String("not-fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInlineLoadOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"script-run,after-append".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_script_load_fires_before_later_restore_inline() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-external-script-load-after-page-task-turn"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWritePageTaskRestored"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWritePageTaskSaw"),
|
|
Some(&JsValueSnapshot::String("missing".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWritePageTaskRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"written-script,written-load,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWritePageTaskFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"written-script,written-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_script_delayed_load_does_not_block_parent_runtime() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/document-write-delayed-external-script-does-not-block-parent-runtime",
|
|
))
|
|
.await?;
|
|
|
|
let Some(JsValueSnapshot::Number(elapsed)) =
|
|
diagnostic_global(&page, "documentWriteDelayedAfterElapsed")
|
|
else {
|
|
panic!("missing documentWriteDelayedAfterElapsed diagnostic");
|
|
};
|
|
assert!(
|
|
*elapsed < 60.0,
|
|
"document.write returned after {elapsed}ms; delayed script load should not block parent JS"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDelayedAfterOrder"),
|
|
Some(&JsValueSnapshot::String("outer-after-write".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDelayedExternalSawOuter"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDelayedRestoreSawExternal"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDelayedRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"outer-after-write,external-script,external-load,restore-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_open_during_parser_script_with_pending_written_external_is_ignored() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/document-open-during-parser-script-with-pending-written-external-is-ignored",
|
|
))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("id=\"written-before-ignored-open\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("id=\"written-after-ignored-open\"")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentOpenParserExternalRan"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentOpenParserInlineRan"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentOpenParserOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-schedule,external-script,written-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_external_classic_dispatches_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-connected-external-classic-dispatches-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedLoadSaw"),
|
|
Some(&JsValueSnapshot::String("fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedLoadFinalOrder"),
|
|
Some(&JsValueSnapshot::String("external-load".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedLoadAfterExternalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"external-load,after-external".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_external_classic_load_document_write_uses_insertion_point() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-connected-external-classic-load-document-write-insertion-point",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedLoadWriteText"),
|
|
Some(&JsValueSnapshot::String("Some text".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedLoadWriteReadyState"),
|
|
Some(&JsValueSnapshot::String("loading".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_external_classic_load_document_write_notifies_parent() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-connected-external-classic-load-document-write-parent-callback",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedFrameLoadWriteObservedText"),
|
|
Some(&JsValueSnapshot::String("Some text".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedFrameLoadWriteObservedCallbackType"),
|
|
Some(&JsValueSnapshot::String("function".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedFrameLoadWriteCall"),
|
|
Some(&JsValueSnapshot::String("called".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedFrameLoadWriteText"),
|
|
Some(&JsValueSnapshot::String("Some text".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_external_classic_error_microtask_precedes_later_inline() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-connected-external-classic-error-microtask"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedErrorSaw"),
|
|
Some(&JsValueSnapshot::String("fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedErrorDuring"),
|
|
Some(&JsValueSnapshot::String("error".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedErrorAfterInline"),
|
|
Some(&JsValueSnapshot::String(
|
|
"error,error-microtask,after-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedErrorFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"error,error-microtask,after-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_external_classic_unknown_scheme_error_document_write_continues()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-connected-external-classic-unknown-scheme-errors-and-continues",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedUnknownSchemeFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"unknown-error,unknown-written,missing-error,missing-written,after-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedUnknownSchemeUnknownSawFlag"),
|
|
Some(&JsValueSnapshot::String("false".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedUnknownSchemeMissingSawFlag"),
|
|
Some(&JsValueSnapshot::String("true".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedUnknownSchemeFinalReadyStates"),
|
|
Some(&JsValueSnapshot::String(
|
|
"unknown-error:loading,unknown-written:loading,missing-error:loading,missing-written:loading".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserConnectedUnknownSchemeScriptCount"),
|
|
Some(&JsValueSnapshot::Number(6.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_connected_inline_classic_does_not_dispatch_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-connected-inline-classic-does-not-dispatch-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserInlineLoadSaw"),
|
|
Some(&JsValueSnapshot::String("fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserInlineLoadAttributeTarget"),
|
|
Some(&JsValueSnapshot::String("dynamic".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserInlineLoadFinalTargets"),
|
|
Some(&JsValueSnapshot::String("dynamic".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_external_defer_dispatches_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-external-defer-dispatches-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedDeferLoadSaw"),
|
|
Some(&JsValueSnapshot::String("fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedDeferLoadFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"inline-defer,external-load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedDeferLoadAfterInlineOrder"),
|
|
Some(&JsValueSnapshot::String("inline-defer".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_external_async_dispatches_load_after_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-external-async-dispatches-load"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadSaw"),
|
|
Some(&JsValueSnapshot::String("fired".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadSawTail"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadAfterDclOrder"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,async-script,script-load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedAsyncLoadWindowOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,async-script,script-load,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_in_order_load_dispatches_after_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-in-order-load-after-domcontentloaded"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderLoadAfterDcl"),
|
|
Some(&JsValueSnapshot::String("after-append,dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderLoadDuringLoad"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append,dcl,external-script,load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderLoadFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append,dcl,external-script,load,load-microtask,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_in_order_error_dispatches_after_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-in-order-error-after-domcontentloaded"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderErrorAfterDcl"),
|
|
Some(&JsValueSnapshot::String("dcl,after-append".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderErrorDuringError"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,after-append,error".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderErrorFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,after-append,error,error-microtask,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_in_order_with_defer_still_executes_after_domcontentloaded()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/runtime-owned-external-in-order-with-defer-stays-after-domcontentloaded",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderWithDeferAfterDeferOrder"),
|
|
Some(&JsValueSnapshot::String("after-append,defer".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderWithDeferDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append,defer,dcl".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderWithDeferLoadOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append,defer,dcl,external-script,load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderWithDeferFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append,defer,dcl,external-script,load,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_in_order_from_domcontentloaded_handler_stays_after_domcontentloaded()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-in-order-from-domcontentloaded-handler"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderFromDclAfterDclOrder"),
|
|
Some(&JsValueSnapshot::String("dcl,after-append".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderFromDclLoadOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,after-append,external-script,load,load-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInOrderFromDclFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl,after-append,external-script,load,load-microtask,window-load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_async_does_not_block_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-async-does-not-block-domcontentloaded"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncClassicDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncClassicDuringLoad"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive,external-script,load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncClassicFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive,external-script,load,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_async_fast_does_not_overtake_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/runtime-owned-external-async-fast-does-not-overtake-domcontentloaded",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncFastDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncFastLoadOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive,external-script:interactive,load:interactive"
|
|
.to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncFastFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,dcl:interactive,external-script:interactive,load:interactive,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_async_can_run_before_a_delayed_streaming_tail() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-async-fast-streaming-tail"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncFastDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,external-script:loading,load:loading,dcl:interactive"
|
|
.to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncFastFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,external-script:loading,load:loading,dcl:interactive,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_inline_module_single_line_import_executes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-inline-module-single-line-import-executes"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeInlineModuleErrors"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
let final_order = match diagnostic_global(&page, "runtimeInlineModuleFinalOrder") {
|
|
Some(JsValueSnapshot::String(order)) => order.as_str(),
|
|
other => panic!("runtime inline module should publish its final order, got {other:?}"),
|
|
};
|
|
assert!(
|
|
matches!(
|
|
final_order,
|
|
"ready:loading,after-append,dcl:interactive,module:1,module-microtask,window-load:complete"
|
|
| "ready:loading,after-append,module:1,module-microtask,dcl:interactive,window-load:complete"
|
|
),
|
|
"runtime-created async module and DOMContentLoaded may race, but both must finish before Window load: {final_order}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_inline_module_runs_while_parser_defer_is_blocked() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url("/compat/runtime-owned-inline-module-runs-while-parser-defer-is-blocked"),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeModuleBlockedDeferFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ready:loading,after-append,module:interactive,module-microtask,defer,dcl:interactive,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_async_with_defer_does_not_block_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/runtime-owned-external-async-with-defer-does-not-block-domcontentloaded",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncWithDeferAfterDeferOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,defer".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncWithDeferDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,defer,dcl:interactive".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncWithDeferLoadOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,defer,dcl:interactive,external-script,load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedAsyncWithDeferFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after-append:async=true,defer,dcl:interactive,external-script,load,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_default_async_module_side_effect_races_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/runtime-owned-default-async-module-side-effect-after-domcontentloaded",
|
|
))
|
|
.await?;
|
|
|
|
let final_order = match diagnostic_global(&page, "runtimeOwnedAsyncModuleFinalOrder") {
|
|
Some(JsValueSnapshot::String(order)) => order.as_str(),
|
|
other => panic!("runtime external module should publish its final order, got {other:?}"),
|
|
};
|
|
assert!(
|
|
matches!(
|
|
final_order,
|
|
"ready:loading,after-append:async=true,dcl:interactive,module,module-microtask,window-load:complete"
|
|
| "ready:loading,after-append:async=true,module,module-microtask,dcl:interactive,window-load:complete"
|
|
),
|
|
"runtime-created async module and DOMContentLoaded may race, but both must finish before Window load: {final_order}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_inline_module_missing_default_export_reports_window_error_after_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/runtime-owned-inline-module-missing-default-export-after-domcontentloaded",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInlineModuleLinkFailureMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedInlineModuleLinkFailureFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ready:loading,after-append:loading:async=true,dcl:interactive,later-module,window-error,window-error-microtask,window-load:complete".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn runtime_owned_external_module_load_failure_reports_script_error_after_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/runtime-owned-external-module-load-failure-after-later-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedExternalModuleLoadFailureWindowErrors"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "runtimeOwnedExternalModuleLoadFailureFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ready:loading,after-broken:loading:async=true,after-later:loading:async=true,dcl:interactive,later-module,later-module-microtask,script-error,script-error-microtask,window-load:complete".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_script_error_fires_before_later_restore_inline() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-external-script-error-after-page-task-turn"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteErrorPageTaskRestored"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteErrorPageTaskSaw"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteErrorPageTaskRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"written-error,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteErrorPageTaskFinalOrder"),
|
|
Some(&JsValueSnapshot::String("written-error".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_type_mutation_from_connected_datablock_remains_inert() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-type-mutation-remains-inert"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicTypeMutationBefore"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicTypeMutationRuns"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_error_does_not_abort_later_in_order_work() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-error-does-not-abort-queue"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicScriptErrorCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicScriptErrorDocumentCapture"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicScriptErrorOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,load".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String("in-order-fast".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_preparation_context_stays_in_old_document() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-preparation-context-stays-in-old-document"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.dynamicPreparationContextResult === 'replacement-only'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentOpenAsyncRan"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "staleDynamicRan"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPreparationContextResult"),
|
|
Some(&JsValueSnapshot::String("replacement-only".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn implicit_document_write_from_async_keeps_old_defer_work() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-implicit-replace-drops-old-defer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitReplaceRan"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitStaleDeferRan"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitResult"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn implicit_document_write_from_async_keeps_old_module_work() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-implicit-replace-drops-old-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitModuleReplaceRan"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitStaleModuleRan"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImplicitModuleResult"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_replacement_async_stays_after_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-replacement-async-stays-after-domcontentloaded"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementInlineRuns"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementAsyncDclOrder"),
|
|
Some(&JsValueSnapshot::String("dcl:interactive".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementAsyncScriptOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl:interactive,async-script:interactive".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementAsyncLoadOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl:interactive,async-script:interactive,load:interactive".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementAsyncFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dcl:interactive,async-script:interactive,load:interactive,window-load:complete"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_replacement_syncs_style_sources_before_written_scripts() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-replacement-style-source-sync"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteReplacementStyleDisplay"),
|
|
Some(&JsValueSnapshot::String("block".to_owned()))
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-style-display=\"block\""),
|
|
"{}",
|
|
page.serialize_html_async().await.unwrap()
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_nested_writer_restores_outer_insertion_point() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-nested-writer-restores-outer-insertion-point"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteNestedWriterResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"after|outer-start,inner,after-visible".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_nested_external_script_serializes_outer_resume() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-nested-external-script-serializes-outer-resume"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteNestedExternalResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"parent,parent-after-write,child,outer-after,outer-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_split_script_continues_parser_session() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-external-split-script-parser-session"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteExternalSplitSessionResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"parent,written-inline,tail-hidden,parent-after-write,after-parent".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_inserted_external_script_resumes_chunked_root_input() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = tokio::time::timeout(
|
|
Duration::from_secs(5),
|
|
browser.fetch(&server.url("/compat/document-write-inserted-external-resumes-chunked-root")),
|
|
)
|
|
.await
|
|
.expect("document.write parser continuation must not spin on queued root input")?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInsertedChunkedResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"before-write,after-write,inserted-external,tail-script".to_owned()
|
|
))
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await?
|
|
.contains("id=\"document-write-inserted-chunked-tail\"")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn imported_started_child_script_stays_inert_when_appended() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/imported-started-child-script-stays-inert"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importedStartedChildResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"child-script,after-import".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_script_sees_parser_visible_dom_boundary() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-parser-visible-dom-boundary"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteParserVisibleDomResult"),
|
|
Some(&JsValueSnapshot::String("ran|yes".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_script_stays_parser_blocking_at_insertion_point() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-external-parser-blocking-boundary"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteExternalResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ran|before|no-after|after-present".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_external_script_load_microtask_precedes_later_written_inline() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/document-write-external-script-load-microtask-before-later-written-inline",
|
|
))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteLoadMicrotaskDuringLoad"),
|
|
Some(&JsValueSnapshot::String("external-script,load".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteLoadMicrotaskAfterInline"),
|
|
Some(&JsValueSnapshot::String(
|
|
"external-script,load,load-microtask,after-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteLoadMicrotaskFinal"),
|
|
Some(&JsValueSnapshot::String(
|
|
"external-script,load,load-microtask,after-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_inline_importmap_applies_before_written_module() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-importmap-before-written-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteImportMapModuleResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,module:1:2".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_inline_importmap_applies_before_written_external_module() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-importmap-before-written-external-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteExternalImportMapResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,module:1:2".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_invalid_inline_importmap_reports_window_error_before_written_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-invalid-importmap-before-written-module"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapErrors"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_invalid_inline_importmap_reports_window_error_before_restore_inline()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-invalid-importmap-before-restore-inline"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapProgressErrors"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapProgressRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapProgressMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,restore-inline,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteInvalidImportMapProgressFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,restore-inline,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_defer_script_queues_after_later_classic() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-defer-queues-after-later-classic"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDeferQueueResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,defer-script".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_defer_script_runs_before_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-defer-runs-before-domcontentloaded"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDeferDclAtDcl"),
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,defer-script,dcl".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteDeferDclFinal"),
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,defer-script,dcl,load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn child_document_open_after_parent_load_runs_written_data_script() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/child-document-open-after-parent-load-data-script"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "childDocumentOpenAfterLoadDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "childDocumentOpenAfterLoadResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"parent-load,data-script-1,after-close,data-script-2".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_open_after_load_runs_written_external_scripts() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/document-open-after-load-external-scripts"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.documentOpenAfterLoadResult === 'inline,external-1,external-2' && document.readyState === 'complete'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("id=\"written\"")
|
|
);
|
|
let Some(JsValueSnapshot::String(order)) =
|
|
diagnostic_global(&page, "documentOpenAfterLoadResult")
|
|
else {
|
|
panic!("missing documentOpenAfterLoadResult diagnostic");
|
|
};
|
|
assert_eq!(order, "inline,external-1,external-2");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_multi_level_nested_writer_preserves_stack() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-multi-level-nested-writer"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteMultiLevelResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"inner-after|middle-after|outer-start,middle-start,inner,inner-after-visible,middle-after-visible".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_module_runs_without_hanging_late_stylesheet_tail() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-late-stylesheet-does-not-block-written-module"))
|
|
.await?;
|
|
|
|
let result = diagnostic_global(&page, "documentWriteLateStyleResult");
|
|
let order = diagnostic_global(&page, "documentWriteLateStyleOrder");
|
|
let html = page.serialize_html_async().await?;
|
|
assert_eq!(
|
|
result,
|
|
Some(&JsValueSnapshot::String(
|
|
"classic-after-write,module-before-style|true".to_owned()
|
|
)),
|
|
"unexpected document.write/module order: order={order:?}, html={}",
|
|
html,
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_split_tags_stream_across_multiple_calls() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-split-tags-stream-across-calls"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteSplitStreamResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ran|div-visible|after-present".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_split_script_stream_across_multiple_calls() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-split-script-stream-across-calls"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteSplitScriptResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"inline-script,after-missing|after-present".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_split_external_script_stream_across_multiple_calls() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-split-external-script-stream-across-calls"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteSplitExternalResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"ran|before|no-after|after-present".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_split_importmap_and_module_stream_across_multiple_calls() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/document-write-split-importmap-and-module-stream-across-calls"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteSplitImportMapModuleResult"),
|
|
Some(&JsValueSnapshot::String("1:2|after-present".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_importmap_applies_before_later_dynamic_module() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-importmap-before-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicImportMapGreeting"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_external_importmap_dispatches_error_before_later_module() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-external-importmap-error-before-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicExternalImportMapErrorCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicExternalImportMapOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_execution_failure_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-module-execution-failure-does-not-abort-queue"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleExecFailureContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleExecFailureErrorCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleExecFailureMessageMentionsFixture"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleExecFailureOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_missing_default_export_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-module-missing-default-export-does-not-abort-queue"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_pending_star_missing_export_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url("/compat/dynamic-module-pending-star-missing-export-does-not-abort-queue"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModulePendingStarMissingExportContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModulePendingStarMissingExportOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_pending_star_link_failure_happens_before_body_and_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/dynamic-module-pending-star-link-failure-before-body-and-later-module",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPendingStarLinkContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPendingStarLinkBodyRan"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPendingStarLinkOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_pending_star_final_missing_reports_link_failure_instead_of_timeout()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url("/compat/dynamic-module-pending-star-final-missing-reports-link-failure"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPendingStarFinalMissingOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicPendingStarFinalMissingWindowError"),
|
|
Some(&JsValueSnapshot::String(format!(
|
|
"ModuleLinkFailed: module `{}` does not export `missingValue`",
|
|
server.url("/assets/module_pending_star_cycle_b.mjs")
|
|
)))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_missing_default_export_reports_window_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url(
|
|
"/compat/dynamic-module-missing-default-export-reports-window-error-does-not-abort-queue",
|
|
),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportWindowContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportWindowErrorCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportWindowMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportWindowOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_missing_default_export_reports_window_error_payload_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url(
|
|
"/compat/dynamic-module-missing-default-export-reports-window-error-payload-does-not-abort-queue",
|
|
),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportPayloadContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"dynamicModuleMissingExportPayloadErrorMessageMatches"
|
|
),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportPayloadFilenameMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleMissingExportPayloadOrderResult"),
|
|
Some(&JsValueSnapshot::String("later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_tla_rejection_dispatches_error_without_aborting_later_module() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-module-tla-rejection-does-not-abort-queue"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaFailureContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaFailureErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaFailureOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_module_tla_exotic_rejection_reports_window_error_payload_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url(
|
|
"/compat/dynamic-module-tla-exotic-rejection-reports-window-error-payload-does-not-abort-queue",
|
|
),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaPayloadContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaPayloadErrorMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaPayloadFilenameMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleTlaPayloadOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_reattach_does_not_restart_after_commit() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-reattach-stays-started"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicReattachLoads"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String("in-order-fast".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_src_mutation_does_not_restart_after_commit() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-src-mutation-stays-started"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicSrcMutationLoads"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String("in-order-fast".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_async_attr_add_remove_clears_force_async() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-async-attr-clears-force-async"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"in-order-slow,in-order-fast".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_script_src_added_after_connect_starts_once() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-src-added-after-connect-starts-once"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicSrcAddedLoads"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicInOrderScriptOrderResult"),
|
|
Some(&JsValueSnapshot::String("in-order-fast".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn importmap_scopes_and_prefixes_override_global_resolution() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/importmap-scopes-and-prefixes"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapScopedGreeting"),
|
|
Some(&JsValueSnapshot::String("scoped".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapScopedPkgLabel"),
|
|
Some(&JsValueSnapshot::String("scoped-pkg".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapScopesAndPrefixesResult"),
|
|
Some(&JsValueSnapshot::String("scoped,scoped-pkg".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn later_importmap_cannot_override_already_resolved_specifier() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/importmap-merge-after-resolution"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapMergeFirstGreeting"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapMergeSecondGreeting"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapMergeExtraLabel"),
|
|
Some(&JsValueSnapshot::String("extra".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapMergeAfterResolutionResult"),
|
|
Some(&JsValueSnapshot::String("initial,initial,extra".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn importmap_normalizes_url_like_specifiers_before_resolution() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/importmap-url-like-normalization"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapUrlLikeNormalizationResult"),
|
|
Some(&JsValueSnapshot::String("canonical".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn importmap_registered_after_module_load_adds_unresolved_mapping() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/importmap-after-module-load"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapAcquisitionFirstModule"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapAcquisitionLateModule"),
|
|
Some(&JsValueSnapshot::String("extra".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "importMapAcquisitionMutationDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_async_module_does_not_close_multiple_import_map_registration() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-async-module-closes-importmap-acquisition"))
|
|
.await?;
|
|
assert_eq!(diagnostic_global(&page, "dynamicAsyncImportMapLoads"), None);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicAsyncImportMapLabel"),
|
|
Some(&JsValueSnapshot::String("extra".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicAsyncImportMapErrors"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicAsyncModuleReady"),
|
|
None,
|
|
"top-level await must not keep the external module or document load event pending"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_allows_late_dynamic_map_to_add_unresolved_mapping() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url("/compat/importmap-closed-by-parser-owned-module-before-late-dynamic-map"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserModuleAcquisitionFirstGreeting"),
|
|
Some(&JsValueSnapshot::String("initial".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserModuleAcquisitionLateLabel"),
|
|
Some(&JsValueSnapshot::String("extra".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserModuleAcquisitionLateErrors"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserModuleAcquisitionMutationDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_importmap_after_dynamic_module_prepare_adds_unresolved_mapping() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-importmap-blocked-after-dynamic-module-prepare"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModulePrepareBarrierInstalled"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicModuleQueuedBeforeLateParserImportMap"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserImportMapAfterDynamicModuleLabel"),
|
|
Some(&JsValueSnapshot::String("extra".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserImportMapAfterDynamicModuleErrors"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserImportMapAfterDynamicModuleDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_resolution_failure_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-module-error-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleError"),
|
|
Some(&JsValueSnapshot::String("blocked".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_missing_export_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-module-missing-export-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportError"),
|
|
Some(&JsValueSnapshot::String("missing-export".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_pending_star_missing_export_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server
|
|
.url("/compat/parser-owned-module-pending-star-missing-export-before-later-module"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarMissingExportError"),
|
|
Some(&JsValueSnapshot::String(
|
|
"pending-star-missing-export".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarMissingExportContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarMissingExportOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_pending_star_link_failure_happens_before_body_and_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-owned-module-pending-star-link-failure-before-body-and-later-module",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarLinkError"),
|
|
Some(&JsValueSnapshot::String(
|
|
"pending-star-link-failure".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarLinkContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarLinkBodyRan"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarLinkOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_pending_star_final_missing_reports_link_failure_instead_of_timeout()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server
|
|
.url("/compat/parser-owned-module-pending-star-final-missing-reports-link-failure"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarFinalMissingOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedPendingStarFinalMissingWindowError"),
|
|
Some(&JsValueSnapshot::String(format!(
|
|
"ModuleLinkFailed: module `{}` does not export `missingValue`",
|
|
server.url("/assets/module_pending_star_cycle_b.mjs")
|
|
)))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_missing_export_reports_window_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-owned-module-missing-export-reports-window-error-before-later-module",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportWindowContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportWindowErrorCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportWindowMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportWindowOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_missing_export_reports_window_error_after_restore_inline() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-owned-module-missing-export-reports-window-error-after-restore-inline",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportRestoreErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportRestoreRestoreOrder"),
|
|
Some(&JsValueSnapshot::String("restore-inline".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportRestoreMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportRestoreFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_missing_export_reports_window_error_payload_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url(
|
|
"/compat/parser-owned-module-missing-export-reports-window-error-payload-before-later-module",
|
|
),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportPayloadContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportPayloadErrorMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportPayloadFilenameMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedMissingExportPayloadOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_tla_rejection_reports_window_error_after_restore_inline() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/parser-owned-module-tla-rejection-reports-window-error-after-restore-inline",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedTlaRestoreErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedTlaRestoreRestoreOrder"),
|
|
Some(&JsValueSnapshot::String("restore-inline".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedTlaRestoreMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedTlaRestoreFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_tla_rejection_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-module-tla-rejection-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_module_missing_export_reports_window_error_after_restore_inline()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/document-write-module-missing-export-reports-window-error-after-restore-inline",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteMissingExportRestoreErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteMissingExportRestoreRestoreOrder"),
|
|
Some(&JsValueSnapshot::String("restore-inline".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteMissingExportRestoreMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteMissingExportRestoreFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn document_write_module_tla_rejection_reports_window_error_after_restore_inline()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url(
|
|
"/compat/document-write-module-tla-rejection-reports-window-error-after-restore-inline",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteTlaRestoreErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteTlaRestoreRestoreOrder"),
|
|
Some(&JsValueSnapshot::String("restore-inline".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteTlaRestoreMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "documentWriteTlaRestoreFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"restore-inline,window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_module_tla_exotic_rejection_reports_window_error_payload_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server.url(
|
|
"/compat/parser-owned-module-tla-exotic-rejection-reports-window-error-payload-before-later-module",
|
|
),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaPayloadContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaPayloadErrorMessageMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaPayloadFilenameMatches"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedModuleTlaPayloadOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_importmap_parse_failure_dispatches_error_before_later_module() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-importmap-error-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapErrors"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,later-module".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_owned_importmap_parse_failure_waits_until_parser_progress_restores_state()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parser-owned-importmap-error-after-parser-progress"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapProgressRestored"),
|
|
Some(&JsValueSnapshot::String("restored".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapProgressSaw"),
|
|
Some(&JsValueSnapshot::String("not-run".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapProgressRestoreOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,window-error-microtask,restore-inline".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapProgressMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,window-error-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserOwnedImportMapProgressFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"window-error,window-error-microtask,restore-inline".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_default_and_side_effect_imports_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-default-and-side-effect-imports"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultImportValue"),
|
|
Some(&JsValueSnapshot::String("default-export-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultImportSawSideEffectCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultExportEvalCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultImportOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module:default-export-ok,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_default_reexport_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-default-reexport"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultReexportResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"default-reexport-ok:default-reexport-ok:42".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultReexportDefaultName"),
|
|
Some(&JsValueSnapshot::String("makeDefaultValue".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultReexportAliasName"),
|
|
Some(&JsValueSnapshot::String("makeDefaultValue".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultReexportFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_string_literal_export_names_decode_js_escapes_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-string-literal-export-names"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStringLiteralExportNamesResult"),
|
|
Some(&JsValueSnapshot::String("42:string-name-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStringLiteralExportNamesFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_default_function_and_class_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-default-function-and-class"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultDeclResult"),
|
|
Some(&JsValueSnapshot::String("function-ok:ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultDeclFunctionName"),
|
|
Some(&JsValueSnapshot::String("makeValue".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultDeclClassName"),
|
|
Some(&JsValueSnapshot::String("ValueBox".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultDeclDclOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_default_anonymous_declarations_preserve_default_name() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-default-anonymous-declarations"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultAnonymousResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"anon-fn-ok:anon-class-ok".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultAnonymousFunctionName"),
|
|
Some(&JsValueSnapshot::String("default".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultAnonymousClassName"),
|
|
Some(&JsValueSnapshot::String("default".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDefaultAnonymousDclOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_named_class_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-class-named"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportClassResult"),
|
|
Some(&JsValueSnapshot::String("class:named-export-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportClassName"),
|
|
Some(&JsValueSnapshot::String("DerivedAnswer".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportClassDclOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_generator_functions_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-generator-functions"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportGeneratorResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"one:two|async-one:async-two|default-one:default-two".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportGeneratorDefaultName"),
|
|
Some(&JsValueSnapshot::String("default".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportGeneratorFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_const_multiple_bindings_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-const-multiple-bindings"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportConstMultipleResult"),
|
|
Some(&JsValueSnapshot::String("1:2:1-2".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportConstMultipleFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parser_module_completion_keeps_dcl_ahead_of_due_timer_on_owner_loop() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/parser-module-completion-dcl-before-timer"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.parserModuleCompletionFinalOrder === 'module,dcl,timer'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parserModuleCompletionFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl,timer".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_destructuring_bindings_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-export-destructuring-bindings"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.moduleExportDestructuringFinalOrder === 'module,dcl,timeout'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportDestructuringImmediate"),
|
|
Some(&JsValueSnapshot::String(
|
|
"42:destructure-ok:1:2:1:10:20-30".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportDestructuringLater"),
|
|
Some(&JsValueSnapshot::String("2:11:21-31".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportDestructuringFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl,timeout".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_nested_destructuring_bindings_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-export-nested-destructuring-bindings"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.moduleNestedDestructuringFinalOrder === 'module,dcl,timeout'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedDestructuringImmediate"),
|
|
Some(&JsValueSnapshot::String(
|
|
"42:lead:tail-a-tail-b:nested-default:1:10:20:30-40".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedDestructuringLater"),
|
|
Some(&JsValueSnapshot::String("2:11:21:31-41".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedDestructuringFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl,timeout".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_nested_initializer_commas_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-export-nested-initializer-commas"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.moduleNestedInitializerFinalOrder === 'module,dcl,timeout'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedInitializerImmediate"),
|
|
Some(&JsValueSnapshot::String(
|
|
"42:1-2-3:multi-comma:1:1".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedInitializerLater"),
|
|
Some(&JsValueSnapshot::String("nested-ok:2:2".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleNestedInitializerFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl,timeout".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dependency_fetch_uses_module_credentials_mode() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let _ = browser.fetch(&server.url("/cookie")).await?;
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-dependency-fetch-uses-module-credentials"))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains(r#"data-module-dependency-cookie="true""#),
|
|
"same-origin module dependency fetch should carry the document cookie"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_import_export_lists_support_comments_and_trailing_commas_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-import-export-list-comments"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportExportListCommentsResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"default-ok:42:comments-ok".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportExportListCommentsFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_multiline_dynamic_import_executes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-multiline-dynamic-import"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMultilineDynamicImportIsPromise"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleMultilineDynamicImportValue === 'multiline-import-ok'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMultilineDynamicImportValue"),
|
|
Some(&JsValueSnapshot::String("multiline-import-ok".to_owned()))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "moduleMultilineDynamicImportFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "module", "dcl");
|
|
assert_order_contains(final_order, "resolved:multiline-import-ok");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_import_with_comments_and_trailing_comma_executes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-dynamic-import-comments-and-trailing-comma"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleDynamicImportCommentResolved === 'comment-ok'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportCommentResolved"),
|
|
Some(&JsValueSnapshot::String("comment-ok".to_owned()))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "moduleDynamicImportCommentFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "module-start", "module-end");
|
|
assert_order_before(final_order, "module-end", "dcl");
|
|
assert_order_contains(final_order, "resolved:comment-ok");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_import_with_static_concat_executes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-dynamic-import-static-concat"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStaticConcatPromiseLike"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleDynamicImportStaticConcatAwaitedValue === 'comment-ok'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStaticConcatPromiseValue"),
|
|
Some(&JsValueSnapshot::String("comment-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStaticConcatAwaitedValue"),
|
|
Some(&JsValueSnapshot::String("comment-ok".to_owned()))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "moduleDynamicImportStaticConcatFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "module-start", "dcl");
|
|
assert_order_before(final_order, "promise:comment-ok", "awaited:comment-ok");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_import_source_rejects_without_hanging() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-dynamic-import-source-rejects"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleDynamicImportSourceRejected === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportSourceRejected"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportSourceErrorName"),
|
|
Some(&JsValueSnapshot::String("SyntaxError".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportSourceMessageIncludesSourcePhase"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_import_resolves_after_imported_module_replaces_iframe_document() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/dynamic-import-document-write-iframe"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.dynamicImportDocumentWriteDone === true && window.dynamicImportDocumentWriteResolved === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicImportDocumentWriteBody"),
|
|
Some(&JsValueSnapshot::String(
|
|
"document.write body contents\n".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicImportDocumentWriteRejected"),
|
|
None
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_import_attributes_and_dynamic_options_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-import-attributes-and-dynamic-options"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleImportAttributesDynamicValue === 'comment-ok' && window.moduleImportAttributesTextRejected === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAttributesPromiseLike"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAttributesDynamicValue"),
|
|
Some(&JsValueSnapshot::String("comment-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAttributesFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"static:comment-ok,dcl,dynamic:comment-ok".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAttributesTextErrorName"),
|
|
Some(&JsValueSnapshot::String("TypeError".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_import_assertions_legacy_syntax_reports_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-import-assertions-legacy-syntax"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAssertionsPromiseLike"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAssertionsDynamicValue"),
|
|
None
|
|
);
|
|
let Some(JsValueSnapshot::String(error)) =
|
|
diagnostic_global(&page, "moduleImportAssertionsError")
|
|
else {
|
|
panic!("expected legacy import assertions syntax error");
|
|
};
|
|
assert!(error.contains("Unexpected identifier 'assert'"), "{error}");
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportAssertionsFinalOrder"),
|
|
Some(&JsValueSnapshot::String("window-error,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_static_json_and_css_imports_use_synthetic_modules() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-static-json-css-import"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticJsonCssValue"),
|
|
Some(&JsValueSnapshot::String("42:json-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticJsonCssSheetRules"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticJsonCssSheetText"),
|
|
Some(&JsValueSnapshot::String(
|
|
"body { color: rgb(1, 2, 3); }".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticJsonCssFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_static_wasm_import_exposes_wasm_exports() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-static-wasm-import"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmExports"),
|
|
Some(&JsValueSnapshot::String("func,glob,mem,tab".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmFuncType"),
|
|
Some(&JsValueSnapshot::String("function".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmMem"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmGlob"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmGlobValue"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmNamedGlob"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmNamedGlobValue"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmTab"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_wasm_import_uses_original_instance_constructor() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-dynamic-wasm-import-ignores-patched-instance"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"dynamic wasm import error: {:?}",
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceFuncType"),
|
|
Some(&JsValueSnapshot::String("function".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceMem"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceGlob"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceGlobValue"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicWasmPatchedInstanceStillPatched"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_wasm_namespace_instance_returns_cached_instance() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-wasm-namespace-instance"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"namespaceInstance error: {:?}",
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceStatic"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceShared"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceFuncType"),
|
|
Some(&JsValueSnapshot::String("function".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceRejectsPlainObject"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmNamespaceInstanceRejectsJsNamespace"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_static_wasm_import_chain_links_wasm_and_js_dependencies() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-static-wasm-import-chain"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmImportResult"),
|
|
Some(&JsValueSnapshot::String("executed".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_static_wasm_import_allows_acyclic_js_dependency_graph() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-static-wasm-import-js-dependency-graph"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmImportJsDependencyResult"),
|
|
Some(&JsValueSnapshot::String("leaf".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_static_wasm_import_preserves_js_dependency_error_object() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-static-wasm-import-throwing-js-dependency"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyConstructor"),
|
|
Some(&JsValueSnapshot::String("LinkError".to_owned())),
|
|
"message: {:?}",
|
|
diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyMessage")
|
|
);
|
|
let message = diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyMessage")
|
|
.and_then(|value| match value {
|
|
JsValueSnapshot::String(message) => Some(message.as_str()),
|
|
_ => None,
|
|
})
|
|
.unwrap_or("");
|
|
assert!(
|
|
message.contains("dependency-link-boom"),
|
|
"message should preserve the dependency error detail: {message:?}"
|
|
);
|
|
assert!(
|
|
!message.contains("TypeError: LinkError"),
|
|
"message should not wrap the original LinkError in a synthetic TypeError: {message:?}"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyScriptLoad"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyScriptError"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStaticWasmThrowingJsDependencyLoaded"),
|
|
None
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_mutable_wasm_global_initial_value_is_unwrapped() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-mutable-wasm-global-initial-value"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalInitial"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_wasm_global_unwrap_uses_original_value_getter() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-wasm-global-unwrapping-ignores-patched-getter"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"wasm global unwrap error: {:?}",
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterValue"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterNamedType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterNamedIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmGlobalPatchedGetterNamedValue"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
#[ignore = "requires V8 wasm-aware module binding loads for mutable global exports"]
|
|
async fn module_mutable_wasm_global_export_is_live_binding() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-mutable-wasm-global-live-binding"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveInitialNamespace"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveInitialNamed"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveInitialGetter"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveGetterAfterSet"),
|
|
Some(&JsValueSnapshot::Number(555.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveNamespaceAfterSet"),
|
|
Some(&JsValueSnapshot::Number(555.0)),
|
|
"namespace access must observe the current wasm global value"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveNamedAfterSet"),
|
|
Some(&JsValueSnapshot::Number(555.0)),
|
|
"static named imports must observe the current wasm global value"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalLiveIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
#[ignore = "requires V8 wasm-aware module binding loads for mutable global re-exports"]
|
|
async fn module_mutable_wasm_global_dep_reexport_is_live_binding() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-mutable-wasm-global-dep-reexport-live-binding"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportInitialNamespace"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportInitialNamed"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportInitialGetter"),
|
|
Some(&JsValueSnapshot::Number(100.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportGetterAfterSet"),
|
|
Some(&JsValueSnapshot::Number(777.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportNamespaceAfterSet"),
|
|
Some(&JsValueSnapshot::Number(777.0)),
|
|
"namespace access must observe the dependency wasm global storage"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportNamedAfterSet"),
|
|
Some(&JsValueSnapshot::Number(777.0)),
|
|
"static imports must observe the dependency wasm global storage"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMutableWasmGlobalReexportIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_v128_wasm_global_export_throws_reference_error_on_namespace_access() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-wasm-v128-global-export-throws-tdz"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmV128TdzDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"v128 TDZ page error: {:?}",
|
|
diagnostic_global(&page, "moduleWasmV128TdzError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmV128TdzMutableValue"),
|
|
Some(&JsValueSnapshot::Number(100.0)),
|
|
"v128 TDZ page error: {:?}",
|
|
diagnostic_global(&page, "moduleWasmV128TdzError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmV128TdzThrows"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"namespace access should throw ReferenceError"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmV128TdzErrorName"),
|
|
Some(&JsValueSnapshot::String("ReferenceError".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_source_phase_wasm_import_returns_compiled_module_sources() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-source-phase-wasm-import"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseStaticIsModule"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseAbstractModuleSourceHidden"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseAbstractModuleSourceName"),
|
|
Some(&JsValueSnapshot::String("AbstractModuleSource".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseModuleConstructorExtendsAbstract"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseModulePrototypeExtendsAbstract"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseStaticIsAbstractModuleSource"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseStaticExports"),
|
|
Some(&JsValueSnapshot::String("func,glob,mem,tab".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseSharedIsModule"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseSharedEvaluationResult"),
|
|
Some(&JsValueSnapshot::String("executed".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicIsModule"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"dynamic error: {:?}, unhandled: {:?}",
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicError"),
|
|
diagnostic_global(&page, "moduleSourcePhaseUnhandled")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicLogged"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn audio_worklet_module_imports_wasm_source_phase() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/audio-worklet-wasm-source-phase"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "audioWorkletWasmDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "audioWorkletWasmError"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "audioWorkletWasmResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"42|true|true|func,glob,mem,tab".to_owned()
|
|
))
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-audio-worklet-wasm=\"42|true|true|func,glob,mem,tab\""),
|
|
"audio worklet wasm result should be reflected in final HTML: {}",
|
|
page.serialize_html_async().await.unwrap()
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_source_phase_imports_share_resolved_module_identity() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-source-phase-identity"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseIdentityDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"source-phase identity error: {:?}",
|
|
diagnostic_global(&page, "moduleSourcePhaseIdentityError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseIdentityNamespaceShared"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseIdentitySourceShared"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_source_phase_wasm_import_reuses_modulepreload_resource() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-source-phase-wasm-modulepreload"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"preload error: {:?}",
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadCountAfterLoad"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadTransferPositive"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadInitiator"),
|
|
Some(&JsValueSnapshot::String("link".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadImportIsModule"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhasePreloadCountAfterImport"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_source_phase_dynamic_script_reuses_modulepreload_resource() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-source-phase-wasm-dynamic-script-modulepreload"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
r#"
|
|
(globalThis.moduleSourcePhaseDynamicPreloadDone === true &&
|
|
globalThis.moduleSourcePhaseStaticDone === true) ||
|
|
Boolean(globalThis.moduleSourcePhaseDynamicPreloadError) ||
|
|
Boolean(globalThis.moduleSourcePhaseDynamicScriptError)
|
|
"#,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicPreloadDone"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"preload error: {:?}, script error: {:?}",
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicPreloadError"),
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicScriptError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseStaticDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicBeforeAwait"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicAfterAwait"),
|
|
Some(&JsValueSnapshot::Bool(true)),
|
|
"script error: {:?}",
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicScriptError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicScriptLoad"),
|
|
Some(&JsValueSnapshot::Bool(false)),
|
|
"successful inline module scripts must not dispatch load"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicPreloadCountAfterLoad"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSourcePhaseDynamicPreloadCountAfterImport"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_external_wasm_script_executes_start_function() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-external-wasm-script-executes-start"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExternalWasmScriptErrored"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExternalWasmScriptLog"),
|
|
Some(&JsValueSnapshot::String("executed".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_wasm_csp_blocks_cross_origin_script_element_fetch() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-wasm-csp-blocks-cross-origin"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmCspViolationCount"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmCspViolationText"),
|
|
Some(&JsValueSnapshot::String(
|
|
"script-src-elem|script-src-elem|true|true".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmCspExecuted"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn wasm_api_csp_blocks_eval_from_response_header() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/wasm-api-csp-blocks-eval-from-response-header"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "wasmCspHeaderResult"),
|
|
Some(&JsValueSnapshot::String("CompileError|true".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "wasmCspHeaderEventText"),
|
|
Some(&JsValueSnapshot::String(
|
|
"script-src|script-src|default-src 'self' 'unsafe-inline'|wasm-eval|true".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn wasm_module_postmessage_into_csp_iframe_fires_violation() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/wasm-module-postmessage-into-csp-iframe"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.wasmPostMessageCspResult !== ''",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "wasmPostMessageCspResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"script-src|script-src|default-src 'unsafe-inline'|wasm-eval|true".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "wasmPostMessageCspUnexpectedMessage"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_wasm_link_error_reports_typed_window_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-wasm-link-error-reports-typed-window-error"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmLinkErrorConstructor"),
|
|
Some(&JsValueSnapshot::String("LinkError".to_owned())),
|
|
"message: {:?}",
|
|
diagnostic_global(&page, "moduleWasmLinkErrorMessage")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmLinkErrorScriptLoad"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmLinkErrorScriptError"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_wasm_js_cycle_reports_guard_without_crashing() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-wasm-js-cycle-reports-guard"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleConstructor"),
|
|
Some(&JsValueSnapshot::String("Error".to_owned())),
|
|
"message: {:?}",
|
|
diagnostic_global(&page, "moduleWasmJsCycleMessage")
|
|
);
|
|
let message = diagnostic_global(&page, "moduleWasmJsCycleMessage")
|
|
.and_then(|value| match value {
|
|
JsValueSnapshot::String(message) => Some(message.as_str()),
|
|
_ => None,
|
|
})
|
|
.unwrap_or("");
|
|
assert!(
|
|
message.contains(
|
|
"cyclic WebAssembly module evaluation through JavaScript dependencies is not supported yet"
|
|
),
|
|
"message should expose the cycle guard: {message:?}"
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleUnexpected"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
#[ignore = "requires wasm module records to participate in the V8 module evaluation SCC"]
|
|
async fn module_wasm_js_cycle_evaluates_js_dependency_initializers() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-wasm-js-cycle-future-acceptance"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureError"),
|
|
Some(&JsValueSnapshot::String(String::new())),
|
|
"cycle error: {:?}",
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureError")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureGlobalType"),
|
|
Some(&JsValueSnapshot::String("number".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureGlobalIsGlobal"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureGlobalValue"),
|
|
Some(&JsValueSnapshot::Number(24.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureFunction"),
|
|
Some(&JsValueSnapshot::Number(43.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureIncrementGlobal"),
|
|
Some(&JsValueSnapshot::Number(43.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureMemoryBefore"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureMutateMemory"),
|
|
Some(&JsValueSnapshot::Number(42.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureMemoryAfter"),
|
|
Some(&JsValueSnapshot::Number(42.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureTableBeforeNull"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureTableRefIsFunction"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleWasmJsCycleFutureTableAfterSame"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_js_wasm_cycle_function_import_captures_initial_binding() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-js-wasm-cycle-function-import"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleJsWasmCycleConstructor"),
|
|
Some(&JsValueSnapshot::String(String::new())),
|
|
"message: {:?}",
|
|
diagnostic_global(&page, "moduleJsWasmCycleMessage")
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleJsWasmCycleInitialRun"),
|
|
Some(&JsValueSnapshot::Number(42.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleJsWasmCycleAfterReassignRun"),
|
|
Some(&JsValueSnapshot::Number(42.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleJsWasmCycleImportedBinding"),
|
|
Some(&JsValueSnapshot::Number(24.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_reserved_wasm_names_reject_with_link_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/module-reserved-wasm-names-reject-with-link-error"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleReservedWasmNameDone"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleReservedWasmNameResults"),
|
|
Some(&JsValueSnapshot::String(
|
|
[
|
|
"import-name:LinkError:true",
|
|
"export-name:LinkError:true",
|
|
"import-module:LinkError:true",
|
|
"patched-link-error:LinkError:true:true:false",
|
|
]
|
|
.join("|")
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleReservedWasmNameUnhandled"),
|
|
Some(&JsValueSnapshot::String(String::new()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_import_meta_resolve_uses_import_map_and_static_concat() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-import-meta-resolve"))
|
|
.await?;
|
|
let expected_target = server.url("/assets/module-dynamic-import-comments-target.mjs");
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveMapped"),
|
|
Some(&JsValueSnapshot::String(expected_target.clone()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveConcat"),
|
|
Some(&JsValueSnapshot::String(expected_target))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveThrew"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,resolved,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_import_meta_resolve_supports_comments_and_trailing_comma() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-import-meta-resolve-comments-and-trailing-comma"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveCommentValue"),
|
|
Some(&JsValueSnapshot::String(
|
|
server.url("/assets/module-dynamic-import-comments-target.mjs"),
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleImportMetaResolveCommentFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,resolved,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_import_with_template_literal_executes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-dynamic-import-template-literal"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportTemplatePromiseLike"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleDynamicImportTemplateResult === 'template-ok:template-ok'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportTemplateResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"template-ok:template-ok".to_owned()
|
|
))
|
|
);
|
|
let final_order =
|
|
diagnostic_global_string(&page, "moduleDynamicImportTemplateFinalOrder").unwrap_or("");
|
|
assert_order_before(final_order, "dcl", "module");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_dynamic_import_eval_and_function_use_active_script_base_url() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-dynamic-import-string-compilation/base/page"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleDynamicImportStringCompilationDone === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStringCompilationError"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStringCompilationEval"),
|
|
Some(&JsValueSnapshot::String("dynamic-base-ok:eval".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStringCompilationFunction"),
|
|
Some(&JsValueSnapshot::String(
|
|
"dynamic-base-ok:function".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleDynamicImportStringCompilationFinalOrder"),
|
|
Some(&JsValueSnapshot::String("script,eval,function".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_string_literal_specifiers_decode_js_escapes() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-escaped-string-literal-specifiers"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleEscapedSpecifierResult === 'escaped-default:escaped-value:escaped-value:escaped-default'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleEscapedSpecifierResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"escaped-default:escaped-value:escaped-value:escaped-default".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleEscapedSpecifierOrderAtDcl"),
|
|
Some(&JsValueSnapshot::String("dcl".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleEscapedSpecifierFinalOrder"),
|
|
Some(&JsValueSnapshot::String("dcl,module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_variable_live_bindings_update_across_turns() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-export-variable-live-bindings"))
|
|
.await?;
|
|
browser
|
|
.wait_for_page_delay(&mut page, Duration::from_millis(250))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportVariableInitial"),
|
|
Some(&JsValueSnapshot::String("0:init".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportVariableAfterMicrotask"),
|
|
Some(&JsValueSnapshot::String("0:init".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportVariableAfterTimeout"),
|
|
Some(&JsValueSnapshot::String("2:timeout".to_owned()))
|
|
);
|
|
let final_order =
|
|
diagnostic_global(&page, "moduleExportVariableFinalOrder").and_then(|value| match value {
|
|
JsValueSnapshot::String(value) => Some(value.as_str()),
|
|
_ => None,
|
|
});
|
|
assert!(
|
|
matches!(
|
|
final_order,
|
|
Some(
|
|
"module:0:init,microtask:0:init,dcl,timeout:2:timeout"
|
|
| "module:0:init,microtask:0:init,timeout:2:timeout,dcl"
|
|
)
|
|
),
|
|
"unexpected export-variable order: {final_order:?}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_self_bare_dynamic_import_returns_promise_and_waits_for_own_evaluation() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-self-bare-dynamic-import-resolves-after-own-evaluation"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleSelfBareImportResolvedValue === 'ready'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportIsPromise"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportResolvedValue"),
|
|
Some(&JsValueSnapshot::String("ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-start,module-end,dcl,resolved:ready".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_self_bare_dynamic_import_after_settle_reuses_existing_promise() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-self-bare-dynamic-import-after-settle-resolves"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"globalThis.moduleSelfBareImportAfterSettleFinalOrder === 'module-start,module-end,dcl,timeout-start,resolved:ready-after-settle'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportAfterSettleIsPromise"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportAfterSettleResolvedValue"),
|
|
Some(&JsValueSnapshot::String("ready-after-settle".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSelfBareImportAfterSettleFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-start,module-end,dcl,timeout-start,resolved:ready-after-settle".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_runtime_helper_calls_are_not_shadowed_by_module_lexicals() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-runtime-helper-shadowing"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleRuntimeHelperShadowValue"),
|
|
Some(&JsValueSnapshot::String("shadow-safe".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleRuntimeHelperShadowLexicalType"),
|
|
Some(&JsValueSnapshot::String("object".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleRuntimeHelperShadowFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_multiline_import_and_export_list_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-multiline-import-and-export-list"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMultilineResult"),
|
|
Some(&JsValueSnapshot::String("42:multiline-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleMultilineOrderResult"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_star_and_namespace_reexport_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-star-and-namespace-reexport"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"42:ok:42:ok:default-ok".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarOrderResult"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_string_literal_export_names_decode_surrogate_pairs_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-string-literal-export-names-surrogate-pairs"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStringLiteralExportNamesSurrogatePairsLaunch"),
|
|
Some(&JsValueSnapshot::Number(42.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleStringLiteralExportNamesSurrogatePairsRelay"),
|
|
Some(&JsValueSnapshot::String("orbital".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleStringLiteralExportNamesSurrogatePairsOrderResult",
|
|
),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_star_string_literal_namespace_decodes_js_escapes_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-star-string-literal-namespace"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarStringLiteralNsResult"),
|
|
Some(&JsValueSnapshot::String("42:ok:default-ok".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarStringLiteralNsFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_escaped_identifier_names_execute() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-escaped-identifier-names"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleEscapedIdentifierNamesResult"),
|
|
Some(&JsValueSnapshot::String("default-ok:41:42:box".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleEscapedIdentifierNamesFinalOrder"),
|
|
Some(&JsValueSnapshot::String("module,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_export_star_ambiguous_name_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-export-star-ambiguous-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarAmbiguousError"),
|
|
Some(&JsValueSnapshot::String("ambiguous".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarAmbiguousContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleExportStarAmbiguousOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_missing_export_dispatches_error_without_aborting_later_module() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-cycle-missing-export-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleMissingExportError"),
|
|
Some(&JsValueSnapshot::String("missing-export".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleMissingExportContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleMissingExportOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_initializing_missing_export_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-cycle-initializing-missing-export-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleInitializingMissingError"),
|
|
Some(&JsValueSnapshot::String("missing-export".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleInitializingMissingContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleInitializingMissingOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_default_missing_through_export_star_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(
|
|
&server
|
|
.url("/compat/module-cycle-default-missing-from-export-star-before-later-module"),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleDefaultMissingError"),
|
|
Some(&JsValueSnapshot::String("missing-default".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleDefaultMissingContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleDefaultMissingOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_import_in_cycle_waits_for_target_module_evaluation_before_resolving() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-cycle-dynamic-import-waits-for-target-evaluation"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleCycleSawAValue === 'a-ready'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleAfterAEndOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"b-start,b-after-import,a-start,a-end".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleSawAValue"),
|
|
Some(&JsValueSnapshot::String("a-ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"b-start,b-after-import,a-start,a-end,dcl,b-dynamic-resolved".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"b-start,b-after-import,a-start,a-end,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_late_binding_becomes_visible_after_target_evaluation()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-cycle-export-star-late-binding"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarLateBindingValue"),
|
|
Some(&JsValueSnapshot::Number(41.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_multihop_late_binding_becomes_visible_after_target_evaluation()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-cycle-export-star-multihop-late-binding"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarMultihopLateBindingValue"),
|
|
Some(&JsValueSnapshot::Number(41.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_late_ambiguity_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-cycle-export-star-late-ambiguous-before-later-module"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarLateAmbiguousError"),
|
|
Some(&JsValueSnapshot::String("ambiguous".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarLateAmbiguousContinuation"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarLateAmbiguousOrderResult"),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_late_ambiguity_omits_export_from_namespace_object() -> Result<()>
|
|
{
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(
|
|
&server.url("/compat/module-cycle-export-star-late-ambiguous-namespace-omits-export"),
|
|
)
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleCycleExportStarLateAmbiguousNamespaceImported === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
let imported = diagnostic_global(&page, "moduleCycleExportStarLateAmbiguousNamespaceImported");
|
|
if imported != Some(&JsValueSnapshot::Bool(true)) {
|
|
panic!(
|
|
"expected namespace dynamic import to resolve, imported={imported:?}, error={:?}, order={:?}",
|
|
diagnostic_global(&page, "moduleCycleExportStarLateAmbiguousNamespaceError"),
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarLateAmbiguousNamespaceOrderResult"
|
|
)
|
|
);
|
|
}
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarLateAmbiguousNamespaceHasShared"
|
|
),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarLateAmbiguousNamespaceDescriptor"
|
|
),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
let final_order = diagnostic_global_string(
|
|
&page,
|
|
"moduleCycleExportStarLateAmbiguousNamespaceOrderResult",
|
|
)
|
|
.unwrap_or("");
|
|
assert_order_before(final_order, "dcl", "import-resolved");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_multihop_late_ambiguity_omits_export_from_namespace_object()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/module-cycle-export-star-multihop-late-ambiguous-namespace-omits-export",
|
|
))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleCycleExportStarMultihopLateAmbiguousNamespaceImported === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
let imported = diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceImported",
|
|
);
|
|
if imported != Some(&JsValueSnapshot::Bool(true)) {
|
|
panic!(
|
|
"expected multihop namespace dynamic import to resolve, imported={imported:?}, error={:?}, order={:?}",
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceError"
|
|
),
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceOrderResult"
|
|
)
|
|
);
|
|
}
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceHasShared",
|
|
),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceDescriptor",
|
|
),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
let final_order = diagnostic_global_string(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousNamespaceOrderResult",
|
|
)
|
|
.unwrap_or("");
|
|
assert_order_before(final_order, "dcl", "import-resolved");
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_cycle_export_star_multihop_late_ambiguity_dispatches_error_without_aborting_later_module()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page =
|
|
browser
|
|
.fetch(&server.url(
|
|
"/compat/module-cycle-export-star-multihop-late-ambiguous-before-later-module",
|
|
))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleCycleExportStarMultihopLateAmbiguousError"),
|
|
Some(&JsValueSnapshot::String("ambiguous".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousContinuation",
|
|
),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(
|
|
&page,
|
|
"moduleCycleExportStarMultihopLateAmbiguousOrderResult",
|
|
),
|
|
Some(&JsValueSnapshot::String("error,later-module".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn static_import_waits_for_initializing_shared_non_cycle_dependency() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(
|
|
&server.url("/compat/module-static-import-waits-for-initializing-non-cycle-dependency"),
|
|
)
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleSharedInitializingParentAValue === 'ready' && window.moduleSharedInitializingParentBValue === 'ready'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentAValue"),
|
|
Some(&JsValueSnapshot::String("ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentBValue"),
|
|
Some(&JsValueSnapshot::String("ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentAObservedDepEnd"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentBObservedDepEnd"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
let dcl_order = diagnostic_global(&page, "moduleSharedInitializingDclOrder").and_then(
|
|
|value| match value {
|
|
JsValueSnapshot::String(value) => Some(value.as_str()),
|
|
_ => None,
|
|
},
|
|
);
|
|
assert!(
|
|
matches!(dcl_order, Some("dcl")),
|
|
"unexpected shared-initializing order: {dcl_order:?}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn static_import_waits_for_initializing_non_cycle_dependency_to_settle() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(
|
|
&server.url("/compat/module-static-import-waits-for-initializing-non-cycle-dependency"),
|
|
)
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleSharedInitializingParentAValue === 'ready' && window.moduleSharedInitializingParentBValue === 'ready'",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentAValue"),
|
|
Some(&JsValueSnapshot::String("ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentBValue"),
|
|
Some(&JsValueSnapshot::String("ready".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentAObservedDepEnd"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedInitializingParentBObservedDepEnd"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
let dcl_order = diagnostic_global(&page, "moduleSharedInitializingDclOrder");
|
|
assert!(
|
|
matches!(dcl_order, Some(JsValueSnapshot::String(order)) if order == "dcl"),
|
|
"unexpected moduleSharedInitializingDclOrder: {dcl_order:?}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn shared_failed_module_dependency_is_not_reexecuted_across_later_parser_owned_importers()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-shared-failed-dependency-is-not-reexecuted"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedFailedEvalCountDuringErrors"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedFailedEvalCountAtDcl"),
|
|
Some(&JsValueSnapshot::Number(1.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedFailedOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-shared-failed-a:error,module-shared-failed-b:error,dcl".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn shared_unsupported_module_dependency_is_not_retried_across_later_parser_owned_importers()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let mut tight_config = AppConfig::default();
|
|
tight_config.fetch_mut().set_request_timeout_ms(100);
|
|
let browser = Browser::new(tight_config)?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-shared-unsupported-dependency-is-not-retried"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedUnsupportedOrderResult"),
|
|
Some(&JsValueSnapshot::String(
|
|
"module-shared-unsupported-a:error,module-shared-unsupported-b:error,dcl".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleSharedUnsupportedReady"),
|
|
None
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_top_level_await_does_not_delay_domcontentloaded_past_initial_evaluation()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-top-level-await-delays-domcontentloaded"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTlaDclSawAwait"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTlaOrderResult"),
|
|
Some(&JsValueSnapshot::String("module-start,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_top_level_await_over_fifty_ms_does_not_delay_domcontentloaded_to_completion()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/module-top-level-await-over-fifty-ms-delays-domcontentloaded"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleLongTlaDclSawAwait"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleLongTlaWaitedPastLegacyCap"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleLongTlaOrderResult"),
|
|
Some(&JsValueSnapshot::String("module-start,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn module_tla_dependency_completion_does_not_delay_domcontentloaded() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let mut page = browser
|
|
.fetch(&server.url("/compat/module-tla-dependency-delays-parent-and-domcontentloaded"))
|
|
.await?;
|
|
browser
|
|
.wait_for_script_truthy(
|
|
&mut page,
|
|
"window.moduleTlaDependencyRootSawReady === true",
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTlaDependencyRootSawReady"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTlaDependencyDclSawReady"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "moduleTlaDependencyOrderResult"),
|
|
Some(&JsValueSnapshot::String("dep-start,dcl".to_owned()))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn dynamic_nomodule_script_commits_skip_and_stays_inert_after_mutation() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/dynamic-script-nomodule-commits-skip"))
|
|
.await?;
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicNomoduleRuns"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "dynamicNomoduleAfterMutation"),
|
|
Some(&JsValueSnapshot::Number(0.0))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn parse_time_lifecycle_tasks_keep_task_turn_boundaries_across_defer_dcl_async_and_load()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-lifecycle-tasks"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask,async-script,load".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskAfterLoadMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask,async-script,load,load-microtask"
|
|
.to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclMicrotaskSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskLoadMicrotaskSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn parse_time_lifecycle_async_phase_stays_outside_defer_and_dcl_turns() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/parse-time-lifecycle-tasks"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclAfterMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskFinalOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask,async-script,load".to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn parse_time_lifecycle_queue_can_stop_cleanly_at_domcontentloaded_stage() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/parse-time-lifecycle-tasks"),
|
|
RenderedDomWaitUntil::DomContentLoaded,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclMicrotaskSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskDclAfterMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask".to_owned()
|
|
))
|
|
);
|
|
assert_eq!(diagnostic_global(&page, "lifecycleTaskAsyncSeen"), None);
|
|
assert_eq!(diagnostic_global(&page, "lifecycleTaskLoadSeen"), None);
|
|
assert_eq!(diagnostic_global(&page, "lifecycleTaskFinalOrder"), None);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskLoadMicrotaskSeen"),
|
|
None
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskAfterLoadMicrotaskOrder"),
|
|
None
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn parse_time_lifecycle_queue_can_stop_cleanly_at_load_stage_after_load_microtask()
|
|
-> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/compat/parse-time-lifecycle-tasks"),
|
|
RenderedDomWaitUntil::Load,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskLoadSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskLoadMicrotaskSeen"),
|
|
Some(&JsValueSnapshot::Bool(true))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "lifecycleTaskAfterLoadMicrotaskOrder"),
|
|
Some(&JsValueSnapshot::String(
|
|
"defer-script,defer-microtask,dcl,dcl-microtask,async-script,load,load-microtask"
|
|
.to_owned()
|
|
))
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|