#![cfg(feature = "integration")]
#![allow(clippy::float_cmp, clippy::unreadable_literal)]
use std::sync::Once;
use std::time::Duration;
use viewpoint_core::Browser;
use viewpoint_js::js;
static TRACING_INIT: Once = Once::new();
fn init_tracing() {
TRACING_INIT.call_once(|| {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::from_default_env()
.add_directive(tracing::Level::INFO.into()),
)
.with_test_writer()
.try_init()
.ok();
});
}
#[tokio::test]
async fn test_clock_date_mocking() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body><h1>Clock Test</h1></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
clock
.set_fixed_time("2024-01-01T00:00:00Z")
.await
.expect("Failed to set fixed time");
let timestamp: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
assert_eq!(timestamp as i64, 1704067200000);
let date_string: String = page
.evaluate(js! { new Date().toISOString() })
.await
.expect("Failed to get date string");
assert!(date_string.starts_with("2024-01-01T00:00:00"));
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_clock_timer_mocking() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body><script>window.timerFired = false;</script></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
let _: f64 = page
.evaluate(js! { setTimeout(() => { window.timerFired = true; }, 5000) })
.await
.expect("Failed to set timer");
let fired_before: bool = page
.evaluate(js! { window.timerFired })
.await
.expect("Failed to check timer");
assert!(!fired_before, "Timer should not have fired yet");
let timers_fired = clock
.run_for(Duration::from_secs(5))
.await
.expect("Failed to run for duration");
assert!(timers_fired >= 1, "At least one timer should have fired");
let fired_after: bool = page
.evaluate(js! { window.timerFired })
.await
.expect("Failed to check timer");
assert!(fired_after, "Timer should have fired after advancing time");
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_clock_fast_forward() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body><script>window.timerFired = false;</script></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
clock
.set_fixed_time(1704067200000i64)
.await
.expect("Failed to set time");
let time_before: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
clock
.fast_forward(Duration::from_secs(3600))
.await
.expect("Failed to fast forward");
let time_after: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
let expected_time = time_before + 3600.0 * 1000.0;
assert!(
(time_after - expected_time).abs() < 1000.0,
"Time should have advanced by ~1 hour. Before: {time_before}, After: {time_after}"
);
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_clock_run_all_timers() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body><script>window.count = 0;</script></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
let _: serde_json::Value = page
.evaluate(js! {
setTimeout(() => { window.count = window.count + 1; }, 1000);
setTimeout(() => { window.count = window.count + 1; }, 2000);
setTimeout(() => { window.count = window.count + 1; }, 5000);
})
.await
.expect("Failed to set timers");
let pending = clock
.pending_timer_count()
.await
.expect("Failed to get count");
assert_eq!(pending, 3, "Should have 3 pending timers");
let fired = clock.run_all_timers().await.expect("Failed to run timers");
assert!(fired >= 3, "Should have fired at least 3 timers");
let count: f64 = page
.evaluate(js! { window.count })
.await
.expect("Failed to get count");
assert_eq!(count as i32, 3, "All timers should have incremented count");
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_clock_system_time() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
clock
.set_system_time("2024-06-15T12:00:00Z")
.await
.expect("Failed to set system time");
let time1: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
tokio::time::sleep(Duration::from_millis(100)).await;
let time2: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
assert!(
time2 > time1,
"System time should flow: {time2} should be > {time1}"
);
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_clock_pause_resume() {
init_tracing();
let browser = Browser::launch()
.headless(true)
.launch()
.await
.expect("Failed to launch browser");
let context = browser
.new_context()
.await
.expect("Failed to create context");
let page = context.new_page().await.expect("Failed to create page");
page.set_content("<html><body></body></html>")
.set()
.await
.expect("Failed to set content");
let mut clock = page.clock();
clock.install().await.expect("Failed to install clock");
clock
.pause_at("2024-01-01T12:00:00Z")
.await
.expect("Failed to pause");
let time1: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
tokio::time::sleep(Duration::from_millis(50)).await;
let time2: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
assert_eq!(time1, time2, "Time should be paused");
clock.resume().await.expect("Failed to resume");
tokio::time::sleep(Duration::from_millis(100)).await;
let time3: f64 = page
.evaluate(js! { Date.now() })
.await
.expect("Failed to get time");
assert!(time3 > time2, "Time should be flowing after resume");
clock.uninstall().await.expect("Failed to uninstall clock");
browser.close().await.expect("Failed to close browser");
}