@e2e-dev/web 0.11.1 → 0.12.0

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 (139) hide show
  1. package/README.md +15 -9
  2. package/dist/.build.json +1 -1
  3. package/dist/actions.d.ts +1 -2
  4. package/dist/actions.js +54 -6
  5. package/dist/attempt-session.d.ts +1 -2
  6. package/dist/attempt-session.js +0 -1
  7. package/dist/bin.d.ts +2 -0
  8. package/dist/bin.js +3 -0
  9. package/dist/browser-connection.d.ts +7 -4
  10. package/dist/browser-connection.js +7 -2
  11. package/dist/browser.d.ts +43 -7
  12. package/dist/browser.js +93 -36
  13. package/dist/capture-scope.d.ts +1 -2
  14. package/dist/capture-scope.js +0 -1
  15. package/dist/cdp-recovery.d.ts +1 -2
  16. package/dist/cdp-recovery.js +0 -1
  17. package/dist/cdp-selectors.d.ts +1 -2
  18. package/dist/cdp-selectors.js +0 -1
  19. package/dist/cli.d.ts +9 -0
  20. package/dist/cli.js +42 -0
  21. package/dist/closed-shadow.d.ts +0 -1
  22. package/dist/closed-shadow.js +0 -1
  23. package/dist/dialogs.d.ts +1 -2
  24. package/dist/dialogs.js +0 -1
  25. package/dist/downloads.d.ts +1 -2
  26. package/dist/downloads.js +0 -1
  27. package/dist/engine.d.ts +1 -2
  28. package/dist/engine.js +74 -17
  29. package/dist/evaluation.d.ts +2 -1
  30. package/dist/evaluation.js +3 -2
  31. package/dist/in-page/read-semantics.d.ts +4 -6
  32. package/dist/in-page/read-semantics.js +184 -54
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.js +0 -1
  35. package/dist/init-scripts.d.ts +30 -0
  36. package/dist/init-scripts.js +105 -0
  37. package/dist/install.d.ts +8 -2
  38. package/dist/install.js +35 -9
  39. package/dist/label-selector.d.ts +0 -1
  40. package/dist/label-selector.js +0 -1
  41. package/dist/locators.d.ts +1 -2
  42. package/dist/locators.js +30 -19
  43. package/dist/observation-capture.d.ts +1 -2
  44. package/dist/observation-capture.js +0 -1
  45. package/dist/observation.d.ts +1 -2
  46. package/dist/observation.js +3 -4
  47. package/dist/observe.d.ts +1 -2
  48. package/dist/observe.js +0 -1
  49. package/dist/operation-budget.d.ts +0 -1
  50. package/dist/operation-budget.js +0 -1
  51. package/dist/protected-app.d.ts +20 -5
  52. package/dist/protected-app.js +36 -6
  53. package/dist/provider-video.d.ts +1 -2
  54. package/dist/provider-video.js +0 -1
  55. package/dist/provider.d.ts +0 -1
  56. package/dist/provider.js +0 -1
  57. package/dist/read-node.d.ts +16 -44
  58. package/dist/read-node.js +7 -22
  59. package/dist/read-selector.d.ts +51 -0
  60. package/dist/read-selector.js +69 -0
  61. package/dist/refs.d.ts +0 -1
  62. package/dist/refs.js +0 -1
  63. package/dist/route-options.d.ts +44 -0
  64. package/dist/route-options.js +125 -0
  65. package/dist/route-pattern.d.ts +0 -1
  66. package/dist/route-pattern.js +0 -1
  67. package/dist/selector-engines.d.ts +0 -1
  68. package/dist/selector-engines.js +4 -1
  69. package/dist/shown-selector.d.ts +20 -0
  70. package/dist/shown-selector.js +27 -0
  71. package/dist/support.d.ts +4 -3
  72. package/dist/support.js +1 -2
  73. package/dist/surface.d.ts +40 -4
  74. package/dist/surface.js +62 -28
  75. package/dist/video.d.ts +1 -2
  76. package/dist/video.js +0 -1
  77. package/package.json +9 -5
  78. package/dist/actions.d.ts.map +0 -1
  79. package/dist/actions.js.map +0 -1
  80. package/dist/attempt-session.d.ts.map +0 -1
  81. package/dist/attempt-session.js.map +0 -1
  82. package/dist/browser-connection.d.ts.map +0 -1
  83. package/dist/browser-connection.js.map +0 -1
  84. package/dist/browser.d.ts.map +0 -1
  85. package/dist/browser.js.map +0 -1
  86. package/dist/capture-scope.d.ts.map +0 -1
  87. package/dist/capture-scope.js.map +0 -1
  88. package/dist/cdp-recovery.d.ts.map +0 -1
  89. package/dist/cdp-recovery.js.map +0 -1
  90. package/dist/cdp-selectors.d.ts.map +0 -1
  91. package/dist/cdp-selectors.js.map +0 -1
  92. package/dist/closed-shadow.d.ts.map +0 -1
  93. package/dist/closed-shadow.js.map +0 -1
  94. package/dist/dialogs.d.ts.map +0 -1
  95. package/dist/dialogs.js.map +0 -1
  96. package/dist/downloads.d.ts.map +0 -1
  97. package/dist/downloads.js.map +0 -1
  98. package/dist/engine.d.ts.map +0 -1
  99. package/dist/engine.js.map +0 -1
  100. package/dist/evaluation.d.ts.map +0 -1
  101. package/dist/evaluation.js.map +0 -1
  102. package/dist/in-page/read-semantics.d.ts.map +0 -1
  103. package/dist/in-page/read-semantics.js.map +0 -1
  104. package/dist/index.d.ts.map +0 -1
  105. package/dist/index.js.map +0 -1
  106. package/dist/install.d.ts.map +0 -1
  107. package/dist/install.js.map +0 -1
  108. package/dist/label-selector.d.ts.map +0 -1
  109. package/dist/label-selector.js.map +0 -1
  110. package/dist/locators.d.ts.map +0 -1
  111. package/dist/locators.js.map +0 -1
  112. package/dist/observation-capture.d.ts.map +0 -1
  113. package/dist/observation-capture.js.map +0 -1
  114. package/dist/observation.d.ts.map +0 -1
  115. package/dist/observation.js.map +0 -1
  116. package/dist/observe.d.ts.map +0 -1
  117. package/dist/observe.js.map +0 -1
  118. package/dist/operation-budget.d.ts.map +0 -1
  119. package/dist/operation-budget.js.map +0 -1
  120. package/dist/protected-app.d.ts.map +0 -1
  121. package/dist/protected-app.js.map +0 -1
  122. package/dist/provider-video.d.ts.map +0 -1
  123. package/dist/provider-video.js.map +0 -1
  124. package/dist/provider.d.ts.map +0 -1
  125. package/dist/provider.js.map +0 -1
  126. package/dist/read-node.d.ts.map +0 -1
  127. package/dist/read-node.js.map +0 -1
  128. package/dist/refs.d.ts.map +0 -1
  129. package/dist/refs.js.map +0 -1
  130. package/dist/route-pattern.d.ts.map +0 -1
  131. package/dist/route-pattern.js.map +0 -1
  132. package/dist/selector-engines.d.ts.map +0 -1
  133. package/dist/selector-engines.js.map +0 -1
  134. package/dist/support.d.ts.map +0 -1
  135. package/dist/support.js.map +0 -1
  136. package/dist/surface.d.ts.map +0 -1
  137. package/dist/surface.js.map +0 -1
  138. package/dist/video.d.ts.map +0 -1
  139. package/dist/video.js.map +0 -1
package/dist/cli.js ADDED
@@ -0,0 +1,42 @@
1
+ /** The `e2e-web` command: browser installs with the Playwright version this engine pins. */
2
+ import { BROWSER_NAMES, isBrowserName } from './browser-connection.js';
3
+ import { runPlaywrightCli } from './install.js';
4
+ const BROWSER_ARGS = `[${BROWSER_NAMES.join('|')} ...] [--with-deps]`;
5
+ const USAGE = `Usage: npx @e2e-dev/web install ${BROWSER_ARGS}
6
+ bunx @e2e-dev/web install ${BROWSER_ARGS}
7
+ pnpm exec e2e-web install ${BROWSER_ARGS}
8
+
9
+ Installs browsers for the Playwright version @e2e-dev/web runs. Installs
10
+ chromium when no browser is named. --with-deps also installs the system
11
+ libraries the browsers need on Linux.
12
+ `;
13
+ const PROCESS_IO = {
14
+ stdout: (text) => process.stdout.write(text),
15
+ stderr: (text) => process.stderr.write(text),
16
+ run: runPlaywrightCli,
17
+ };
18
+ /** Runs `e2e-web` with the arguments after the command name and resolves to the exit code. */
19
+ export async function main(args, io = PROCESS_IO) {
20
+ const [command, ...rest] = args;
21
+ if (command === 'help' || args.some((arg) => arg === '--help' || arg === '-h')) {
22
+ io.stdout(USAGE);
23
+ return 0;
24
+ }
25
+ if (command !== 'install') {
26
+ io.stderr(`${command === undefined ? 'missing command' : `unknown command "${command}"`}\n\n${USAGE}`);
27
+ return 2;
28
+ }
29
+ const options = rest.filter((arg) => arg.startsWith('-'));
30
+ const names = rest.filter((arg) => !arg.startsWith('-'));
31
+ const unknownOption = options.find((option) => option !== '--with-deps');
32
+ if (unknownOption !== undefined) {
33
+ io.stderr(`unknown option "${unknownOption}"\n\n${USAGE}`);
34
+ return 2;
35
+ }
36
+ const unknownBrowser = names.find((name) => !isBrowserName(name));
37
+ if (unknownBrowser !== undefined) {
38
+ io.stderr(`unknown browser "${unknownBrowser}"; expected one of ${BROWSER_NAMES.join(', ')}\n`);
39
+ return 2;
40
+ }
41
+ return io.run(['install', ...options, ...(names.length > 0 ? names : ['chromium'])]);
42
+ }
@@ -61,4 +61,3 @@ export declare const CLOSED_SHADOW_SELECTOR_ENGINES: readonly {
61
61
  readonly name: string;
62
62
  readonly source: string;
63
63
  }[];
64
- //# sourceMappingURL=closed-shadow.d.ts.map
@@ -139,4 +139,3 @@ export const CLOSED_SHADOW_SELECTOR_ENGINES = [
139
139
  { name: CLOSED_SHADOW_SELECTOR_ENGINE, source: CLOSED_SHADOW_SELECTOR_ENGINE_SOURCE },
140
140
  { name: SEARCH_ROOTS_SELECTOR_ENGINE, source: SEARCH_ROOTS_SELECTOR_ENGINE_SOURCE },
141
141
  ];
142
- //# sourceMappingURL=closed-shadow.js.map
package/dist/dialogs.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * fixture; the surface owns the router so a dialog that fires before a test
4
4
  * ever touches `web` is still accounted for instead of silently dismissed.
5
5
  */
6
- import type { Dialog as PwDialog } from 'playwright';
6
+ import type { Dialog as PwDialog } from 'playwright-core';
7
7
  import { ErrorLatch } from './support.ts';
8
8
  /** A native dialog as a test's handler sees it. */
9
9
  export interface Dialog {
@@ -35,4 +35,3 @@ export declare class DialogRouter {
35
35
  dispatch(dialog: PwDialog): Promise<void>;
36
36
  private route;
37
37
  }
38
- //# sourceMappingURL=dialogs.d.ts.map
package/dist/dialogs.js CHANGED
@@ -84,4 +84,3 @@ export class DialogRouter {
84
84
  }
85
85
  }
86
86
  }
87
- //# sourceMappingURL=dialogs.js.map
@@ -5,7 +5,7 @@
5
5
  * that serves downloads names a directory on the browser's own disk; the
6
6
  * browser saves there, and the provider reads each finished file back.
7
7
  */
8
- import type { Download, Page } from 'playwright';
8
+ import type { Download, Page } from 'playwright-core';
9
9
  import type { LeaseDownloads } from './provider.ts';
10
10
  /**
11
11
  * Has the browser behind `page` save the downloads of the page's context
@@ -26,4 +26,3 @@ export declare function saveFromBrowser(download: Download, downloads: LeaseDown
26
26
  * save names it.
27
27
  */
28
28
  export declare function saveLocally(download: Download, file: string, unserved: string | undefined): Promise<void>;
29
- //# sourceMappingURL=downloads.d.ts.map
package/dist/downloads.js CHANGED
@@ -71,4 +71,3 @@ export async function saveLocally(download, file, unserved) {
71
71
  throw new EngineError('ENGINE_FAILURE', `download failed: ${message(cause)}; ${unserved}`, { retryable: false, cause });
72
72
  }
73
73
  }
74
- //# sourceMappingURL=downloads.js.map
package/dist/engine.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * package imports the contract from `e2e/engine` and contributes the `browser`
6
6
  * fixture the way a device engine contributes `device`.
7
7
  */
8
- import type { BrowserContext, Page } from 'playwright';
8
+ import type { BrowserContext, Page } from 'playwright-core';
9
9
  import { type EngineHandle } from 'e2e/engine';
10
10
  import { type WebOptions } from './surface.ts';
11
11
  /**
@@ -23,4 +23,3 @@ export interface PlaywrightLiveSurface {
23
23
  /** The live surface of a handle this module created, or undefined for any other engine. */
24
24
  export declare function surfaceOf(engine: EngineHandle): PlaywrightLiveSurface | undefined;
25
25
  export declare function web(options?: WebOptions): EngineHandle;
26
- //# sourceMappingURL=engine.d.ts.map
package/dist/engine.js CHANGED
@@ -10,6 +10,8 @@ import { createRequire } from 'node:module';
10
10
  import { asBrowserProvider } from './provider.js';
11
11
  import { PlaywrightSurface } from './surface.js';
12
12
  import { createBrowserFixture } from './browser.js';
13
+ import { validateInitScripts } from './init-scripts.js';
14
+ import { FIELD_VALUE_CONTROL, headerProblem } from './protected-app.js';
13
15
  /** App fields only a device target reads. */
14
16
  const NATIVE_APP_KEYS = ['bundleId', 'appPath', 'launchArguments', 'permissions'];
15
17
  /** Creates one Playwright engine: one browser per worker, one context per attempt. */
@@ -46,6 +48,12 @@ export function web(options = {}) {
46
48
  validateTestIdAttribute(options.testIdAttribute);
47
49
  if (options.userAgent !== undefined)
48
50
  validateUserAgent(options.userAgent, options.headers);
51
+ if (options.locale !== undefined)
52
+ validateLocale(options.locale, options.headers);
53
+ if (options.timezoneId !== undefined)
54
+ validateTimezoneId(options.timezoneId);
55
+ if (options.initScripts !== undefined)
56
+ validateInitScripts(options.initScripts);
49
57
  if (options.screencast !== undefined)
50
58
  validateScreencast(options.screencast);
51
59
  const reconnecting = options.connect?.reconnectEndpoint !== undefined;
@@ -55,8 +63,9 @@ export function web(options = {}) {
55
63
  // A per-attempt lease rides the same persistent context as `reconnectEndpoint`, with the same limits.
56
64
  const recoverable = reconnecting || provider?.scope === 'attempt';
57
65
  const mode = provider === undefined ? 'connect.reconnectEndpoint' : `browser provider "${provider.name}" with scope "attempt"`;
58
- if (recoverable && (options.headers !== undefined || options.basicAuth !== undefined || options.userAgent !== undefined)) {
59
- throw new ConfigurationError('INVALID_CONFIG', `${mode} uses a persistent context; headers, basicAuth, and userAgent require a newly created context`);
66
+ const creationOptions = CREATION_OPTION_KEYS.filter((key) => options[key] !== undefined);
67
+ if (recoverable && creationOptions.length > 0) {
68
+ throw new ConfigurationError('INVALID_CONFIG', `${mode} uses a persistent context; ${creationOptions.join(', ')} ${creationOptions.length === 1 ? 'requires' : 'require'} a newly created context`);
60
69
  }
61
70
  const surface = new PlaywrightSurface(options);
62
71
  const handle = defineEngine({
@@ -125,7 +134,12 @@ const WEB_OPTION_KEYS = Object.keys({
125
134
  basicAuth: true,
126
135
  testIdAttribute: true,
127
136
  userAgent: true,
137
+ locale: true,
138
+ timezoneId: true,
139
+ initScripts: true,
128
140
  });
141
+ /** The options applied when the engine creates a browser context, which a persistent context never is. */
142
+ const CREATION_OPTION_KEYS = ['headers', 'basicAuth', 'userAgent', 'locale', 'timezoneId'];
129
143
  /**
130
144
  * What a persistent context rules out, named with its cause: `what` replaces
131
145
  * the browser context, which `mode` never does. A per-attempt lease starts
@@ -135,11 +149,6 @@ function persistentContextLimit(what, mode, leased) {
135
149
  const remedy = leased ? '; every attempt already starts on a fresh browser, and scope "worker" can clear one mid-test' : '';
136
150
  return new ConfigurationError('UNSUPPORTED_CAPABILITY', `${what} is unavailable with ${mode}: it replaces the browser context, and the attempt rides one persistent context${remedy}`);
137
151
  }
138
- /** An HTTP header field name: one or more `token` characters (RFC 9110). */
139
- const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
140
- /** A control character no HTTP field value may carry; a horizontal tab is the one the grammar allows. */
141
- // oxlint-disable-next-line no-control-regex -- the control characters are the point
142
- const FIELD_VALUE_CONTROL = /[\u0000-\u0008\u000A-\u001F\u007F]/;
143
152
  /** True for a plain object; the shape both options take. Config runs as JavaScript, so the types alone are no guard. */
144
153
  function isRecord(value) {
145
154
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -155,15 +164,9 @@ function validateHeaders(headers) {
155
164
  throw new ConfigurationError('INVALID_CONFIG', 'web({ headers }) must be an object of header name to value');
156
165
  }
157
166
  for (const [name, value] of Object.entries(headers)) {
158
- if (!HEADER_NAME.test(name)) {
159
- throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) has an invalid header name: "${name}"`);
160
- }
161
- if (typeof value !== 'string') {
162
- throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) header "${name}" must be a string, got ${typeof value}`);
163
- }
164
- if (FIELD_VALUE_CONTROL.test(value)) {
165
- throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) header "${name}" must not contain a control character`);
166
- }
167
+ const problem = headerProblem(name, value);
168
+ if (problem !== undefined)
169
+ throw new ConfigurationError('INVALID_CONFIG', `web({ headers }) ${problem}`);
167
170
  }
168
171
  }
169
172
  /** Refuses credentials the browser could not present: a missing field, or a `:` in the user name (RFC 7617). */
@@ -199,6 +202,61 @@ function validateUserAgent(userAgent, headers) {
199
202
  throw new ConfigurationError('INVALID_CONFIG', 'web({ userAgent }) and a user-agent header in web({ headers }) conflict; set userAgent only');
200
203
  }
201
204
  }
205
+ /**
206
+ * Refuses a locale that is not a BCP 47 tag, and one an `accept-language`
207
+ * header would override on the app's site while `navigator.language` kept
208
+ * reporting it.
209
+ */
210
+ function validateLocale(locale, headers) {
211
+ if (typeof locale !== 'string' || locale === '' || !isLanguageTag(locale)) {
212
+ throw new ConfigurationError('INVALID_CONFIG', `web({ locale }) must be a BCP 47 language tag such as "de-DE", got ${JSON.stringify(locale)}`);
213
+ }
214
+ if (headers !== undefined && Object.keys(headers).some((name) => name.toLowerCase() === 'accept-language')) {
215
+ throw new ConfigurationError('INVALID_CONFIG', 'web({ locale }) and an accept-language header in web({ headers }) conflict; set locale only');
216
+ }
217
+ }
218
+ /**
219
+ * True when `tag` names a language the browser can run in: a Unicode locale
220
+ * `Intl.Locale` parses, other than the undetermined `und`, which Chromium
221
+ * refuses at context creation as it does a private-use-only tag.
222
+ */
223
+ function isLanguageTag(tag) {
224
+ try {
225
+ const { language } = new Intl.Locale(tag);
226
+ return language !== undefined && language !== 'und';
227
+ }
228
+ catch {
229
+ return false;
230
+ }
231
+ }
232
+ /**
233
+ * Refuses, at config load, a time zone the browser would otherwise refuse at
234
+ * the first context. Best effort: Node cannot tell a miscased alias
235
+ * (`us/eastern`) from the real one, so the browser's own `Invalid timezone ID`
236
+ * stays the final word.
237
+ */
238
+ function validateTimezoneId(timezoneId) {
239
+ if (typeof timezoneId !== 'string' || timezoneId === '' || !isTimeZone(timezoneId)) {
240
+ throw new ConfigurationError('INVALID_CONFIG', `web({ timezoneId }) must be an IANA time zone such as "Europe/Berlin", got ${JSON.stringify(timezoneId)}`);
241
+ }
242
+ }
243
+ /**
244
+ * True when `timeZone` is an IANA name `Intl` resolves, spelled with its own
245
+ * case. Node also takes an offset (`+01:00`) and any casing (`europe/berlin`),
246
+ * both of which Chromium refuses; an alias resolving to another name
247
+ * (`US/Eastern`) is fine.
248
+ */
249
+ function isTimeZone(timeZone) {
250
+ if (/^[+-]/.test(timeZone))
251
+ return false;
252
+ try {
253
+ const resolved = new Intl.DateTimeFormat('en-US', { timeZone }).resolvedOptions().timeZone;
254
+ return resolved === timeZone || resolved.toLowerCase() !== timeZone.toLowerCase();
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
202
260
  /**
203
261
  * An attribute name the document grammar accepts (XML `Name`, ASCII): a value
204
262
  * outside it could never be on an element, so `getByTestId` would match nothing
@@ -268,4 +326,3 @@ function ownVersion() {
268
326
  return 'unknown';
269
327
  }
270
328
  }
271
- //# sourceMappingURL=engine.js.map
@@ -5,6 +5,8 @@ type EvaluationResult = {
5
5
  ok: false;
6
6
  message: string;
7
7
  };
8
+ /** The `__name` helper tsx's `keepNames` output calls, declared beside test code serialized into the page. */
9
+ export declare const KEEP_NAMES_HELPER = "const __name = (target, value) => Object.defineProperty(target, 'name', { value, configurable: true });";
8
10
  /**
9
11
  * Compiles the page-side error boundary without evaluating the caller's source in this process.
10
12
  *
@@ -15,4 +17,3 @@ type EvaluationResult = {
15
17
  */
16
18
  export declare function compileEvaluation(source: string, hasArgument: boolean): (arg: unknown) => Promise<EvaluationResult>;
17
19
  export {};
18
- //# sourceMappingURL=evaluation.d.ts.map
@@ -1,6 +1,8 @@
1
1
  /** Separates exceptions thrown by trusted page code from failures of the evaluation transport. */
2
2
  import { TestError } from 'e2e/engine';
3
3
  import { message } from './support.js';
4
+ /** The `__name` helper tsx's `keepNames` output calls, declared beside test code serialized into the page. */
5
+ export const KEEP_NAMES_HELPER = "const __name = (target, value) => Object.defineProperty(target, 'name', { value, configurable: true });";
4
6
  /**
5
7
  * Compiles the page-side error boundary without evaluating the caller's source in this process.
6
8
  *
@@ -13,7 +15,7 @@ export function compileEvaluation(source, hasArgument) {
13
15
  try {
14
16
  return new Function('arg', `
15
17
  return (async () => {
16
- const __name = (target, value) => Object.defineProperty(target, 'name', { value, configurable: true });
18
+ ${KEEP_NAMES_HELPER}
17
19
  try {
18
20
  return { ok: true, value: await (${source}\n)(${hasArgument ? 'arg' : ''}) };
19
21
  } catch (cause) {
@@ -32,4 +34,3 @@ export function compileEvaluation(source, hasArgument) {
32
34
  throw new TestError('EVALUATE_FAILED', message(cause), { cause });
33
35
  }
34
36
  }
35
- //# sourceMappingURL=evaluation.js.map
@@ -10,11 +10,10 @@ import type { SemanticMode, SemanticResult } from '../read-node.ts';
10
10
  * Serialized into the page by Playwright. Must stay self-contained: no outer
11
11
  * captures beyond its two arguments.
12
12
  *
13
- * `mode.kind === 'node'` reads exactly one element for locator reads. An
14
- * element no longer in its document reads as detached with its value alone:
15
- * a handle taken a moment earlier can point at a node the page has since
16
- * replaced, and what such a node reports (no boxes, so hidden) describes
17
- * nothing on screen.
13
+ * `mode.kind === 'node'` reads exactly one element for locator reads, in
14
+ * the selector engine's task that found it (`read-selector.ts`,
15
+ * `label-selector.ts`), so the element is always in its document.
16
+ * `mode.kind === 'hidden'` answers that one element's `states.hidden` alone.
18
17
  * `mode.kind === 'tree'` walks the subtree for one agent observation and
19
18
  * returns live element handles aligned with the flattened node list. The two
20
19
  * modes also project nodes differently; those differences are data (see
@@ -25,4 +24,3 @@ export declare const readSemanticsFunction: <Mode extends SemanticMode>(element:
25
24
  secureFieldSelector: string;
26
25
  mode: Mode;
27
26
  }) => SemanticResult<Mode>;
28
- //# sourceMappingURL=read-semantics.d.ts.map