turbopack_bench/util/
page_guard.rs1use std::{sync::Arc, time::Duration};
2
3use anyhow::{Context, Result, anyhow};
4use chromiumoxide::{
5 Page,
6 cdp::js_protocol::runtime::{EventBindingCalled, EventExceptionThrown},
7 listeners::EventStream,
8};
9use futures::{Stream, StreamExt};
10use tokio::time::timeout;
11
12use crate::{BINDING_NAME, PreparedApp};
13
14const MAX_HYDRATION_TIMEOUT: Duration = Duration::from_secs(120);
15const TEST_APP_HYDRATION_DONE: &str = "Hydration done";
16
17pub struct PageGuard<'a> {
19 page: Option<Page>,
20 app: Option<PreparedApp<'a>>,
21 events: Box<dyn Stream<Item = Event> + Unpin>,
22}
23
24enum Event {
25 EventBindingCalled(Arc<EventBindingCalled>),
26 EventExceptionThrown(Arc<EventExceptionThrown>),
27}
28
29impl<'a> PageGuard<'a> {
30 pub fn new(
32 page: Page,
33 events: EventStream<EventBindingCalled>,
34 errors: EventStream<EventExceptionThrown>,
35 app: PreparedApp<'a>,
36 ) -> Self {
37 Self {
38 page: Some(page),
39 app: Some(app),
40 events: Box::new(futures::stream::select(
41 events.map(Event::EventBindingCalled),
42 errors.map(Event::EventExceptionThrown),
43 )),
44 }
45 }
46
47 pub fn page(&self) -> &Page {
49 self.page.as_ref().unwrap()
51 }
52
53 pub async fn close_page(mut self) -> Result<PreparedApp<'a>> {
55 self.page.take().unwrap().close().await?;
57 Ok(
58 self.app.take().unwrap(),
60 )
61 }
62
63 pub async fn wait_for_binding(&mut self, payload: &str) -> Result<()> {
65 while let Some(event) = self.events.next().await {
66 match event {
67 Event::EventBindingCalled(event) => {
68 if event.name == BINDING_NAME && event.payload == payload {
69 return Ok(());
70 }
71 }
72 Event::EventExceptionThrown(event) => {
73 anyhow::bail!("Exception throw in page: {}", event.exception_details)
74 }
75 }
76 }
77
78 Err(anyhow!("event stream ended before binding was called"))
79 }
80
81 pub async fn wait_for_hydration(&mut self) -> Result<()> {
83 timeout(
84 MAX_HYDRATION_TIMEOUT,
85 self.wait_for_binding(TEST_APP_HYDRATION_DONE),
86 )
87 .await
88 .context("Timeout happened while waiting for hydration")?
89 .context("Error happened while waiting for hydration")?;
90 Ok(())
91 }
92}
93
94impl Drop for PageGuard<'_> {
95 fn drop(&mut self) {
96 if let Some(page) = self.page.take() {
98 futures::executor::block_on(page.close()).expect("failed to close page");
101 }
102 }
103}