@ait-co/devtools 0.1.144 → 0.2.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 (181) hide show
  1. package/README.en.md +33 -217
  2. package/README.md +33 -246
  3. package/dist/in-app/auto.d.ts +1 -138
  4. package/dist/in-app/auto.js +28 -1102
  5. package/dist/in-app/auto.js.map +1 -1
  6. package/dist/in-app/index.d.ts +38 -547
  7. package/dist/in-app/index.d.ts.map +1 -1
  8. package/dist/in-app/index.js +62 -939
  9. package/dist/in-app/index.js.map +1 -1
  10. package/dist/mcp/cli.d.ts +1 -54
  11. package/dist/mcp/cli.js +33 -9720
  12. package/dist/mcp/cli.js.map +1 -1
  13. package/dist/mcp/server.d.ts +1 -88
  14. package/dist/mcp/server.js +36 -1076
  15. package/dist/mcp/server.js.map +1 -1
  16. package/dist/mock/index.d.ts +19 -20
  17. package/dist/mock/index.d.ts.map +1 -1
  18. package/dist/mock/index.js.map +1 -1
  19. package/dist/panel/index.js +1 -103
  20. package/dist/panel/index.js.map +1 -1
  21. package/dist/relay-url-store-CPZAn-T5.js +107 -0
  22. package/dist/relay-url-store-CPZAn-T5.js.map +1 -0
  23. package/dist/relay-url-store-DLjlvMSA.cjs +108 -0
  24. package/dist/relay-url-store-DLjlvMSA.cjs.map +1 -0
  25. package/dist/stubs/bin-devtools-mcp.js +58 -0
  26. package/dist/stubs/bin-devtools-mcp.js.map +1 -0
  27. package/dist/stubs/bin-devtools-test.d.ts +2 -0
  28. package/dist/stubs/bin-devtools-test.js +55 -0
  29. package/dist/stubs/bin-devtools-test.js.map +1 -0
  30. package/dist/test-runner/config.d.ts +1 -231
  31. package/dist/test-runner/config.js +41 -45
  32. package/dist/test-runner/config.js.map +1 -1
  33. package/dist/{tunnel-BGT9Curk.cjs → tunnel-BKZkOyQp.cjs} +1 -1
  34. package/dist/{tunnel-BGT9Curk.cjs.map → tunnel-BKZkOyQp.cjs.map} +1 -1
  35. package/dist/{tunnel-BOKmLzBO.js → tunnel-CqSCIrdU.js} +1 -1
  36. package/dist/{tunnel-BOKmLzBO.js.map → tunnel-CqSCIrdU.js.map} +1 -1
  37. package/dist/unplugin/index.cjs +9 -18
  38. package/dist/unplugin/index.cjs.map +1 -1
  39. package/dist/unplugin/index.d.cts +22 -3
  40. package/dist/unplugin/index.d.cts.map +1 -1
  41. package/dist/unplugin/index.d.ts +23 -4
  42. package/dist/unplugin/index.d.ts.map +1 -1
  43. package/dist/unplugin/index.js +10 -19
  44. package/dist/unplugin/index.js.map +1 -1
  45. package/package.json +10 -25
  46. package/dist/attach-orchestrator-0F0m_UqQ.js +0 -1845
  47. package/dist/attach-orchestrator-0F0m_UqQ.js.map +0 -1
  48. package/dist/attach-orchestrator-D65KxFy_.js +0 -1831
  49. package/dist/attach-orchestrator-D65KxFy_.js.map +0 -1
  50. package/dist/attach-orchestrator-DL3NQ9ca.js +0 -1846
  51. package/dist/attach-orchestrator-DL3NQ9ca.js.map +0 -1
  52. package/dist/bundle-C796JIwG.d.ts +0 -159
  53. package/dist/bundle-C796JIwG.d.ts.map +0 -1
  54. package/dist/capture-DsP525OZ.d.ts +0 -58
  55. package/dist/capture-DsP525OZ.d.ts.map +0 -1
  56. package/dist/cdp-connection-rP1WdnH5.d.ts +0 -287
  57. package/dist/cdp-connection-rP1WdnH5.d.ts.map +0 -1
  58. package/dist/cell-BaLvusOl.js +0 -68
  59. package/dist/cell-BaLvusOl.js.map +0 -1
  60. package/dist/cell-CBUS3-nT.js +0 -274
  61. package/dist/cell-CBUS3-nT.js.map +0 -1
  62. package/dist/cell-EBKKpAAT.js +0 -307
  63. package/dist/cell-EBKKpAAT.js.map +0 -1
  64. package/dist/chii-relay-B3ZhjGMi.js +0 -304
  65. package/dist/chii-relay-B3ZhjGMi.js.map +0 -1
  66. package/dist/chii-relay-CGMlePMd.cjs +0 -304
  67. package/dist/chii-relay-CGMlePMd.cjs.map +0 -1
  68. package/dist/debug-server-B3ABDrRI.js +0 -456
  69. package/dist/debug-server-B3ABDrRI.js.map +0 -1
  70. package/dist/debug-server-BWhwrVXa.js +0 -1158
  71. package/dist/debug-server-BWhwrVXa.js.map +0 -1
  72. package/dist/debug-server-CfQNxxGW.js +0 -600
  73. package/dist/debug-server-CfQNxxGW.js.map +0 -1
  74. package/dist/devtools-opener-3Drge_RJ.js +0 -75
  75. package/dist/devtools-opener-3Drge_RJ.js.map +0 -1
  76. package/dist/devtools-opener-CJpEsXXQ.js +0 -76
  77. package/dist/devtools-opener-CJpEsXXQ.js.map +0 -1
  78. package/dist/devtools-opener-CxtryS8c.js +0 -75
  79. package/dist/devtools-opener-CxtryS8c.js.map +0 -1
  80. package/dist/in-app/auto.d.ts.map +0 -1
  81. package/dist/mcp/cli.d.ts.map +0 -1
  82. package/dist/mcp/server.d.ts.map +0 -1
  83. package/dist/pool-DcaaOwUq.d.ts +0 -14761
  84. package/dist/pool-DcaaOwUq.d.ts.map +0 -1
  85. package/dist/qr-http-server-C_lqOrgc.js +0 -1644
  86. package/dist/qr-http-server-C_lqOrgc.js.map +0 -1
  87. package/dist/qr-http-server-CopuMbub.js +0 -1644
  88. package/dist/qr-http-server-CopuMbub.js.map +0 -1
  89. package/dist/qr-http-server-DrbIVDjO.js +0 -1645
  90. package/dist/qr-http-server-DrbIVDjO.js.map +0 -1
  91. package/dist/relay-factory-N9QobQxG.js +0 -206
  92. package/dist/relay-factory-N9QobQxG.js.map +0 -1
  93. package/dist/relay-secret-store-BR0YIkNv.cjs +0 -241
  94. package/dist/relay-secret-store-BR0YIkNv.cjs.map +0 -1
  95. package/dist/relay-secret-store-Bmyleu0A.js +0 -154
  96. package/dist/relay-secret-store-Bmyleu0A.js.map +0 -1
  97. package/dist/relay-secret-store-CQenfcSL.js +0 -154
  98. package/dist/relay-secret-store-CQenfcSL.js.map +0 -1
  99. package/dist/relay-secret-store-CYM8CBIF.js +0 -240
  100. package/dist/relay-secret-store-CYM8CBIF.js.map +0 -1
  101. package/dist/relay-secret-store-DKxs7zwq.js +0 -153
  102. package/dist/relay-secret-store-DKxs7zwq.js.map +0 -1
  103. package/dist/relay-secret-store-WJ8EGkIl.js +0 -153
  104. package/dist/relay-secret-store-WJ8EGkIl.js.map +0 -1
  105. package/dist/relay-url-store-BR2XodiO.js +0 -123
  106. package/dist/relay-url-store-BR2XodiO.js.map +0 -1
  107. package/dist/relay-url-store-C1as_m5G.cjs +0 -115
  108. package/dist/relay-url-store-C1as_m5G.cjs.map +0 -1
  109. package/dist/relay-url-store-CH63fVCm.js +0 -122
  110. package/dist/relay-url-store-CH63fVCm.js.map +0 -1
  111. package/dist/relay-url-store-CzFo_84F.js +0 -114
  112. package/dist/relay-url-store-CzFo_84F.js.map +0 -1
  113. package/dist/relay-url-store-DaY1QPes.js +0 -123
  114. package/dist/relay-url-store-DaY1QPes.js.map +0 -1
  115. package/dist/relay-url-store-xmUuTjXA.js +0 -122
  116. package/dist/relay-url-store-xmUuTjXA.js.map +0 -1
  117. package/dist/relay-worker-B5HKkGUY.js +0 -832
  118. package/dist/relay-worker-B5HKkGUY.js.map +0 -1
  119. package/dist/relay-worker-YdlpZQl9.d.ts +0 -214
  120. package/dist/relay-worker-YdlpZQl9.d.ts.map +0 -1
  121. package/dist/rolldown-runtime-DGkTqVfb.js +0 -15
  122. package/dist/rolldown-runtime-DUslC3ob.js +0 -14
  123. package/dist/runtime-kn9DxOeg.d.ts +0 -249
  124. package/dist/runtime-kn9DxOeg.d.ts.map +0 -1
  125. package/dist/test-runner/bin.js +0 -2584
  126. package/dist/test-runner/bin.js.map +0 -1
  127. package/dist/test-runner/bridge-stub.d.ts +0 -125
  128. package/dist/test-runner/bridge-stub.d.ts.map +0 -1
  129. package/dist/test-runner/bridge-stub.js +0 -92
  130. package/dist/test-runner/bridge-stub.js.map +0 -1
  131. package/dist/test-runner/bundle.d.ts +0 -2
  132. package/dist/test-runner/bundle.js +0 -439
  133. package/dist/test-runner/bundle.js.map +0 -1
  134. package/dist/test-runner/capture.d.ts +0 -2
  135. package/dist/test-runner/capture.js +0 -44
  136. package/dist/test-runner/capture.js.map +0 -1
  137. package/dist/test-runner/config.d.ts.map +0 -1
  138. package/dist/test-runner/method-pace.d.ts +0 -82
  139. package/dist/test-runner/method-pace.d.ts.map +0 -1
  140. package/dist/test-runner/method-pace.js +0 -120
  141. package/dist/test-runner/method-pace.js.map +0 -1
  142. package/dist/test-runner/pool.d.ts +0 -2
  143. package/dist/test-runner/pool.js +0 -136
  144. package/dist/test-runner/pool.js.map +0 -1
  145. package/dist/test-runner/relay-factory.d.ts +0 -11245
  146. package/dist/test-runner/relay-factory.d.ts.map +0 -1
  147. package/dist/test-runner/relay-factory.js +0 -206
  148. package/dist/test-runner/relay-factory.js.map +0 -1
  149. package/dist/test-runner/relay-worker.d.ts +0 -2
  150. package/dist/test-runner/relay-worker.js +0 -2
  151. package/dist/test-runner/report.d.ts +0 -163
  152. package/dist/test-runner/report.d.ts.map +0 -1
  153. package/dist/test-runner/report.js +0 -198
  154. package/dist/test-runner/report.js.map +0 -1
  155. package/dist/test-runner/rpc.d.ts +0 -56
  156. package/dist/test-runner/rpc.d.ts.map +0 -1
  157. package/dist/test-runner/rpc.js +0 -98
  158. package/dist/test-runner/rpc.js.map +0 -1
  159. package/dist/test-runner/runtime.d.ts +0 -2
  160. package/dist/test-runner/runtime.js +0 -659
  161. package/dist/test-runner/runtime.js.map +0 -1
  162. package/dist/test-runner/task-graph.d.ts +0 -38
  163. package/dist/test-runner/task-graph.d.ts.map +0 -1
  164. package/dist/test-runner/task-graph.js +0 -182
  165. package/dist/test-runner/task-graph.js.map +0 -1
  166. package/dist/throttle-DKKzX1qC.js +0 -59
  167. package/dist/throttle-DKKzX1qC.js.map +0 -1
  168. package/dist/totp-BqmCLSNA.js +0 -189
  169. package/dist/totp-BqmCLSNA.js.map +0 -1
  170. package/dist/totp-CMHR5lsW.cjs +0 -191
  171. package/dist/totp-CMHR5lsW.cjs.map +0 -1
  172. package/dist/totp-CZLLKfOC.js +0 -200
  173. package/dist/totp-CZLLKfOC.js.map +0 -1
  174. package/dist/totp-DAxys-r0.js +0 -199
  175. package/dist/totp-DAxys-r0.js.map +0 -1
  176. package/dist/totp-DfekTBk3.js +0 -211
  177. package/dist/totp-DfekTBk3.js.map +0 -1
  178. package/dist/totp-Dwft0Kz7.js +0 -3
  179. package/dist/totp-WY6l0ysP.js +0 -190
  180. package/dist/totp-WY6l0ysP.js.map +0 -1
  181. /package/dist/{test-runner/bin.d.ts → stubs/bin-devtools-mcp.d.ts} +0 -0
@@ -1,659 +0,0 @@
1
- import { r as isThrottledError } from "../throttle-DKKzX1qC.js";
2
- //#region src/test-runner/runtime.ts
3
- /**
4
- * Thin test runtime for WebView execution.
5
- *
6
- * This file is bundled by `bundle.ts` together with the user's test file
7
- * into a single IIFE injected into the WebView via `Runtime.evaluate`.
8
- * It MUST stay browser-compatible — no Node.js APIs allowed.
9
- *
10
- * Design: rather than shipping @vitest/runner verbatim into a WebView (where
11
- * its Node-side internals cause issues), this runtime provides a minimal
12
- * compatible API surface — describe/it/test/expect globals — collects results
13
- * into a plain JSON-safe object, and exports `runTestModule` as the entry point.
14
- *
15
- * @vitest/runner and @vitest/expect are listed as dependencies so that the
16
- * package's type contracts are available and so the browser-compatible subsets
17
- * can be referenced. The Vitest custom pool that drives this runtime through
18
- * Vitest's `PoolRunnerInitializer` lives in `pool.ts`.
19
- *
20
- * NOTE: this file is imported by type from Node-side code (rpc.ts / relay-worker.ts)
21
- * for the RunReport / TestResult type shapes. The runtime ITSELF is not imported
22
- * at runtime on the Node side — only the types are used.
23
- */
24
- /** Backoff delays (ms) between throttle-retry attempts: 1s, then 2s. */
25
- const TEST_RETRY_BACKOFF_MS = [1e3, 2e3];
26
- /** Max retry attempts per test after the initial run (devtools#767: "최대 2회 재시도"). */
27
- const TEST_MAX_RETRIES = TEST_RETRY_BACKOFF_MS.length;
28
- /** Real `setTimeout`-backed sleep — the production default for {@link RunTestModuleOptions.sleep}. */
29
- const DEFAULT_SLEEP = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
30
- /**
31
- * Renders a thrown value into a `TestResult.error` string. Handles three
32
- * shapes:
33
- * - a real `Error` (or subclass) — its `.message`;
34
- * - an error-LIKE plain object with a string `.message` (the 2.x native
35
- * bridge envelope this test-runner observes, e.g.
36
- * `{name, code, userInfo, moduleName, __isError}` — devtools#767's
37
- * `APP_BRIDGE_THROTTLED` rejections are exactly this shape, not an
38
- * `Error` instance) — its `.message` field;
39
- * - anything else — `String(e)` (the pre-#767 fallback, unchanged).
40
- *
41
- * Without the middle case, a native envelope's `.message` (the only useful
42
- * diagnostic text) was discarded in favor of the useless `"[object Object]"`.
43
- */
44
- function stringifyThrown(e) {
45
- if (e instanceof Error) return e.message;
46
- if (typeof e === "object" && e !== null) {
47
- const rec = e;
48
- if (typeof rec.message === "string") return rec.message;
49
- }
50
- return String(e);
51
- }
52
- /**
53
- * Brand property that tags asymmetric matcher markers (`expect.any(String)`,
54
- * `expect.objectContaining(...)`, etc). A reserved string key is used as the
55
- * brand so the guard stays a plain own-property check — markers are only ever
56
- * compared in-process by reference, so no cross-realm Symbol coordination is
57
- * needed.
58
- */
59
- const ASYMMETRIC_BRAND = "$$asymmetricMatch";
60
- /** Narrowing guard: is `v` an asymmetric matcher marker? */
61
- function isAsymmetric(v) {
62
- return typeof v === "object" && v !== null && v[ASYMMETRIC_BRAND] === true && typeof v.asymmetricMatch === "function";
63
- }
64
- /**
65
- * `expect.any(Ctor)` — matches a value by its constructor. Primitive wrappers
66
- * (`String`/`Number`/`Boolean`) are matched by `typeof` (so the unboxed
67
- * primitive matches) AND by `instanceof` (so a boxed wrapper instance also
68
- * matches). `BigInt`/`Symbol` match by `typeof`. `Object` matches any non-null
69
- * object (NOT functions, matching vitest), `Array` uses `Array.isArray`,
70
- * `Function` uses `typeof === 'function'`, and any other constructor (user
71
- * classes) falls back to `instanceof`.
72
- */
73
- function makeAny(ctor) {
74
- const name = ctor.name ?? String(ctor);
75
- const ctorAsFn = ctor;
76
- return {
77
- [ASYMMETRIC_BRAND]: true,
78
- toString: () => `Any<${name}>`,
79
- asymmetricMatch(actual) {
80
- if (actual === null || actual === void 0) return false;
81
- switch (ctor) {
82
- case String: return typeof actual === "string" || actual instanceof String;
83
- case Number: return typeof actual === "number" || actual instanceof Number;
84
- case Boolean: return typeof actual === "boolean" || actual instanceof Boolean;
85
- case BigInt: return typeof actual === "bigint";
86
- case Symbol: return typeof actual === "symbol";
87
- case Function: return typeof actual === "function";
88
- case Object: return typeof actual === "object";
89
- case Array: return Array.isArray(actual);
90
- default: return ctorAsFn ? actual instanceof ctorAsFn : false;
91
- }
92
- }
93
- };
94
- }
95
- /** `expect.anything()` — matches any value except `null`/`undefined`. */
96
- function makeAnything() {
97
- return {
98
- [ASYMMETRIC_BRAND]: true,
99
- toString: () => "Anything",
100
- asymmetricMatch: (actual) => actual !== null && actual !== void 0
101
- };
102
- }
103
- /** `expect.objectContaining(obj)` — partial-match every key of `obj` on `actual`. */
104
- function makeObjectContaining(expected) {
105
- return {
106
- [ASYMMETRIC_BRAND]: true,
107
- toString: () => `ObjectContaining<${safeStringify(expected)}>`,
108
- asymmetricMatch: (actual) => deepMatchObject(actual, expected)
109
- };
110
- }
111
- /** `expect.arrayContaining(arr)` — `actual` is an array containing each element of `arr`. */
112
- function makeArrayContaining(expected) {
113
- return {
114
- [ASYMMETRIC_BRAND]: true,
115
- toString: () => `ArrayContaining<${safeStringify(expected)}>`,
116
- asymmetricMatch(actual) {
117
- if (!Array.isArray(actual)) return false;
118
- return expected.every((exp) => actual.some((act) => isAsymmetric(exp) ? exp.asymmetricMatch(act) : deepEqual(act, exp)));
119
- }
120
- };
121
- }
122
- /** `expect.stringContaining(sub)` — `actual` is a string containing `sub`. */
123
- function makeStringContaining(sub) {
124
- return {
125
- [ASYMMETRIC_BRAND]: true,
126
- toString: () => `StringContaining<${sub}>`,
127
- asymmetricMatch: (actual) => typeof actual === "string" && actual.includes(sub)
128
- };
129
- }
130
- /** `expect.stringMatching(re)` — `actual` is a string matching `re` (string or RegExp). */
131
- function makeStringMatching(re) {
132
- const pattern = typeof re === "string" ? new RegExp(re) : re;
133
- return {
134
- [ASYMMETRIC_BRAND]: true,
135
- toString: () => `StringMatching<${String(pattern)}>`,
136
- asymmetricMatch: (actual) => typeof actual === "string" && pattern.test(actual)
137
- };
138
- }
139
- /** JSON.stringify that never throws (cycles/BigInt) — for failure messages. */
140
- function safeStringify(v) {
141
- try {
142
- return JSON.stringify(v, (_k, val) => isAsymmetric(val) ? val.toString() : typeof val === "bigint" ? `${val}n` : val) ?? String(v);
143
- } catch {
144
- return String(v);
145
- }
146
- }
147
- /**
148
- * Recursive structural equality — replaces JSON.stringify comparison to
149
- * handle key-order differences and undefined values correctly.
150
- *
151
- * When the `b` (expected) side is an asymmetric marker, its `asymmetricMatch`
152
- * predicate decides the result instead of structural comparison — markers are
153
- * placeholders, not plain objects, so deep-comparing them never matches.
154
- */
155
- function deepEqual(a, b) {
156
- if (isAsymmetric(b)) return b.asymmetricMatch(a);
157
- if (Object.is(a, b)) return true;
158
- if (a === null || b === null) return false;
159
- if (typeof a !== "object" || typeof b !== "object") return false;
160
- if (Array.isArray(a) && Array.isArray(b)) {
161
- if (a.length !== b.length) return false;
162
- for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;
163
- return true;
164
- }
165
- if (Array.isArray(a) || Array.isArray(b)) return false;
166
- const aObj = a;
167
- const bObj = b;
168
- const aKeys = Object.keys(aObj);
169
- const bKeys = Object.keys(bObj);
170
- if (aKeys.length !== bKeys.length) return false;
171
- for (const key of aKeys) {
172
- if (!Object.hasOwn(bObj, key)) return false;
173
- if (!deepEqual(aObj[key], bObj[key])) return false;
174
- }
175
- return true;
176
- }
177
- /**
178
- * Partial-match: every key in `expected` must exist in `received` with a
179
- * recursively matching value. Extra keys in `received` are ignored.
180
- *
181
- * As in `deepEqual`, an asymmetric marker on the `expected` side delegates to
182
- * its `asymmetricMatch` predicate rather than being compared structurally.
183
- */
184
- function deepMatchObject(received, expected) {
185
- if (isAsymmetric(expected)) return expected.asymmetricMatch(received);
186
- if (Object.is(received, expected)) return true;
187
- if (expected === null || received === null) return Object.is(received, expected);
188
- if (typeof expected !== "object" || typeof received !== "object") return Object.is(received, expected);
189
- if (Array.isArray(expected) && Array.isArray(received)) {
190
- if (expected.length !== received.length) return false;
191
- for (let i = 0; i < expected.length; i++) if (!deepMatchObject(received[i], expected[i])) return false;
192
- return true;
193
- }
194
- if (Array.isArray(expected) || Array.isArray(received)) return false;
195
- const expObj = expected;
196
- const recObj = received;
197
- for (const key of Object.keys(expObj)) {
198
- if (!Object.hasOwn(recObj, key)) return false;
199
- if (!deepMatchObject(recObj[key], expObj[key])) return false;
200
- }
201
- return true;
202
- }
203
- /**
204
- * Resolves a dot-separated property path on an object.
205
- * Returns `{ found: true, value }` or `{ found: false }`.
206
- */
207
- function resolvePath(obj, dotPath) {
208
- const parts = dotPath.split(".");
209
- let cur = obj;
210
- for (const part of parts) {
211
- if (cur === null || cur === void 0 || typeof cur !== "object") return { found: false };
212
- const record = cur;
213
- if (!Object.hasOwn(record, part)) return { found: false };
214
- cur = record[part];
215
- }
216
- return {
217
- found: true,
218
- value: cur
219
- };
220
- }
221
- /** Thrown by expect matchers on failure. */
222
- var AssertionError = class extends Error {
223
- constructor(message) {
224
- super(message);
225
- this.name = "AssertionError";
226
- }
227
- };
228
- /** Minimal expect builder compatible with Vitest's jest-style API. */
229
- var Expectation = class Expectation {
230
- #received;
231
- #negated = false;
232
- constructor(received) {
233
- this.#received = received;
234
- }
235
- get not() {
236
- const neg = new Expectation(this.#received);
237
- neg.#negated = true;
238
- return neg;
239
- }
240
- #assert(pass, msg) {
241
- if (!(this.#negated ? !pass : pass)) throw new AssertionError(this.#negated ? `Expected NOT: ${msg}` : msg);
242
- }
243
- toBe(expected) {
244
- this.#assert(Object.is(this.#received, expected), `Expected ${String(expected)}, received ${String(this.#received)}`);
245
- }
246
- toEqual(expected) {
247
- this.#assert(deepEqual(this.#received, expected), `Expected ${JSON.stringify(expected)}, received ${JSON.stringify(this.#received)}`);
248
- }
249
- toBeTruthy() {
250
- this.#assert(Boolean(this.#received), `Expected truthy, received ${String(this.#received)}`);
251
- }
252
- toBeFalsy() {
253
- this.#assert(!this.#received, `Expected falsy, received ${String(this.#received)}`);
254
- }
255
- toBeNull() {
256
- this.#assert(this.#received === null, `Expected null, received ${String(this.#received)}`);
257
- }
258
- toBeUndefined() {
259
- this.#assert(this.#received === void 0, `Expected undefined, received ${String(this.#received)}`);
260
- }
261
- toBeGreaterThan(n) {
262
- this.#assert(typeof this.#received === "number" && this.#received > n, `Expected > ${n}, received ${String(this.#received)}`);
263
- }
264
- toBeLessThan(n) {
265
- this.#assert(typeof this.#received === "number" && this.#received < n, `Expected < ${n}, received ${String(this.#received)}`);
266
- }
267
- toContain(sub) {
268
- if (Array.isArray(this.#received)) {
269
- this.#assert(this.#received.includes(sub), `Expected array to contain ${JSON.stringify(sub)}, received ${JSON.stringify(this.#received)}`);
270
- return;
271
- }
272
- this.#assert(typeof this.#received === "string" && typeof sub === "string" && this.#received.includes(sub), `Expected to contain "${String(sub)}", received "${String(this.#received)}"`);
273
- }
274
- toThrow(msgFragment) {
275
- if (typeof this.#received !== "function") throw new AssertionError("toThrow: expected a function");
276
- let threw = false;
277
- let errorMsg = "";
278
- try {
279
- this.#received();
280
- } catch (e) {
281
- threw = true;
282
- errorMsg = e instanceof Error ? e.message : String(e);
283
- }
284
- if (msgFragment !== void 0) this.#assert(threw && errorMsg.includes(msgFragment), `Expected to throw containing "${msgFragment}", got "${errorMsg}"`);
285
- else this.#assert(threw, "Expected function to throw");
286
- }
287
- toMatchObject(expected) {
288
- this.#assert(deepMatchObject(this.#received, expected), `Expected object to match ${JSON.stringify(expected)}, received ${JSON.stringify(this.#received)}`);
289
- }
290
- toHaveProperty(dotPath, ...rest) {
291
- const { found, value: actual } = resolvePath(this.#received, dotPath);
292
- if (rest.length > 0) {
293
- const value = rest[0];
294
- this.#assert(found && deepEqual(actual, value), `Expected property "${dotPath}" to equal ${JSON.stringify(value)}, got ${JSON.stringify(actual)}`);
295
- } else this.#assert(found, `Expected property "${dotPath}" to exist`);
296
- }
297
- toBeInstanceOf(ctor) {
298
- this.#assert(this.#received instanceof ctor, `Expected instance of ${ctor.name ?? String(ctor)}, received ${String(this.#received)}`);
299
- }
300
- toBeTypeOf(typeStr) {
301
- this.#assert(typeof this.#received === typeStr, `Expected typeof "${typeStr}", received "${typeof this.#received}"`);
302
- }
303
- toHaveLength(n) {
304
- const length = this.#received?.length;
305
- this.#assert(typeof length === "number" && length === n, `Expected length ${n}, received ${typeof length === "number" ? length : String(this.#received)}`);
306
- }
307
- };
308
- /** The `expect` function installed as a global. */
309
- const expect = ((received) => new Expectation(received));
310
- expect.any = makeAny;
311
- expect.anything = makeAnything;
312
- expect.objectContaining = makeObjectContaining;
313
- expect.arrayContaining = makeArrayContaining;
314
- expect.stringContaining = makeStringContaining;
315
- expect.stringMatching = makeStringMatching;
316
- /**
317
- * Internal sentinel thrown by `ctx.skip()` to unwind the test body early.
318
- * Caught by the run loop (never surfaced as a failure) — see `runTestModule`'s
319
- * test-execution `catch` branch.
320
- */
321
- var InPageSkipSentinel = class {
322
- constructor(note) {
323
- this.note = note;
324
- }
325
- };
326
- function _makeTestContext(name) {
327
- return {
328
- skip(condition, note) {
329
- if (condition === void 0 || condition) throw new InPageSkipSentinel(note);
330
- },
331
- task: { name }
332
- };
333
- }
334
- const _pendingTests = [];
335
- const _suiteStack = [];
336
- /** Registers a suite scope; calls `fn` synchronously to collect inner tests. */
337
- function describe(name, fn) {
338
- _suiteStack.push(name);
339
- fn();
340
- _suiteStack.pop();
341
- }
342
- /** Registers a test. */
343
- function it(name, fn) {
344
- _pendingTests.push({
345
- suitePath: [..._suiteStack],
346
- name,
347
- fn,
348
- skip: false
349
- });
350
- }
351
- /** Alias for `it`. */
352
- const test = it;
353
- /** Skipped test — registered but not executed. */
354
- describe.skip = (name, _fn) => {};
355
- it.skip = (name, _fn) => {
356
- _pendingTests.push({
357
- suitePath: [..._suiteStack],
358
- name,
359
- fn: () => {},
360
- skip: true
361
- });
362
- };
363
- test.skip = it.skip;
364
- function _conditionalIt(skip) {
365
- return (name, fn) => {
366
- _pendingTests.push({
367
- suitePath: [..._suiteStack],
368
- name,
369
- fn,
370
- skip
371
- });
372
- };
373
- }
374
- it.skipIf = (cond) => _conditionalIt(Boolean(cond));
375
- it.runIf = (cond) => _conditionalIt(!cond);
376
- test.skipIf = it.skipIf;
377
- test.runIf = it.runIf;
378
- describe.skipIf = (cond) => cond ? (name, _fn) => void 0 : (name, fn) => describe(name, fn);
379
- describe.runIf = (cond) => cond ? (name, fn) => describe(name, fn) : (name, _fn) => void 0;
380
- const _hooks = [];
381
- function _registerHook(type, fn) {
382
- _hooks.push({
383
- suitePath: [..._suiteStack],
384
- type,
385
- fn
386
- });
387
- }
388
- /**
389
- * Returns hooks whose suitePath is a prefix of `testSuitePath`
390
- * (i.e. the hook's scope contains the test).
391
- */
392
- function _hooksFor(type, testSuitePath) {
393
- return _hooks.filter((h) => {
394
- if (h.type !== type) return false;
395
- if (h.suitePath.length > testSuitePath.length) return false;
396
- for (let i = 0; i < h.suitePath.length; i++) if (h.suitePath[i] !== testSuitePath[i]) return false;
397
- return true;
398
- }).map((h) => h.fn);
399
- }
400
- /** Runs an array of hook functions in order, awaiting each. */
401
- async function _runHooks(fns) {
402
- for (const fn of fns) try {
403
- await fn();
404
- } catch (e) {
405
- return e instanceof Error ? e : new Error(String(e));
406
- }
407
- return null;
408
- }
409
- function beforeAll(fn) {
410
- _registerHook("beforeAll", fn);
411
- }
412
- function afterAll(fn) {
413
- _registerHook("afterAll", fn);
414
- }
415
- function beforeEach(fn) {
416
- _registerHook("beforeEach", fn);
417
- }
418
- function afterEach(fn) {
419
- _registerHook("afterEach", fn);
420
- }
421
- const _spyRegistry = [];
422
- function _createMockFn(impl) {
423
- let currentImpl = impl;
424
- const calls = [];
425
- const mockFn = ((...args) => {
426
- const returnValue = currentImpl ? currentImpl(...args) : void 0;
427
- calls.push({
428
- args,
429
- returnValue
430
- });
431
- return returnValue;
432
- });
433
- mockFn.mock = { calls };
434
- mockFn.mockImplementation = function(fn) {
435
- currentImpl = fn;
436
- return this;
437
- };
438
- mockFn.mockReturnValue = function(value) {
439
- currentImpl = () => value;
440
- return this;
441
- };
442
- mockFn.mockRestore = () => {};
443
- return mockFn;
444
- }
445
- const vi = {
446
- spyOn(obj, method) {
447
- const original = obj[method];
448
- const spy = _createMockFn(typeof original === "function" ? original : void 0);
449
- spy.mockRestore = () => {
450
- obj[method] = original;
451
- };
452
- _spyRegistry.push({
453
- obj,
454
- method,
455
- original
456
- });
457
- obj[method] = spy;
458
- return spy;
459
- },
460
- fn(impl) {
461
- return _createMockFn(impl);
462
- },
463
- restoreAllMocks() {
464
- for (const { obj, method, original } of _spyRegistry) obj[method] = original;
465
- _spyRegistry.length = 0;
466
- }
467
- };
468
- /**
469
- * Runtime globals object — exported for direct use in unit tests so that
470
- * test factories can reference runtime's own `it`/`expect`/`beforeAll`/etc.
471
- * without depending on `globalThis` injection.
472
- *
473
- * In a real WebView bundle these are accessed via globals installed by
474
- * `runTestModule`; in Node tests, import from here directly.
475
- */
476
- const runtimeGlobals = {
477
- describe,
478
- it,
479
- test,
480
- expect,
481
- beforeAll,
482
- afterAll,
483
- beforeEach,
484
- afterEach,
485
- vi
486
- };
487
- /**
488
- * Installs describe/it/test/expect/afterAll/afterEach/beforeAll/beforeEach/vi
489
- * as globals, invokes `moduleFactory` to register the user's tests, then
490
- * executes them and returns a RunReport.
491
- *
492
- * This function is exported as `__testBundle.runTestModule` by the IIFE wrapper
493
- * that bundle.ts generates. The Node-side rpc.ts calls it via `Runtime.evaluate`.
494
- *
495
- * @param moduleFactory - A zero-argument function that contains the user's
496
- * top-level test code (describe/it/test calls). The bundler wraps the entire
497
- * test module so that its top-level statements become the body of this factory.
498
- * @param opts - Test-only overrides (see {@link RunTestModuleOptions}). Never
499
- * passed by the generated bundle (`rpc.ts#buildRunTestsExpression` calls
500
- * `runTestModule(__userFactory)` with exactly one argument) — this second
501
- * parameter exists so unit tests can inject a fake sleep and assert the
502
- * THROTTLED-retry timing without real delays.
503
- */
504
- async function runTestModule(moduleFactory, opts) {
505
- const sleep = opts?.sleep ?? DEFAULT_SLEEP;
506
- _pendingTests.length = 0;
507
- _suiteStack.length = 0;
508
- _hooks.length = 0;
509
- _spyRegistry.length = 0;
510
- const g = globalThis;
511
- g.describe = describe;
512
- g.it = it;
513
- g.test = test;
514
- g.expect = expect;
515
- g.beforeAll = beforeAll;
516
- g.afterAll = afterAll;
517
- g.beforeEach = beforeEach;
518
- g.afterEach = afterEach;
519
- g.vi = vi;
520
- if (moduleFactory) await moduleFactory();
521
- const wallStart = Date.now();
522
- const startedAt = new Date(wallStart).toISOString();
523
- const results = [];
524
- const firedBeforeAll = /* @__PURE__ */ new Set();
525
- const lastIndexForScope = /* @__PURE__ */ new Map();
526
- for (let i = 0; i < _pendingTests.length; i++) {
527
- const key = _pendingTests[i].suitePath.join("\0");
528
- lastIndexForScope.set(key, i);
529
- const path = _pendingTests[i].suitePath;
530
- for (let depth = 0; depth < path.length; depth++) {
531
- const parentKey = path.slice(0, depth).join("\0");
532
- const cur = lastIndexForScope.get(parentKey) ?? -1;
533
- if (i > cur) lastIndexForScope.set(parentKey, i);
534
- }
535
- }
536
- const MODULE_KEY = "";
537
- if (!lastIndexForScope.has(MODULE_KEY) && _pendingTests.length > 0) lastIndexForScope.set(MODULE_KEY, _pendingTests.length - 1);
538
- if (_pendingTests.length > 0) {
539
- const baErr = await _runHooks(_hooksFor("beforeAll", []));
540
- firedBeforeAll.add(MODULE_KEY);
541
- if (baErr) {
542
- for (const pending of _pendingTests) {
543
- const fullName = [...pending.suitePath, pending.name].join(" > ");
544
- results.push({
545
- name: fullName,
546
- status: "fail",
547
- duration: 0,
548
- error: `beforeAll failed: ${baErr.message}`
549
- });
550
- }
551
- const duration = Date.now() - wallStart;
552
- const passed = results.filter((r) => r.status === "pass").length;
553
- const failed = results.filter((r) => r.status === "fail").length;
554
- const skipped = results.filter((r) => r.status === "skip").length;
555
- await _runHooks(_hooksFor("afterAll", []));
556
- return {
557
- startedAt,
558
- duration,
559
- passed,
560
- failed,
561
- skipped,
562
- tests: results
563
- };
564
- }
565
- }
566
- const paceMs = (() => {
567
- const raw = globalThis.__AIT_PACE_MS__;
568
- return typeof raw === "number" && raw > 0 ? raw : 0;
569
- })();
570
- let ranFirstTest = false;
571
- for (let i = 0; i < _pendingTests.length; i++) {
572
- const pending = _pendingTests[i];
573
- const fullName = [...pending.suitePath, pending.name].join(" > ");
574
- if (pending.skip) {
575
- results.push({
576
- name: fullName,
577
- status: "skip",
578
- duration: 0
579
- });
580
- continue;
581
- }
582
- if (paceMs > 0 && ranFirstTest) await sleep(paceMs);
583
- ranFirstTest = true;
584
- for (let depth = 1; depth <= pending.suitePath.length; depth++) {
585
- const scopeKey = pending.suitePath.slice(0, depth).join("\0");
586
- if (!firedBeforeAll.has(scopeKey)) {
587
- const baErr = await _runHooks(_hooks.filter((h) => h.type === "beforeAll" && h.suitePath.join("\0") === scopeKey).map((h) => h.fn));
588
- firedBeforeAll.add(scopeKey);
589
- if (baErr) results.push({
590
- name: fullName,
591
- status: "fail",
592
- duration: 0,
593
- error: `beforeAll failed: ${baErr.message}`
594
- });
595
- }
596
- }
597
- const beErr = await _runHooks(_hooksFor("beforeEach", pending.suitePath));
598
- const tStart = Date.now();
599
- let testErr;
600
- let inPageSkip;
601
- let throttleRetries = 0;
602
- if (beErr) testErr = `beforeEach failed: ${beErr.message}`;
603
- else for (let attempt = 0;; attempt++) try {
604
- await pending.fn(_makeTestContext(fullName));
605
- testErr = void 0;
606
- break;
607
- } catch (e) {
608
- if (e instanceof InPageSkipSentinel) {
609
- inPageSkip = e;
610
- break;
611
- }
612
- testErr = stringifyThrown(e);
613
- if (isThrottledError(e) && attempt < TEST_MAX_RETRIES) {
614
- throttleRetries += 1;
615
- await sleep(TEST_RETRY_BACKOFF_MS[attempt]);
616
- continue;
617
- }
618
- break;
619
- }
620
- const aeErr = await _runHooks(_hooksFor("afterEach", pending.suitePath));
621
- if (aeErr && !testErr && !inPageSkip) testErr = `afterEach failed: ${aeErr.message}`;
622
- if (inPageSkip) results.push({
623
- name: fullName,
624
- status: "skip",
625
- duration: Date.now() - tStart,
626
- ...inPageSkip.note !== void 0 ? { note: inPageSkip.note } : {}
627
- });
628
- else if (testErr !== void 0) results.push({
629
- name: fullName,
630
- status: "fail",
631
- duration: Date.now() - tStart,
632
- error: testErr,
633
- ...throttleRetries > 0 ? { throttleRetries } : {}
634
- });
635
- else results.push({
636
- name: fullName,
637
- status: "pass",
638
- duration: Date.now() - tStart,
639
- ...throttleRetries > 0 ? { throttleRetries } : {}
640
- });
641
- for (let depth = pending.suitePath.length; depth >= 1; depth--) {
642
- const scopeKey = pending.suitePath.slice(0, depth).join("\0");
643
- if ((lastIndexForScope.get(scopeKey) ?? -1) === i) await _runHooks(_hooks.filter((h) => h.type === "afterAll" && h.suitePath.join("\0") === scopeKey).map((h) => h.fn));
644
- }
645
- }
646
- await _runHooks(_hooks.filter((h) => h.type === "afterAll" && h.suitePath.length === 0).map((h) => h.fn));
647
- return {
648
- startedAt,
649
- duration: Date.now() - wallStart,
650
- passed: results.filter((r) => r.status === "pass").length,
651
- failed: results.filter((r) => r.status === "fail").length,
652
- skipped: results.filter((r) => r.status === "skip").length,
653
- tests: results
654
- };
655
- }
656
- //#endregion
657
- export { runTestModule, runtimeGlobals };
658
-
659
- //# sourceMappingURL=runtime.js.map