-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhydration-waiter.ts
More file actions
51 lines (44 loc) · 1.71 KB
/
Copy pathhydration-waiter.ts
File metadata and controls
51 lines (44 loc) · 1.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* hydration-waiter: don't interact with the app before it's interactive.
*
* Extracted from the iterate monorepo's internal Playwright test
* infrastructure (github.com/iterate/iterate, private).
*/
import type { Plugin, LocatorWithOriginal } from "../plugin-system.ts";
export type HydrationWaiterOptions = {
/** Selector for unhydrated state. Default: '[data-hydrated="false"]' */
selector?: string;
/** Timeout for hydration. Default: 10_000 */
timeout?: number;
/** Whether to skip this plugin. Default: false */
disabled?: boolean;
};
/**
* Waits for the app to be hydrated before any locator action.
* Looks for `[data-hydrated="false"]` and waits for it to disappear.
*
* Your app needs to cooperate: render `data-hydrated="false"` on some element
* server-side and flip it to "true" (or remove it) once the framework has
* hydrated. With React, for example, flip it in a top-level component that
* only runs client-side.
*/
export const hydrationWaiter = (options: HydrationWaiterOptions = {}): Plugin => {
if (process.env.PWDEBUG) {
return { name: "hydration-waiter" };
}
const selector = options.selector || '[data-hydrated="false"]';
const timeout = options.timeout || 10_000;
return {
name: "hydration-waiter",
middleware: async ({ page }, next) => {
if (options.disabled) return next();
const unhydratedLocator = page.locator(selector) as LocatorWithOriginal;
const isUnhydrated = await unhydratedLocator.isVisible();
if (isUnhydrated) {
// timeout is this plugin's hydration budget; waitFor_original bypasses spinner waiter.
await unhydratedLocator.waitFor_original({ state: "hidden", timeout });
}
return next();
},
};
};