@specific.dev/spectest 0.68.0 → 0.69.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.
package/dist/browser.js CHANGED
@@ -1406,7 +1406,7 @@ function buildBackend(holder, recorder, buildOpts) {
1406
1406
  }, { wrap: true });
1407
1407
  },
1408
1408
  async waitForFunction(description, fn, arg, options = {}) {
1409
- const timeoutMs = options.timeout ?? 5_000;
1409
+ const timeoutMs = options.timeout ?? DEFAULT_ACTION_TIMEOUT_MS;
1410
1410
  const intervalMs = options.polling ?? 100;
1411
1411
  const src = typeof fn === "string" ? fn : fn.toString();
1412
1412
  const t = truncateUtf8(src);
package/dist/index.js CHANGED
@@ -713,7 +713,7 @@ function buildLocatorMatchers(loc, negated, message) {
713
713
  // The last error a probe threw, kept for the failure message: a matcher
714
714
  // whose read waits for the element (textContent, inputValue, isEnabled …)
715
715
  // reports a missing element as a playwright timeout, and that — not
716
- // "got <error: Timeout 5000ms exceeded …>" — is the sentence to fail with.
716
+ // "got <error: Timeout 10000ms exceeded …>" — is the sentence to fail with.
717
717
  let probeError;
718
718
  for (;;) {
719
719
  let satisfied;
@@ -753,8 +753,8 @@ function buildLocatorMatchers(loc, negated, message) {
753
753
  }
754
754
  if (Date.now() >= deadline) {
755
755
  // The deadline, not the stopwatch: every one of these polled until it
756
- // ran out, and "(waited 5s)" is both what the author set and the same
757
- // number twice in a row — a measured 4_987ms is neither.
756
+ // ran out, and "(waited 10s)" is both what the author set and the same
757
+ // number twice in a row — a measured 9_987ms is neither.
758
758
  const msg = (await elementFailure(probeError)) ??
759
759
  `${describe(actual)} (waited ${formatWaited(budget)})`;
760
760
  const sourceSeq = await probe.settle(matcher, Date.now() - started, msg, settleOpts);
@@ -1,7 +1,7 @@
1
1
  // Readable failures for locator steps.
2
2
  //
3
3
  // Playwright reports every unmet actionability wait the same way: a
4
- // `TimeoutError` whose message is "Timeout 5000ms exceeded." with the real
4
+ // `TimeoutError` whose message is "Timeout 10000ms exceeded." with the real
5
5
  // story — did the element exist at all? was it disabled? did something cover
6
6
  // it? — buried in a call log below it. A test that clicks a button that is not
7
7
  // on the page therefore fails with a sentence about OUR deadline, which reads
package/dist/locator.d.ts CHANGED
@@ -3,11 +3,16 @@ import type { RecordableFields } from "./browser.js";
3
3
  import type { Wrapped } from "./inspect.js";
4
4
  import { type HintTarget } from "./locator-hints.js";
5
5
  /** Default deadline for a locator action/read's target to become actionable.
6
- * Playwright's own default is 30s — far too slow-failing for tests; 5s
7
- * matches the pre-Playwright behavior. A per-call `{ timeout }` overrides it;
8
- * `undefined` falls through to the context default (also set to this in
9
- * browser.ts's `newViewContext`). Navigations keep a longer deadline. */
10
- export declare const DEFAULT_ACTION_TIMEOUT_MS = 5000;
6
+ * Playwright's own default is 30s — far too slow-failing for tests. This was
7
+ * 5s (the pre-Playwright behavior) until 2026-09-03, and 5s turned out to be
8
+ * about one page load: a real app under a full VM fan-out took 1.8-2.0s for a
9
+ * full navigation plus its client data chain on a quiet box, and 4x that on
10
+ * the tail, so a step that was correct and merely late failed. 10s still
11
+ * fails fast enough to be useful and leaves room for the tail. A per-call
12
+ * `{ timeout }` overrides it; `undefined` falls through to the context
13
+ * default (also set to this in browser.ts's `newViewContext`). Navigations
14
+ * keep a longer deadline. */
15
+ export declare const DEFAULT_ACTION_TIMEOUT_MS = 10000;
11
16
  export interface GetByTextOptions {
12
17
  /** Whole-string, case-sensitive match instead of the default
13
18
  * case-insensitive substring. Ignored when the query is a RegExp. */
package/dist/locator.js CHANGED
@@ -27,11 +27,16 @@ import { formatHints, nearMissHints } from "./locator-hints.js";
27
27
  import { resolveExistingProjectPath } from "./project-files.js";
28
28
  import { truncateUtf8 } from "./recorder.js";
29
29
  /** Default deadline for a locator action/read's target to become actionable.
30
- * Playwright's own default is 30s — far too slow-failing for tests; 5s
31
- * matches the pre-Playwright behavior. A per-call `{ timeout }` overrides it;
32
- * `undefined` falls through to the context default (also set to this in
33
- * browser.ts's `newViewContext`). Navigations keep a longer deadline. */
34
- export const DEFAULT_ACTION_TIMEOUT_MS = 5_000;
30
+ * Playwright's own default is 30s — far too slow-failing for tests. This was
31
+ * 5s (the pre-Playwright behavior) until 2026-09-03, and 5s turned out to be
32
+ * about one page load: a real app under a full VM fan-out took 1.8-2.0s for a
33
+ * full navigation plus its client data chain on a quiet box, and 4x that on
34
+ * the tail, so a step that was correct and merely late failed. 10s still
35
+ * fails fast enough to be useful and leaves room for the tail. A per-call
36
+ * `{ timeout }` overrides it; `undefined` falls through to the context
37
+ * default (also set to this in browser.ts's `newViewContext`). Navigations
38
+ * keep a longer deadline. */
39
+ export const DEFAULT_ACTION_TIMEOUT_MS = 10_000;
35
40
  // Brand + chain carrier. Both are `Symbol.for` keys so `JSON.stringify` drops
36
41
  // them (locators are never serialized) while runtime code can still detect a
37
42
  // locator and read a nested one's chain during lowering.
@@ -401,7 +406,7 @@ export function makeLocator(backend, strategy, chain) {
401
406
  return formatHints(await nearMissHints(page, target));
402
407
  };
403
408
  // Every terminal op runs through this: playwright's actionability timeouts
404
- // say "Timeout 5000ms exceeded" and hide what actually went wrong in a call
409
+ // say "Timeout 10000ms exceeded" and hide what actually went wrong in a call
405
410
  // log, so they are rewritten into a sentence naming the element and its
406
411
  // state (see locator-errors.ts), and a failure that found NO element asks
407
412
  // the page what it does hold. Applied INSIDE `pageOp`, so the recorded step
package/dist/terminal.js CHANGED
@@ -28,6 +28,9 @@
28
28
  import { Terminal as XtermHeadless } from "@xterm/headless";
29
29
  import { recordTerminalStep, reserveEvent, truncateUtf8 } from "./recorder.js";
30
30
  import { wrap } from "./inspect.js";
31
+ // One default deadline for "wait until the app shows it", whichever surface
32
+ // the author is watching — see the constant for why it is what it is.
33
+ import { DEFAULT_ACTION_TIMEOUT_MS } from "./locator.js";
31
34
  // Cap on cumulative output we keep in memory. The asciicast frames
32
35
  // grow separately (they live on the session record) and aren't bounded
33
36
  // here. Matches `TERMINAL_OUTPUT_CAP_BYTES` in daemon.ts.
@@ -421,7 +424,7 @@ export async function openTerminal(args) {
421
424
  return renderScreen();
422
425
  },
423
426
  async waitFor(description, matcher, waitOpts) {
424
- const timeoutMs = waitOpts?.timeoutMs ?? 5_000;
427
+ const timeoutMs = waitOpts?.timeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;
425
428
  const intervalMs = waitOpts?.intervalMs ?? 100;
426
429
  const t = Date.now();
427
430
  const resv = reserveEvent();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/spectest",
3
- "version": "0.68.0",
3
+ "version": "0.69.0",
4
4
  "description": "Spectest SDK for defining test environments in TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/browser.ts CHANGED
@@ -1943,7 +1943,7 @@ function buildBackend(
1943
1943
  arg?: unknown,
1944
1944
  options: { timeout?: number; polling?: number } = {},
1945
1945
  ): Promise<Wrapped<T>> {
1946
- const timeoutMs = options.timeout ?? 5_000;
1946
+ const timeoutMs = options.timeout ?? DEFAULT_ACTION_TIMEOUT_MS;
1947
1947
  const intervalMs = options.polling ?? 100;
1948
1948
  const src = typeof fn === "string" ? fn : fn.toString();
1949
1949
  const t = truncateUtf8(src);
package/src/index.ts CHANGED
@@ -2900,7 +2900,7 @@ function buildLocatorMatchers(
2900
2900
  // The last error a probe threw, kept for the failure message: a matcher
2901
2901
  // whose read waits for the element (textContent, inputValue, isEnabled …)
2902
2902
  // reports a missing element as a playwright timeout, and that — not
2903
- // "got <error: Timeout 5000ms exceeded …>" — is the sentence to fail with.
2903
+ // "got <error: Timeout 10000ms exceeded …>" — is the sentence to fail with.
2904
2904
  let probeError: unknown;
2905
2905
  for (;;) {
2906
2906
  let satisfied: boolean;
@@ -2939,8 +2939,8 @@ function buildLocatorMatchers(
2939
2939
  }
2940
2940
  if (Date.now() >= deadline) {
2941
2941
  // The deadline, not the stopwatch: every one of these polled until it
2942
- // ran out, and "(waited 5s)" is both what the author set and the same
2943
- // number twice in a row — a measured 4_987ms is neither.
2942
+ // ran out, and "(waited 10s)" is both what the author set and the same
2943
+ // number twice in a row — a measured 9_987ms is neither.
2944
2944
  const msg = (await elementFailure(probeError)) ??
2945
2945
  `${describe(actual)} (waited ${formatWaited(budget)})`;
2946
2946
  const sourceSeq = await probe.settle(matcher, Date.now() - started, msg, settleOpts);
@@ -1,7 +1,7 @@
1
1
  // Readable failures for locator steps.
2
2
  //
3
3
  // Playwright reports every unmet actionability wait the same way: a
4
- // `TimeoutError` whose message is "Timeout 5000ms exceeded." with the real
4
+ // `TimeoutError` whose message is "Timeout 10000ms exceeded." with the real
5
5
  // story — did the element exist at all? was it disabled? did something cover
6
6
  // it? — buried in a call log below it. A test that clicks a button that is not
7
7
  // on the page therefore fails with a sentence about OUR deadline, which reads
package/src/locator.ts CHANGED
@@ -32,11 +32,16 @@ import { resolveExistingProjectPath } from "./project-files.js";
32
32
  import { truncateUtf8 } from "./recorder.js";
33
33
 
34
34
  /** Default deadline for a locator action/read's target to become actionable.
35
- * Playwright's own default is 30s — far too slow-failing for tests; 5s
36
- * matches the pre-Playwright behavior. A per-call `{ timeout }` overrides it;
37
- * `undefined` falls through to the context default (also set to this in
38
- * browser.ts's `newViewContext`). Navigations keep a longer deadline. */
39
- export const DEFAULT_ACTION_TIMEOUT_MS = 5_000;
35
+ * Playwright's own default is 30s — far too slow-failing for tests. This was
36
+ * 5s (the pre-Playwright behavior) until 2026-09-03, and 5s turned out to be
37
+ * about one page load: a real app under a full VM fan-out took 1.8-2.0s for a
38
+ * full navigation plus its client data chain on a quiet box, and 4x that on
39
+ * the tail, so a step that was correct and merely late failed. 10s still
40
+ * fails fast enough to be useful and leaves room for the tail. A per-call
41
+ * `{ timeout }` overrides it; `undefined` falls through to the context
42
+ * default (also set to this in browser.ts's `newViewContext`). Navigations
43
+ * keep a longer deadline. */
44
+ export const DEFAULT_ACTION_TIMEOUT_MS = 10_000;
40
45
 
41
46
  // Brand + chain carrier. Both are `Symbol.for` keys so `JSON.stringify` drops
42
47
  // them (locators are never serialized) while runtime code can still detect a
@@ -656,7 +661,7 @@ export interface Locator {
656
661
  /** Explicit strict-mode opt-out: the i-th resolved element (0-based). */
657
662
  nth(index: number): Locator;
658
663
 
659
- // ── Actions (auto-wait; default 5s, `{ timeout }` overrides) ──────────
664
+ // ── Actions (auto-wait; default 10s, `{ timeout }` overrides) ─────────
660
665
  click(opts?: ClickOptions): Promise<void>;
661
666
  dblclick(opts?: ClickOptions): Promise<void>;
662
667
  /** Touch-tap (mobile sessions only; throws on desktop). `duration`
@@ -761,7 +766,7 @@ export function makeLocator(
761
766
  };
762
767
 
763
768
  // Every terminal op runs through this: playwright's actionability timeouts
764
- // say "Timeout 5000ms exceeded" and hide what actually went wrong in a call
769
+ // say "Timeout 10000ms exceeded" and hide what actually went wrong in a call
765
770
  // log, so they are rewritten into a sentence naming the element and its
766
771
  // state (see locator-errors.ts), and a failure that found NO element asks
767
772
  // the page what it does hold. Applied INSIDE `pageOp`, so the recorded step
package/src/terminal.ts CHANGED
@@ -32,6 +32,9 @@ import { recordTerminalStep, reserveEvent, truncateUtf8 } from "./recorder.js";
32
32
  import type { EventReservation } from "./recorder.js";
33
33
  import type { TerminalStepAction } from "./recorder.js";
34
34
  import { wrap } from "./inspect.js";
35
+ // One default deadline for "wait until the app shows it", whichever surface
36
+ // the author is watching — see the constant for why it is what it is.
37
+ import { DEFAULT_ACTION_TIMEOUT_MS } from "./locator.js";
35
38
  import type { Wrapped } from "./inspect.js";
36
39
 
37
40
  export interface TerminalOpts {
@@ -614,7 +617,7 @@ export async function openTerminal(args: OpenTerminalArgs): Promise<InternalTerm
614
617
  matcher: string | RegExp | ((screen: string, output: string) => unknown),
615
618
  waitOpts?: { timeoutMs?: number; intervalMs?: number },
616
619
  ) {
617
- const timeoutMs = waitOpts?.timeoutMs ?? 5_000;
620
+ const timeoutMs = waitOpts?.timeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;
618
621
  const intervalMs = waitOpts?.intervalMs ?? 100;
619
622
  const t = Date.now();
620
623
  const resv = reserveEvent();