@e2edev/web 0.11.0-canary-20260921180210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +112 -0
  4. package/dist/.build.json +1 -0
  5. package/dist/actions.d.ts +23 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +174 -0
  8. package/dist/actions.js.map +1 -0
  9. package/dist/attempt-session.d.ts +76 -0
  10. package/dist/attempt-session.d.ts.map +1 -0
  11. package/dist/attempt-session.js +290 -0
  12. package/dist/attempt-session.js.map +1 -0
  13. package/dist/browser-connection.d.ts +34 -0
  14. package/dist/browser-connection.d.ts.map +1 -0
  15. package/dist/browser-connection.js +87 -0
  16. package/dist/browser-connection.js.map +1 -0
  17. package/dist/capture-scope.d.ts +21 -0
  18. package/dist/capture-scope.d.ts.map +1 -0
  19. package/dist/capture-scope.js +61 -0
  20. package/dist/capture-scope.js.map +1 -0
  21. package/dist/cdp-recovery.d.ts +29 -0
  22. package/dist/cdp-recovery.d.ts.map +1 -0
  23. package/dist/cdp-recovery.js +84 -0
  24. package/dist/cdp-recovery.js.map +1 -0
  25. package/dist/cdp-selectors.d.ts +14 -0
  26. package/dist/cdp-selectors.d.ts.map +1 -0
  27. package/dist/cdp-selectors.js +48 -0
  28. package/dist/cdp-selectors.js.map +1 -0
  29. package/dist/dialogs.d.ts +37 -0
  30. package/dist/dialogs.d.ts.map +1 -0
  31. package/dist/dialogs.js +78 -0
  32. package/dist/dialogs.js.map +1 -0
  33. package/dist/engine.d.ts +26 -0
  34. package/dist/engine.d.ts.map +1 -0
  35. package/dist/engine.js +166 -0
  36. package/dist/engine.js.map +1 -0
  37. package/dist/evaluation.d.ts +11 -0
  38. package/dist/evaluation.d.ts.map +1 -0
  39. package/dist/evaluation.js +27 -0
  40. package/dist/evaluation.js.map +1 -0
  41. package/dist/index.d.ts +21 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +14 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/install.d.ts +40 -0
  46. package/dist/install.d.ts.map +1 -0
  47. package/dist/install.js +117 -0
  48. package/dist/install.js.map +1 -0
  49. package/dist/locators.d.ts +74 -0
  50. package/dist/locators.d.ts.map +1 -0
  51. package/dist/locators.js +260 -0
  52. package/dist/locators.js.map +1 -0
  53. package/dist/observation-capture.d.ts +12 -0
  54. package/dist/observation-capture.d.ts.map +1 -0
  55. package/dist/observation-capture.js +93 -0
  56. package/dist/observation-capture.js.map +1 -0
  57. package/dist/observation.d.ts +63 -0
  58. package/dist/observation.d.ts.map +1 -0
  59. package/dist/observation.js +262 -0
  60. package/dist/observation.js.map +1 -0
  61. package/dist/observe.d.ts +51 -0
  62. package/dist/observe.d.ts.map +1 -0
  63. package/dist/observe.js +104 -0
  64. package/dist/observe.js.map +1 -0
  65. package/dist/operation-budget.d.ts +11 -0
  66. package/dist/operation-budget.d.ts.map +1 -0
  67. package/dist/operation-budget.js +40 -0
  68. package/dist/operation-budget.js.map +1 -0
  69. package/dist/protected-app.d.ts +32 -0
  70. package/dist/protected-app.d.ts.map +1 -0
  71. package/dist/protected-app.js +39 -0
  72. package/dist/protected-app.js.map +1 -0
  73. package/dist/read-node.d.ts +170 -0
  74. package/dist/read-node.d.ts.map +1 -0
  75. package/dist/read-node.js +823 -0
  76. package/dist/read-node.js.map +1 -0
  77. package/dist/refs.d.ts +43 -0
  78. package/dist/refs.d.ts.map +1 -0
  79. package/dist/refs.js +85 -0
  80. package/dist/refs.js.map +1 -0
  81. package/dist/route-pattern.d.ts +15 -0
  82. package/dist/route-pattern.d.ts.map +1 -0
  83. package/dist/route-pattern.js +68 -0
  84. package/dist/route-pattern.js.map +1 -0
  85. package/dist/support.d.ts +119 -0
  86. package/dist/support.d.ts.map +1 -0
  87. package/dist/support.js +299 -0
  88. package/dist/support.js.map +1 -0
  89. package/dist/surface.d.ts +257 -0
  90. package/dist/surface.d.ts.map +1 -0
  91. package/dist/surface.js +587 -0
  92. package/dist/surface.js.map +1 -0
  93. package/dist/video.d.ts +59 -0
  94. package/dist/video.d.ts.map +1 -0
  95. package/dist/video.js +115 -0
  96. package/dist/video.js.map +1 -0
  97. package/dist/web.d.ts +191 -0
  98. package/dist/web.d.ts.map +1 -0
  99. package/dist/web.js +354 -0
  100. package/dist/web.js.map +1 -0
  101. package/package.json +67 -0
@@ -0,0 +1,84 @@
1
+ import { EngineError } from 'e2e/engine';
2
+ import { connectCdp } from './browser-connection.js';
3
+ import { registerCdpSelectors, requireCdpShadowTracking } from './cdp-selectors.js';
4
+ import { connectionAbort } from './operation-budget.js';
5
+ /** A failed reconnect must never quietly provision another session. */
6
+ export function recoveryFailed(detail) {
7
+ return new EngineError('ENGINE_FAILURE', `CDP recovery failed: ${detail}`, { retryable: false });
8
+ }
9
+ /** Reads protocol identity, independent of URL, document contents, and tab order. */
10
+ export async function targetIdentity(page) {
11
+ const session = await page.context().newCDPSession(page);
12
+ try {
13
+ return (await session.send('Target.getTargetInfo')).targetInfo;
14
+ }
15
+ finally {
16
+ await session.detach().catch(() => undefined);
17
+ }
18
+ }
19
+ /** Owns an uncommitted connection until its identity is proven; aborted and late connections detach. */
20
+ export async function attachPersistent(resolve, budget, previous) {
21
+ const { signal } = budget;
22
+ const deadline = Date.now() + budget.timeoutMs;
23
+ const check = () => { if (signal.aborted)
24
+ throw connectionAbort(signal, 'CDP connection'); };
25
+ check();
26
+ const endpoint = await resolve(signal);
27
+ check();
28
+ if (typeof endpoint !== 'string' || endpoint.trim() === '')
29
+ throw recoveryFailed('the endpoint is empty');
30
+ const remaining = deadline - Date.now();
31
+ if (remaining <= 0)
32
+ throw new EngineError('OPERATION_TIMEOUT', 'CDP connection timed out', { retryable: false });
33
+ const browser = await connectCdp(endpoint, remaining);
34
+ const detach = () => { void browser.close().catch(() => undefined); };
35
+ signal.addEventListener('abort', detach, { once: true });
36
+ try {
37
+ check();
38
+ const session = await browser.newBrowserCDPSession();
39
+ let contextId;
40
+ try {
41
+ const contexts = await session.send('Target.getBrowserContexts');
42
+ if (contexts.browserContextIds.length > 0) {
43
+ throw recoveryFailed('the remote browser must contain only its dedicated default context');
44
+ }
45
+ contextId = contexts.defaultBrowserContextId;
46
+ }
47
+ finally {
48
+ await session.detach().catch(() => undefined);
49
+ }
50
+ check();
51
+ const context = browser.contexts()[0];
52
+ if (context === undefined)
53
+ throw recoveryFailed('the default context is missing');
54
+ if (contextId === undefined || contextId === '')
55
+ throw recoveryFailed('the default context identity is unavailable');
56
+ if (previous !== undefined && contextId !== previous.contextId) {
57
+ throw recoveryFailed('reconnectEndpoint returned a different browser');
58
+ }
59
+ await registerCdpSelectors(context, { signal, timeoutMs: deadline - Date.now() });
60
+ check();
61
+ if (previous?.target === undefined)
62
+ return { browser, context, page: null, identity: { contextId } };
63
+ for (const page of context.pages()) {
64
+ const target = await targetIdentity(page);
65
+ check();
66
+ if (target.targetId !== previous.target.targetId)
67
+ continue;
68
+ if (target.browserContextId !== previous.target.browserContextId) {
69
+ throw recoveryFailed('the original page belongs to a different context');
70
+ }
71
+ await requireCdpShadowTracking(page, { signal, timeoutMs: deadline - Date.now() });
72
+ return { browser, context, page, identity: { contextId, target } };
73
+ }
74
+ throw recoveryFailed('the original page no longer exists');
75
+ }
76
+ catch (cause) {
77
+ await browser.close().catch(() => undefined);
78
+ throw cause;
79
+ }
80
+ finally {
81
+ signal.removeEventListener('abort', detach);
82
+ }
83
+ }
84
+ //# sourceMappingURL=cdp-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-recovery.js","sourceRoot":"","sources":["../src/cdp-recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** Registers the closed-root masking selector in a CDP default context. */
2
+ import type { BrowserContext, Page } from 'playwright';
3
+ import { type ConnectionBudget } from './operation-budget.ts';
4
+ /**
5
+ * Playwright 1.63 seeds custom selectors only in newly created contexts, not
6
+ * the persistent context returned by connectOverCDP. Its public register API
7
+ * rejects an already registered name before reaching that context. Use the
8
+ * same context channel as selectors.register, without allocating global names
9
+ * on every reconnect. Missing channel support must prevent masked capture.
10
+ */
11
+ export declare function registerCdpSelectors(context: BrowserContext, budget: ConnectionBudget): Promise<void>;
12
+ /** Existing closed roots cannot be recovered if their document loaded without the tracking hook. */
13
+ export declare function requireCdpShadowTracking(page: Page, budget: ConnectionBudget): Promise<void>;
14
+ //# sourceMappingURL=cdp-selectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-selectors.d.ts","sourceRoot":"","sources":["../src/cdp-selectors.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAU/E;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3G;AAED,oGAAoG;AACpG,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CASlG"}
@@ -0,0 +1,48 @@
1
+ /** Registers the closed-root masking selector in a CDP default context. */
2
+ import { EngineError } from 'e2e/engine';
3
+ import { connectionAbort } from './operation-budget.js';
4
+ import { CLOSED_SHADOW_ROOTS_KEY, CLOSED_SHADOW_SELECTOR_ENGINE, CLOSED_SHADOW_SELECTOR_ENGINE_SOURCE } from './read-node.js';
5
+ /**
6
+ * Playwright 1.63 seeds custom selectors only in newly created contexts, not
7
+ * the persistent context returned by connectOverCDP. Its public register API
8
+ * rejects an already registered name before reaching that context. Use the
9
+ * same context channel as selectors.register, without allocating global names
10
+ * on every reconnect. Missing channel support must prevent masked capture.
11
+ */
12
+ export async function registerCdpSelectors(context, budget) {
13
+ if (budget.signal.aborted)
14
+ throw connectionAbort(budget.signal, 'CDP selector registration');
15
+ if (budget.timeoutMs <= 0) {
16
+ throw new EngineError('OPERATION_TIMEOUT', 'CDP selector registration timed out', { retryable: false });
17
+ }
18
+ const channel = Reflect.get(context, '_channel');
19
+ const register = channel !== null && typeof channel === 'object' ? Reflect.get(channel, 'registerSelectorEngine') : undefined;
20
+ if (typeof register !== 'function') {
21
+ throw new EngineError('UNSUPPORTED_CAPABILITY', 'this Playwright version cannot install secure-field masks in the CDP context', { retryable: false });
22
+ }
23
+ try {
24
+ await channel.registerSelectorEngine({ selectorEngine: {
25
+ name: CLOSED_SHADOW_SELECTOR_ENGINE,
26
+ source: `(${CLOSED_SHADOW_SELECTOR_ENGINE_SOURCE})(undefined)`,
27
+ contentScript: false,
28
+ } }, { timeout: budget.timeoutMs, signal: budget.signal });
29
+ }
30
+ catch (cause) {
31
+ // A version that seeds CDP contexts already has our process-wide registration.
32
+ const duplicate = `"${CLOSED_SHADOW_SELECTOR_ENGINE}" selector engine has been already registered`;
33
+ if (!(cause instanceof Error) || !cause.message.endsWith(duplicate))
34
+ throw cause;
35
+ }
36
+ }
37
+ /** Existing closed roots cannot be recovered if their document loaded without the tracking hook. */
38
+ export async function requireCdpShadowTracking(page, budget) {
39
+ if (budget.signal.aborted)
40
+ throw connectionAbort(budget.signal, 'CDP tracking verification');
41
+ const tracked = await Promise.all(page.frames().map((frame) => frame.evaluate((key) => Reflect.get(globalThis, Symbol.for(key)) instanceof WeakMap, CLOSED_SHADOW_ROOTS_KEY)));
42
+ if (budget.signal.aborted)
43
+ throw connectionAbort(budget.signal, 'CDP tracking verification');
44
+ if (tracked.some((present) => !present)) {
45
+ throw new EngineError('ENGINE_FAILURE', 'CDP recovery failed: secure-field tracking is unavailable after a document changed while disconnected', { retryable: false });
46
+ }
47
+ }
48
+ //# sourceMappingURL=cdp-selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-selectors.js","sourceRoot":"","sources":["../src/cdp-selectors.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAG3E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,eAAe,EAAyB,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,oCAAoC,EAAE,MAAM,gBAAgB,CAAC;AAS9H;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAuB,EAAE,MAAwB;IAC1F,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC7F,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,qCAAqC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1G,CAAC;IACD,MAAM,OAAO,GAAY,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9H,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,WAAW,CAAC,wBAAwB,EAAE,8EAA8E,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACxJ,CAAC;IACD,IAAI,CAAC;QACH,MAAO,OAA2B,CAAC,sBAAsB,CAAC,EAAE,cAAc,EAAE;gBAC1E,IAAI,EAAE,6BAA6B;gBACnC,MAAM,EAAE,IAAI,oCAAoC,cAAc;gBAC9D,aAAa,EAAE,KAAK;aACrB,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,+EAA+E;QAC/E,MAAM,SAAS,GAAG,IAAI,6BAA6B,+CAA+C,CAAC;QACnG,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,MAAM,KAAK,CAAC;IACnF,CAAC;AACH,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAU,EAAE,MAAwB;IACjF,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC7F,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAC5D,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,YAAY,OAAO,EAAE,uBAAuB,CAAC,CAC9G,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC7F,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,uGAAuG,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACzK,CAAC;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Native dialog routing for one attempt. Handlers are registered by the `web`
3
+ * fixture; the surface owns the router so a dialog that fires before a test
4
+ * ever touches `web` is still accounted for instead of silently dismissed.
5
+ */
6
+ import type { Dialog as PwDialog } from 'playwright';
7
+ import { ErrorLatch } from './support.ts';
8
+ /** A native dialog as a test's handler sees it. */
9
+ export interface Dialog {
10
+ /** The dialog's text. */
11
+ readonly message: string;
12
+ /** Accepts the dialog once. */
13
+ accept(text?: string): Promise<void>;
14
+ /** Dismisses the dialog once. */
15
+ dismiss(): Promise<void>;
16
+ }
17
+ export type DialogHandler = 'accept' | 'dismiss' | ((dialog: Dialog) => void | Promise<void>);
18
+ export declare class DialogRouter {
19
+ private readonly latch;
20
+ private registrations;
21
+ /**
22
+ * `latch` is shared with the surface: a dialog failure and a route-handler
23
+ * failure surface through one check at the next step.
24
+ */
25
+ constructor(latch?: ErrorLatch);
26
+ /**
27
+ * Registers a handler (the newest wins) and returns its idempotent
28
+ * unsubscribe. Registrations are tracked by identity, so registering the
29
+ * same handler twice and unsubscribing once leaves one in place.
30
+ */
31
+ add(handler: DialogHandler): () => void;
32
+ /** Rethrows an error latched by an unhandled or failing dialog handler. */
33
+ throwPending(): void;
34
+ /** Routes one native dialog to the newest registered handler. */
35
+ dispatch(dialog: PwDialog): Promise<void>;
36
+ }
37
+ //# sourceMappingURL=dialogs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.d.ts","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,UAAU,EAAW,MAAM,cAAc,CAAC;AAEnD,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACrB,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,iCAAiC;IACjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAM9F,qBAAa,YAAY;IAOX,OAAO,CAAC,QAAQ,CAAC,KAAK;IANlC,OAAO,CAAC,aAAa,CAAsB;IAE3C;;;OAGG;IACH,YAA6B,KAAK,GAAE,UAA6B,EAAI;IAErE;;;;OAIG;IACH,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAMtC;IAED,2EAA2E;IAC3E,YAAY,IAAI,IAAI,CAEnB;IAED,iEAAiE;IAC3D,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD9C;CACF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Native dialog routing for one attempt. Handlers are registered by the `web`
3
+ * fixture; the surface owns the router so a dialog that fires before a test
4
+ * ever touches `web` is still accounted for instead of silently dismissed.
5
+ */
6
+ import { EngineError } from 'e2e/engine';
7
+ import { ErrorLatch, message } from './support.js';
8
+ export class DialogRouter {
9
+ latch;
10
+ registrations = [];
11
+ /**
12
+ * `latch` is shared with the surface: a dialog failure and a route-handler
13
+ * failure surface through one check at the next step.
14
+ */
15
+ constructor(latch = new ErrorLatch()) {
16
+ this.latch = latch;
17
+ }
18
+ /**
19
+ * Registers a handler (the newest wins) and returns its idempotent
20
+ * unsubscribe. Registrations are tracked by identity, so registering the
21
+ * same handler twice and unsubscribing once leaves one in place.
22
+ */
23
+ add(handler) {
24
+ const registration = { handler };
25
+ this.registrations.push(registration);
26
+ return () => {
27
+ this.registrations = this.registrations.filter((entry) => entry !== registration);
28
+ };
29
+ }
30
+ /** Rethrows an error latched by an unhandled or failing dialog handler. */
31
+ throwPending() {
32
+ this.latch.throwPending();
33
+ }
34
+ /** Routes one native dialog to the newest registered handler. */
35
+ async dispatch(dialog) {
36
+ const handler = this.registrations.at(-1)?.handler;
37
+ if (handler === undefined) {
38
+ this.latch.latch(new EngineError('INVALID_STATE', `unhandled ${dialog.type()} dialog: ${dialog.message()}`, { retryable: false }));
39
+ await dialog.dismiss().catch(() => undefined);
40
+ return;
41
+ }
42
+ let decided = false;
43
+ const publicDialog = {
44
+ message: dialog.message(),
45
+ accept: async (text) => {
46
+ decided = true;
47
+ await dialog.accept(text);
48
+ },
49
+ dismiss: async () => {
50
+ decided = true;
51
+ await dialog.dismiss();
52
+ },
53
+ };
54
+ try {
55
+ if (handler === 'accept')
56
+ await dialog.accept();
57
+ else if (handler === 'dismiss')
58
+ await dialog.dismiss();
59
+ else {
60
+ await handler(publicDialog);
61
+ // A dialog left open blocks the page, and the failure would surface
62
+ // later as an unrelated action timeout. The handler contract is the
63
+ // same as a route handler's: decide, or the step fails naming you.
64
+ if (!decided) {
65
+ await dialog.dismiss().catch(() => undefined);
66
+ this.latch.latch(new EngineError('INVALID_STATE', `dialog handler returned without calling accept or dismiss for ${dialog.type()} dialog: ${dialog.message()}`, { retryable: false }));
67
+ }
68
+ }
69
+ }
70
+ catch (cause) {
71
+ this.latch.latch(new EngineError('ENGINE_FAILURE', `dialog handler failed: ${message(cause)}`, {
72
+ retryable: false,
73
+ cause,
74
+ }));
75
+ }
76
+ }
77
+ }
78
+ //# sourceMappingURL=dialogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The Playwright engine for e2e: a browser body built with the
3
+ * public `defineEngine`, validated by the same rules and graded by the same
4
+ * capabilities as any other engine. Core imports nothing from here; this
5
+ * package imports the contract from `e2e/engine` and contributes the `web`
6
+ * fixture the way a device engine contributes `device`.
7
+ */
8
+ import type { BrowserContext, Page } from 'playwright';
9
+ import { type EngineHandle } from 'e2e/engine';
10
+ import { type WebOptions } from './surface.ts';
11
+ /**
12
+ * The live browser objects behind a `web()` handle, for agent-side code
13
+ * that replaces the toolset wholesale (spec chapter 16) and drives the page with
14
+ * its own Playwright tooling. Both accessors read the current attempt: the
15
+ * context exists from `startAttempt`, the page from the first `app.open()` or
16
+ * `web.goto()`, and either throws `INVALID_STATE` before that. The harness
17
+ * remains the notary for what it witnesses; a caller here acts out of band.
18
+ */
19
+ export interface PlaywrightLiveSurface {
20
+ readonly page: () => Page;
21
+ readonly context: () => BrowserContext;
22
+ }
23
+ /** The live surface of a handle this module created, or undefined for any other engine. */
24
+ export declare function surfaceOf(engine: EngineHandle): PlaywrightLiveSurface | undefined;
25
+ export declare function web(options?: WebOptions): EngineHandle;
26
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAMlE;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,cAAc,CAAC;CACxC;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,qBAAqB,GAAG,SAAS,CAIjF;AAED,wBAAgB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,YAAY,CA6E1D"}
package/dist/engine.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The Playwright engine for e2e: a browser body built with the
3
+ * public `defineEngine`, validated by the same rules and graded by the same
4
+ * capabilities as any other engine. Core imports nothing from here; this
5
+ * package imports the contract from `e2e/engine` and contributes the `web`
6
+ * fixture the way a device engine contributes `device`.
7
+ */
8
+ import { ConfigurationError, defineEngine, LOCATOR_ACTION_KINDS, POINTER_ACTION_KINDS, obj, } from 'e2e/engine';
9
+ import { createRequire } from 'node:module';
10
+ import { PlaywrightSurface } from './surface.js';
11
+ import { createWebFixture } from './web.js';
12
+ /** Creates one Playwright engine: one browser per worker, one context per attempt. */
13
+ const surfaces = new WeakMap();
14
+ /** The live surface of a handle this module created, or undefined for any other engine. */
15
+ export function surfaceOf(engine) {
16
+ const surface = surfaces.get(engine);
17
+ if (surface === undefined)
18
+ return undefined;
19
+ return { page: () => surface.requirePage(), context: () => surface.requireContext() };
20
+ }
21
+ export function web(options = {}) {
22
+ if (options.connect !== undefined && options.browser !== undefined && options.browser !== 'chromium') {
23
+ throw new ConfigurationError('INVALID_CONFIG', `web({ connect }) requires the chromium browser; CDP attach is chromium-only, got "${options.browser}"`);
24
+ }
25
+ if ('allowedOrigins' in options) {
26
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ allowedOrigins }) is gone: navigation and secret fills are not gated by origin; remove the option');
27
+ }
28
+ if (options.headers !== undefined)
29
+ validateHeaders(options.headers);
30
+ if (options.basicAuth !== undefined)
31
+ validateBasicAuth(options.basicAuth);
32
+ if (options.testIdAttribute !== undefined)
33
+ validateTestIdAttribute(options.testIdAttribute);
34
+ const recoverable = options.connect?.reconnectEndpoint !== undefined;
35
+ if (recoverable && typeof options.connect?.reconnectEndpoint !== 'function') {
36
+ throw new ConfigurationError('INVALID_CONFIG', 'connect.reconnectEndpoint must be a function');
37
+ }
38
+ if (recoverable && (options.headers !== undefined || options.basicAuth !== undefined)) {
39
+ throw new ConfigurationError('INVALID_CONFIG', 'connect.reconnectEndpoint uses a persistent context; headers and basicAuth require a newly created context');
40
+ }
41
+ const surface = new PlaywrightSurface(options);
42
+ const handle = defineEngine({
43
+ name: 'web',
44
+ version: ownVersion(),
45
+ spiVersion: 1,
46
+ platform: 'web',
47
+ prepare: (info) => surface.prepare(info),
48
+ init: (info) => surface.init(info),
49
+ startAttempt: (context) => surface.startAttempt(context),
50
+ endAttempt: (context) => surface.endAttempt(context),
51
+ dispose: (context) => surface.dispose(context),
52
+ observe: (operation, observeOptions) => surface.observe(operation, observeOptions),
53
+ locate: (expression, operation) => surface.locate(expression, operation),
54
+ perform: (ref, action, operation) => surface.perform(ref, action, operation),
55
+ // A browser honors every action kind of the contract; `actions.ts` dispatches each.
56
+ actions: LOCATOR_ACTION_KINDS,
57
+ // A page takes every pointer action of the contract at a bare point too.
58
+ performAt: (point, action, operation) => surface.performAt(point, action, operation),
59
+ pointerActions: POINTER_ACTION_KINDS,
60
+ keyboard: {
61
+ type: (text, keyboardOptions, operation) => surface.typeText(text, keyboardOptions, operation),
62
+ press: (key, operation) => surface.pressKey(key, operation),
63
+ },
64
+ app: declaredApp(options),
65
+ session: {
66
+ open: (url, operation) => surface.open(url, operation),
67
+ back: (operation) => surface.back(operation),
68
+ restart: (operation) => surface.restart(operation),
69
+ ...(recoverable ? {} : { reset: (operation) => surface.reset(operation) }),
70
+ },
71
+ artifacts: {
72
+ screenshot: (label, operation) => surface.screenshot(label, operation),
73
+ startTrace: (operation) => surface.startTrace(operation),
74
+ stopTrace: (operation) => surface.stopTrace(operation),
75
+ startVideo: (operation) => surface.startVideo(operation),
76
+ stopVideo: (operation) => surface.stopVideo(operation),
77
+ },
78
+ ...(recoverable
79
+ ? {}
80
+ : {
81
+ state: {
82
+ capture: (operation) => surface.captureState(operation),
83
+ restore: (state, operation) => surface.restoreState(state, operation),
84
+ },
85
+ }),
86
+ fixtures: {
87
+ web: (context) => createWebFixture(surface, context),
88
+ },
89
+ });
90
+ surfaces.set(handle, surface);
91
+ return handle;
92
+ }
93
+ /** An HTTP header field name: one or more `token` characters (RFC 9110). */
94
+ const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
95
+ /** A control character no HTTP field value may carry; a horizontal tab is the one the grammar allows. */
96
+ // oxlint-disable-next-line no-control-regex -- the control characters are the point
97
+ const FIELD_VALUE_CONTROL = /[\u0000-\u0008\u000A-\u001F\u007F]/;
98
+ /** True for a plain object; the shape both options take. Config runs as JavaScript, so the types alone are no guard. */
99
+ function isRecord(value) {
100
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
101
+ }
102
+ /**
103
+ * Refuses a header the browser could not send, at config load rather than at
104
+ * the first request: a name outside the token grammar, a value carrying a
105
+ * control character (a line break is a header-injection vector), or a value
106
+ * that is not a string at all.
107
+ */
108
+ function validateHeaders(headers) {
109
+ if (!isRecord(headers)) {
110
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ headers }) must be an object of header name to value');
111
+ }
112
+ for (const [name, value] of Object.entries(headers)) {
113
+ if (!HEADER_NAME.test(name)) {
114
+ throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) has an invalid header name: "${name}"`);
115
+ }
116
+ if (typeof value !== 'string') {
117
+ throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) header "${name}" must be a string, got ${typeof value}`);
118
+ }
119
+ if (FIELD_VALUE_CONTROL.test(value)) {
120
+ throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) header "${name}" must not contain a control character`);
121
+ }
122
+ }
123
+ }
124
+ /** Refuses credentials the browser could not present: a missing field, or a `:` in the user name (RFC 7617). */
125
+ function validateBasicAuth(basicAuth) {
126
+ if (!isRecord(basicAuth)) {
127
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ basicAuth }) must be an object with username and password');
128
+ }
129
+ const { username, password } = basicAuth;
130
+ if (typeof username !== 'string' || username === '') {
131
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ basicAuth }) requires a non-empty username string');
132
+ }
133
+ if (username.includes(':')) {
134
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ basicAuth }) username must not contain ":"');
135
+ }
136
+ if (typeof password !== 'string') {
137
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ basicAuth }) requires a password string');
138
+ }
139
+ }
140
+ /**
141
+ * An attribute name the document grammar accepts (XML `Name`, ASCII): a value
142
+ * outside it could never be on an element, so `getByTestId` would match nothing
143
+ * and every `testId` query would fail silently.
144
+ */
145
+ const ATTRIBUTE_NAME = /^[A-Za-z_:][A-Za-z0-9_:.-]*$/;
146
+ /** Refuses a test-id attribute no element could carry, at config load. */
147
+ function validateTestIdAttribute(attribute) {
148
+ if (typeof attribute !== 'string' || !ATTRIBUTE_NAME.test(attribute)) {
149
+ throw new ConfigurationError('INVALID_CONFIG', `web({ testIdAttribute }) must be an attribute name such as "data-testid", got ${JSON.stringify(attribute)}`);
150
+ }
151
+ }
152
+ /** The app-declaration half of the options, so browser knobs never reach the manifest. */
153
+ function declaredApp(options) {
154
+ const { url, environment, identity, command, readyUrl, services } = options;
155
+ return obj({ url, environment, identity, command, readyUrl, services });
156
+ }
157
+ /** This package's published version, read through require resolution. */
158
+ function ownVersion() {
159
+ try {
160
+ return createRequire(import.meta.url)('../package.json').version;
161
+ }
162
+ catch {
163
+ return 'unknown';
164
+ }
165
+ }
166
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ type EvaluationResult = {
2
+ ok: true;
3
+ value: unknown;
4
+ } | {
5
+ ok: false;
6
+ message: string;
7
+ };
8
+ /** Compiles the page-side error boundary without evaluating the caller's source in this process. */
9
+ export declare function compileEvaluation(source: string, hasArgument: boolean): (arg: unknown) => Promise<EvaluationResult>;
10
+ export {};
11
+ //# sourceMappingURL=evaluation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluation.d.ts","sourceRoot":"","sources":["../src/evaluation.ts"],"names":[],"mappings":"AAIA,KAAK,gBAAgB,GACjB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAoBnH"}
@@ -0,0 +1,27 @@
1
+ /** Separates exceptions thrown by trusted page code from failures of the evaluation transport. */
2
+ import { TestError } from 'e2e/engine';
3
+ import { message } from './support.js';
4
+ /** Compiles the page-side error boundary without evaluating the caller's source in this process. */
5
+ export function compileEvaluation(source, hasArgument) {
6
+ try {
7
+ return new Function('arg', `
8
+ return (async () => {
9
+ try {
10
+ return { ok: true, value: await (${source}\n)(${hasArgument ? 'arg' : ''}) };
11
+ } catch (cause) {
12
+ let message;
13
+ try {
14
+ message = typeof cause?.message === 'string' ? cause.message : String(cause);
15
+ } catch {
16
+ message = 'Page evaluation threw an unprintable value';
17
+ }
18
+ return { ok: false, message };
19
+ }
20
+ })();
21
+ `);
22
+ }
23
+ catch (cause) {
24
+ throw new TestError('EVALUATE_FAILED', message(cause), { cause });
25
+ }
26
+ }
27
+ //# sourceMappingURL=evaluation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluation.js","sourceRoot":"","sources":["../src/evaluation.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAMvC,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,WAAoB;IACpE,IAAI,CAAC;QACH,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE;;;6CAGc,MAAM,OAAO,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;KAW7E,CAAgD,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `@e2edev/web` public surface: the `web()` engine factory, the
3
+ * `web` fixture types, and a `test` typed with that fixture. Everything else a
4
+ * test needs (`expect`, `credentials`) comes from `e2e` itself: this package
5
+ * contributes a surface, it does not re-export the test API.
6
+ */
7
+ import type { Web } from './web.ts';
8
+ export { web, surfaceOf } from './engine.ts';
9
+ export type { PlaywrightLiveSurface } from './engine.ts';
10
+ export type { WebBasicAuth, WebConnectOptions, WebOptions } from './surface.ts';
11
+ export type { BrowserName } from './browser-connection.ts';
12
+ export type { Dialog, DialogHandler } from './dialogs.ts';
13
+ export type { Cookie, CookieFields, FrameScreen, RouteFulfillResponse, Web, WebExpectation, WebResponse, WebRoute, } from './web.ts';
14
+ /**
15
+ * `test` typed with this engine's contributed `web` fixture. The same
16
+ * runtime `test` as `e2e`'s; only the fixture types differ.
17
+ */
18
+ export declare const test: import("e2e").TestAPI<import("e2e").TestFixtures & {
19
+ web: Web;
20
+ }>;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EACV,MAAM,EACN,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,GAAG,EACH,cAAc,EACd,WAAW,EACX,QAAQ,GACT,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,eAAO,MAAM,IAAI;SAAsB,GAAG;EAAK,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `@e2edev/web` public surface: the `web()` engine factory, the
3
+ * `web` fixture types, and a `test` typed with that fixture. Everything else a
4
+ * test needs (`expect`, `credentials`) comes from `e2e` itself: this package
5
+ * contributes a surface, it does not re-export the test API.
6
+ */
7
+ import { test as base } from 'e2e';
8
+ export { web, surfaceOf } from './engine.js';
9
+ /**
10
+ * `test` typed with this engine's contributed `web` fixture. The same
11
+ * runtime `test` as `e2e`'s; only the fixture types differ.
12
+ */
13
+ export const test = base.extend();
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AAGnC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAgB7C;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAgB,CAAC"}