mirror of
https://github.com/lexmount/moli.git
synced 2026-09-23 16:01:25 +00:00
Preserve response filtering through script and service worker loading, validate CORS authorization across redirects, and reject opaque responses before computing supported integrity digests. Keep empty and unsupported metadata behavior unchanged. Add parser, dynamic script, module, and service worker regressions covering 39 scenarios. Validated with cargo fmt --all, full workspace Clippy, and cargo nextest run --no-fail-fast (17457 passed, 13 skipped).
6711 lines
217 KiB
Rust
6711 lines
217 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 = browser
|
|
.fetch(&server.url("/compat/parse-time-async-classic-chunked"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawTail"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
assert_eq!(
|
|
diagnostic_global(&page, "parseTimeAsyncSawDcl"),
|
|
Some(&JsValueSnapshot::Bool(false))
|
|
);
|
|
|
|
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(())
|
|
}
|