mirror of
https://github.com/lexmount/moli.git
synced 2026-09-23 16:01:25 +00:00
885 lines
24 KiB
Rust
885 lines
24 KiB
Rust
use moli_test_support as support;
|
|
|
|
use anyhow::Result;
|
|
use moli_core::runtime::{
|
|
Browser, BrowserConfig as AppConfig, FetchReadinessTimeout, FetchTimeoutPhase,
|
|
RawDocumentPageRequired, RenderedDomWaitUntil,
|
|
};
|
|
use moli_fetch::Request;
|
|
use std::time::{Duration, Instant};
|
|
use support::FixtureServer;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::TcpListener;
|
|
use tokio::task::JoinHandle;
|
|
use url::Url;
|
|
|
|
#[path = "fetch/security.rs"]
|
|
mod security;
|
|
|
|
async fn spawn_stalled_binary_response_server() -> Result<(String, JoinHandle<()>)> {
|
|
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
|
let address = listener.local_addr()?;
|
|
let server = tokio::spawn(async move {
|
|
let Ok((mut stream, _)) = listener.accept().await else {
|
|
return;
|
|
};
|
|
let mut request = [0_u8; 4096];
|
|
let Ok(read) = stream.read(&mut request).await else {
|
|
return;
|
|
};
|
|
if read == 0 {
|
|
return;
|
|
}
|
|
if stream
|
|
.write_all(
|
|
b"HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: 1048576\r\nConnection: keep-alive\r\n\r\n",
|
|
)
|
|
.await
|
|
.is_err()
|
|
{
|
|
return;
|
|
}
|
|
std::future::pending::<()>().await;
|
|
});
|
|
Ok((format!("http://{address}/stalled.bin"), server))
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetches_static_fixture() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser.session().open(&server.url("/static")).await?;
|
|
|
|
assert_eq!(page.status(), 200);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("fixture static")
|
|
);
|
|
assert!(
|
|
page.headers()
|
|
.iter()
|
|
.any(|(name, _)| name == "content-type")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_redirects() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser.fetch(&server.url("/redirect")).await?;
|
|
|
|
assert_eq!(page.status(), 200);
|
|
assert_eq!(page.final_url().path(), "/static");
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("fixture static")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_reports_404_as_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = browser
|
|
.fetch(&server.url("/net/upstream/xhr/404"))
|
|
.await
|
|
.unwrap_err();
|
|
|
|
assert!(error.to_string().contains("404"));
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_renders_403_challenge_document() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_allow_http_error(&server.url("/net/upstream/xhr/403-challenge"))
|
|
.await?;
|
|
assert_eq!(page.status(), 403);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("forbidden challenge")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_reports_500_as_error() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = browser
|
|
.fetch(&server.url("/net/upstream/xhr/500"))
|
|
.await
|
|
.unwrap_err();
|
|
|
|
assert!(error.to_string().contains("500"));
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_allow_http_error_returns_page_for_success() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_allow_http_error(&server.url("/static"))
|
|
.await?;
|
|
assert_eq!(page.status(), 200);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("fixture static")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_with_wait_until_rejects_raw_document_through_page_api() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/net/upstream/xhr/binary"),
|
|
RenderedDomWaitUntil::Load,
|
|
Duration::from_secs(1),
|
|
)
|
|
.await
|
|
.expect_err("the Page fetch API must reject a raw non-HTML document");
|
|
|
|
assert_eq!(
|
|
error.to_string(),
|
|
"raw non-HTML document cannot be returned through the Page fetch API"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn page_fetch_rejects_raw_response_without_waiting_for_its_stalled_body() -> Result<()> {
|
|
let (url, server) = spawn_stalled_binary_response_server().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
let started = Instant::now();
|
|
|
|
let error = tokio::time::timeout(
|
|
Duration::from_secs(1),
|
|
browser.fetch_with_wait_until(&url, RenderedDomWaitUntil::Load, Duration::from_millis(100)),
|
|
)
|
|
.await
|
|
.expect("the Page API must not wait for a raw response body")
|
|
.expect_err("the Page API must reject a raw non-HTML document");
|
|
|
|
assert!(error.is::<RawDocumentPageRequired>());
|
|
assert_eq!(
|
|
error.to_string(),
|
|
"raw non-HTML document cannot be returned through the Page fetch API"
|
|
);
|
|
assert!(
|
|
started.elapsed() < Duration::from_secs(1),
|
|
"raw Page rejection must not fall through to the HTTP transport timeout"
|
|
);
|
|
server.abort();
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn raw_document_body_materialization_uses_the_fetch_deadline() -> Result<()> {
|
|
let (url, server) = spawn_stalled_binary_response_server().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = tokio::time::timeout(
|
|
Duration::from_secs(1),
|
|
browser.fetch_request_document_allow_http_error_with_wait_until(
|
|
Request::get(&url)?,
|
|
RenderedDomWaitUntil::Load,
|
|
Duration::from_millis(100),
|
|
),
|
|
)
|
|
.await
|
|
.expect("raw body materialization must honor the fetch deadline")
|
|
.expect_err("the stalled raw body must time out");
|
|
let timeout = error
|
|
.downcast_ref::<FetchReadinessTimeout>()
|
|
.expect("the raw body timeout must retain its typed phase");
|
|
assert_eq!(timeout.phase(), FetchTimeoutPhase::StreamingMainBody);
|
|
|
|
server.abort();
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn fetch_allow_http_error_renders_404_response() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
let url = server.url("/net/upstream/xhr/404");
|
|
|
|
let page = browser.fetch_allow_http_error(&url).await?;
|
|
assert_eq!(page.status(), 404);
|
|
assert_eq!(page.final_url().as_str(), url);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("Not Found")
|
|
);
|
|
assert!(
|
|
page.headers()
|
|
.iter()
|
|
.any(|(name, _)| name == "content-type")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_location_replace_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/location-nav/replace-source"))
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(page.final_url().query(), Some("from=replace"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=replace")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("replace-source")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_location_assign_href_and_search_setters_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let assign_page = browser
|
|
.fetch(&server.url("/location-nav/assign-source"))
|
|
.await?;
|
|
assert_eq!(assign_page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(assign_page.final_url().query(), Some("from=assign"));
|
|
assert!(
|
|
assign_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=assign")
|
|
);
|
|
assert!(
|
|
!assign_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("assign-source")
|
|
);
|
|
|
|
let href_page = browser
|
|
.fetch(&server.url("/location-nav/href-source"))
|
|
.await?;
|
|
assert_eq!(href_page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(href_page.final_url().query(), Some("from=href"));
|
|
assert!(
|
|
href_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=href")
|
|
);
|
|
assert!(
|
|
!href_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("href-source")
|
|
);
|
|
|
|
let pathname_page = browser
|
|
.fetch(&server.url("/location-nav/pathname-source"))
|
|
.await?;
|
|
assert_eq!(
|
|
pathname_page.final_url().path(),
|
|
"/location-nav/pathname-target"
|
|
);
|
|
assert_eq!(pathname_page.final_url().query(), None);
|
|
assert!(
|
|
pathname_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=pathname")
|
|
);
|
|
assert!(
|
|
!pathname_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("pathname-source")
|
|
);
|
|
|
|
let search_page = browser
|
|
.fetch(&server.url("/location-nav/search-source"))
|
|
.await?;
|
|
assert_eq!(
|
|
search_page.final_url().path(),
|
|
"/location-nav/search-source"
|
|
);
|
|
assert_eq!(search_page.final_url().query(), Some("from=search"));
|
|
assert!(
|
|
search_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=search")
|
|
);
|
|
assert!(
|
|
!search_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("search-source")
|
|
);
|
|
|
|
let host_page = browser
|
|
.fetch(&server.url("/location-nav/host-source"))
|
|
.await?;
|
|
assert_eq!(host_page.final_url().host_str(), Some("localhost"));
|
|
assert_eq!(host_page.final_url().path(), "/location-nav/host-source");
|
|
assert!(
|
|
host_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=host")
|
|
);
|
|
assert!(
|
|
!host_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("host-source")
|
|
);
|
|
|
|
let hostname_page = browser
|
|
.fetch(&server.url("/location-nav/hostname-source"))
|
|
.await?;
|
|
assert_eq!(hostname_page.final_url().host_str(), Some("localhost"));
|
|
assert_eq!(
|
|
hostname_page.final_url().path(),
|
|
"/location-nav/hostname-source"
|
|
);
|
|
assert!(
|
|
hostname_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=hostname")
|
|
);
|
|
assert!(
|
|
!hostname_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("hostname-source")
|
|
);
|
|
|
|
let target_server = FixtureServer::spawn().await?;
|
|
let target_port = fixture_url_port(&target_server.url("/static"));
|
|
let port_page = browser
|
|
.fetch(&server.url(&format!(
|
|
"/location-nav/port-source?targetPort={target_port}"
|
|
)))
|
|
.await?;
|
|
let expected_query = format!("targetPort={target_port}");
|
|
assert_eq!(port_page.final_url().port(), Some(target_port));
|
|
assert_eq!(port_page.final_url().path(), "/location-nav/port-source");
|
|
assert_eq!(port_page.final_url().query(), Some(expected_query.as_str()));
|
|
assert!(
|
|
port_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=port")
|
|
);
|
|
assert!(
|
|
!port_page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("port-source")
|
|
);
|
|
target_server.shutdown().await;
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
fn fixture_url_port(url: &str) -> u16 {
|
|
Url::parse(url)
|
|
.expect("fixture URL should parse")
|
|
.port()
|
|
.expect("fixture URL should include an explicit port")
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_location_reload_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/location-nav/reload-source"))
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/reload-source");
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-reload=done")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("reload-source")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_same_href_location_assignment_after_script_cookie_challenge() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/location-nav/same-href-cookie-challenge"))
|
|
.await?;
|
|
|
|
assert_eq!(
|
|
page.final_url().path(),
|
|
"/location-nav/same-href-cookie-challenge"
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("same-href-cookie-challenge=done")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("same-href-cookie-challenge=source")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_chained_location_navigation_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/location-nav/chain-source"))
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(page.final_url().query(), Some("from=chain-mid"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=chain-mid")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("chain-source")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("<main id=\"mid\">chain-mid</main>")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_timeout_chained_location_navigation_during_network_idle_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/chain-timeout-source"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(page.final_url().query(), Some("from=chain-mid"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=chain-mid")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("chain-timeout-source")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("chain-source")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("<main id=\"mid\">chain-mid</main>")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_timeout_chained_location_navigation_during_domstable_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/chain-timeout-source"),
|
|
RenderedDomWaitUntil::DomStable,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(page.final_url().query(), Some("from=chain-mid"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=chain-mid")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("chain-timeout-source")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("chain-source")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("<main id=\"mid\">chain-mid</main>")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rejects_location_navigation_loop_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = match browser.fetch(&server.url("/location-nav/loop-a")).await {
|
|
Ok(_) => panic!("location navigation loop should fail"),
|
|
Err(error) => error,
|
|
};
|
|
let error_message = format!("{error:#}");
|
|
assert!(
|
|
error_message.contains("too many chained location navigations"),
|
|
"unexpected error: {error:#}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_async_location_search_during_network_idle_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/search-async-source"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/search-async-source");
|
|
assert_eq!(page.final_url().query(), Some("from=search-async"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=search-async")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("search-async-source")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_async_location_search_during_domstable_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/search-async-source"),
|
|
RenderedDomWaitUntil::DomStable,
|
|
Duration::from_secs(5),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/search-async-source");
|
|
assert_eq!(page.final_url().query(), Some("from=search-async"));
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=search-async")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("search-async-source")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rejects_timeout_location_navigation_loop_during_network_idle_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = match browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/loop-timeout-source"),
|
|
RenderedDomWaitUntil::NetworkIdle,
|
|
Duration::from_secs(20),
|
|
)
|
|
.await
|
|
{
|
|
Ok(_) => panic!("location navigation loop should fail during networkidle wait"),
|
|
Err(error) => error,
|
|
};
|
|
let error_message = format!("{error:#}");
|
|
assert!(
|
|
error_message.contains("too many chained location navigations"),
|
|
"unexpected error: {error:#}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rejects_timeout_location_navigation_loop_during_domstable_wait() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let error = match browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/loop-timeout-source"),
|
|
RenderedDomWaitUntil::DomStable,
|
|
Duration::from_secs(20),
|
|
)
|
|
.await
|
|
{
|
|
Ok(_) => panic!("location navigation loop should fail during domstable wait"),
|
|
Err(error) => error,
|
|
};
|
|
let error_message = format!("{error:#}");
|
|
assert!(
|
|
error_message.contains("too many chained location navigations"),
|
|
"unexpected error: {error:#}"
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn follows_post_parse_timeout_location_assign_during_page_load() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch_with_wait_until(
|
|
&server.url("/location-nav/assign-post-parse-timeout-source"),
|
|
RenderedDomWaitUntil::DomStable,
|
|
Duration::from_secs(2),
|
|
)
|
|
.await?;
|
|
|
|
assert_eq!(page.final_url().path(), "/location-nav/target");
|
|
assert_eq!(
|
|
page.final_url().query(),
|
|
Some("from=assign-post-parse-timeout")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("location-target=assign-post-parse-timeout")
|
|
);
|
|
assert!(
|
|
!page
|
|
.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("assign-post-parse-timeout-source"),
|
|
"{}",
|
|
page.serialize_html_async().await.unwrap()
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn repeated_date_locale_formatting_does_not_crash_fetch() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/date-locale-bomb"))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-ok=\"1\"")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn date_locale_methods_return_stable_values_and_invalid_date() -> Result<()> {
|
|
let server = FixtureServer::spawn().await?;
|
|
let browser = Browser::new(AppConfig::default())?;
|
|
|
|
let page = browser
|
|
.fetch(&server.url("/compat/date-locale-details"))
|
|
.await?;
|
|
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-locale-string=\"3/24/2024, 4:05:06 PM\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-locale-date=\"3/24/2024\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-locale-time=\"4:05:06 PM\"")
|
|
);
|
|
assert!(
|
|
page.serialize_html_async()
|
|
.await
|
|
.unwrap()
|
|
.contains("data-invalid=\"Invalid Date\"")
|
|
);
|
|
|
|
server.shutdown().await;
|
|
Ok(())
|
|
}
|