ccqa 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
@@ -4,9 +4,9 @@ declare function ab(...args: string[]): void;
4
4
  declare function abWait(selector: string, timeoutMs?: number): void;
5
5
  /** Assert stable text is visible on page (via wait --text). */
6
6
  declare function abAssertTextVisible(text: string, timeoutMs?: number): void;
7
- /** Assert element is visible (via wait). */
7
+ /** Assert element is visible (polls `get count`; never uses the blocking `wait <selector>`). */
8
8
  declare function abAssertVisible(selector: string, timeoutMs?: number): void;
9
- /** Assert element is NOT visible (via wait --state hidden or --fn for text). */
9
+ /** Assert element is NOT visible (polls `get count` for absence; --fn for text). */
10
10
  declare function abAssertNotVisible(selector: string, timeoutMs?: number): void;
11
11
  /** Assert URL contains a pattern (via get url). */
12
12
  declare function abAssertUrl(pattern: string): void;
@@ -1,4 +1,4 @@
1
- import { n as spawnAB, t as sleepSync } from "../spawn-ab-BxjEhA5e.mjs";
1
+ import { n as spawnAB, t as sleepSync } from "../spawn-ab-DjRh1-4T.mjs";
2
2
  //#region src/runtime/test-helpers.ts
3
3
  const POST_OPEN_SETTLE_MS = 600;
4
4
  function logStep(action, args) {
@@ -18,22 +18,50 @@ function ab(...args) {
18
18
  if (result.status !== 0) fail(`agent-browser ${command} failed (exit ${result.status})`, result);
19
19
  if (command === "open") sleepSync(POST_OPEN_SETTLE_MS);
20
20
  }
21
+ const SELECTOR_POLL_INTERVAL_MS = 500;
22
+ function selectorCount(selector) {
23
+ const r = spawnAB([
24
+ "get",
25
+ "count",
26
+ selector
27
+ ]);
28
+ if (r.status !== 0) return 0;
29
+ const n = Number.parseInt(r.stdout.trim(), 10);
30
+ return Number.isNaN(n) ? 0 : n;
31
+ }
32
+ /**
33
+ * Poll until `get count <selector>` reaches the desired presence state or the
34
+ * timeout elapses. `want: "present"` waits for >=1 match; `"absent"` waits for
35
+ * 0 matches. Returns true on success, false on timeout.
36
+ */
37
+ function pollSelector(selector, want, timeoutMs) {
38
+ const deadline = Date.now() + timeoutMs;
39
+ for (;;) {
40
+ const count = selectorCount(selector);
41
+ if (want === "present" ? count > 0 : count === 0) return true;
42
+ if (Date.now() >= deadline) return false;
43
+ sleepSync(SELECTOR_POLL_INTERVAL_MS);
44
+ }
45
+ }
21
46
  /** Wait for element/text with an explicit timeout so long-running async ops don't hang. */
22
- function abWait(selector, timeoutMs = 18e4) {
47
+ function abWait(selector, timeoutMs = 3e4) {
23
48
  logStep("wait", [selector]);
24
- const result = spawnAB(selector.startsWith("text=") ? [
25
- "wait",
26
- "--text",
27
- selector.slice(5),
28
- "--timeout",
29
- String(timeoutMs)
30
- ] : [
31
- "wait",
32
- selector,
33
- "--timeout",
34
- String(timeoutMs)
35
- ]);
36
- if (result.status !== 0) fail(`wait failed: ${selector}`, result);
49
+ if (selector.startsWith("text=")) {
50
+ const result = spawnAB([
51
+ "wait",
52
+ "--text",
53
+ selector.slice(5),
54
+ "--timeout",
55
+ String(timeoutMs)
56
+ ]);
57
+ if (result.status !== 0) fail(`wait failed: ${selector}`, result);
58
+ return;
59
+ }
60
+ if (!pollSelector(selector, "present", timeoutMs)) fail(`wait failed: ${selector} not present within ${timeoutMs}ms`, {
61
+ status: 1,
62
+ stdout: "",
63
+ stderr: ""
64
+ });
37
65
  }
38
66
  /** Assert stable text is visible on page (via wait --text). */
39
67
  function abAssertTextVisible(text, timeoutMs = 3e4) {
@@ -47,35 +75,45 @@ function abAssertTextVisible(text, timeoutMs = 3e4) {
47
75
  ]);
48
76
  if (result.status !== 0) fail(`Assertion failed: text ${JSON.stringify(text)} not found within ${timeoutMs}ms`, result);
49
77
  }
50
- /** Assert element is visible (via wait). */
78
+ /** Assert element is visible (polls `get count`; never uses the blocking `wait <selector>`). */
51
79
  function abAssertVisible(selector, timeoutMs = 3e4) {
52
80
  logStep("assert.visible", [selector]);
53
- const result = spawnAB([
54
- "wait",
55
- selector,
56
- "--timeout",
57
- String(timeoutMs)
58
- ]);
59
- if (result.status !== 0) fail(`Assertion failed: ${JSON.stringify(selector)} not visible within ${timeoutMs}ms`, result);
81
+ if (selector.startsWith("text=")) {
82
+ const result = spawnAB([
83
+ "wait",
84
+ "--text",
85
+ selector.slice(5),
86
+ "--timeout",
87
+ String(timeoutMs)
88
+ ]);
89
+ if (result.status !== 0) fail(`Assertion failed: ${JSON.stringify(selector)} not visible within ${timeoutMs}ms`, result);
90
+ return;
91
+ }
92
+ if (!pollSelector(selector, "present", timeoutMs)) fail(`Assertion failed: ${JSON.stringify(selector)} not visible within ${timeoutMs}ms`, {
93
+ status: 1,
94
+ stdout: "",
95
+ stderr: ""
96
+ });
60
97
  }
61
- /** Assert element is NOT visible (via wait --state hidden or --fn for text). */
98
+ /** Assert element is NOT visible (polls `get count` for absence; --fn for text). */
62
99
  function abAssertNotVisible(selector, timeoutMs = 3e4) {
63
100
  logStep("assert.hidden", [selector]);
64
- const result = spawnAB(selector.startsWith("text=") ? [
65
- "wait",
66
- "--fn",
67
- `!document.body.innerText.includes(${JSON.stringify(selector.slice(5))})`,
68
- "--timeout",
69
- String(timeoutMs)
70
- ] : [
71
- "wait",
72
- selector,
73
- "--state",
74
- "hidden",
75
- "--timeout",
76
- String(timeoutMs)
77
- ]);
78
- if (result.status !== 0) fail(`Assertion failed: ${JSON.stringify(selector)} still visible after ${timeoutMs}ms`, result);
101
+ if (selector.startsWith("text=")) {
102
+ const result = spawnAB([
103
+ "wait",
104
+ "--fn",
105
+ `!document.body.innerText.includes(${JSON.stringify(selector.slice(5))})`,
106
+ "--timeout",
107
+ String(timeoutMs)
108
+ ]);
109
+ if (result.status !== 0) fail(`Assertion failed: ${JSON.stringify(selector)} still visible after ${timeoutMs}ms`, result);
110
+ return;
111
+ }
112
+ if (!pollSelector(selector, "absent", timeoutMs)) fail(`Assertion failed: ${JSON.stringify(selector)} still visible after ${timeoutMs}ms`, {
113
+ status: 1,
114
+ stdout: "",
115
+ stderr: ""
116
+ });
79
117
  }
80
118
  /** Assert URL contains a pattern (via get url). */
81
119
  function abAssertUrl(pattern) {
@@ -21,7 +21,7 @@ const EAGAIN_BACKOFF_MS = [
21
21
  3e3,
22
22
  3e3
23
23
  ];
24
- const PROCESS_HARD_TIMEOUT_MS = 9e4;
24
+ const PROCESS_HARD_TIMEOUT_MS = 35e3;
25
25
  function sleepSync(ms) {
26
26
  const buf = new SharedArrayBuffer(4);
27
27
  Atomics.wait(new Int32Array(buf), 0, 0, ms);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {