@elench/testkit 0.1.82 → 0.1.84

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 (100) hide show
  1. package/README.md +37 -7
  2. package/lib/cli/agents/index.mjs +64 -0
  3. package/lib/cli/agents/investigate.mjs +75 -0
  4. package/lib/cli/agents/investigation-context.mjs +102 -0
  5. package/lib/cli/agents/prompt-builder.mjs +25 -0
  6. package/lib/cli/agents/providers/claude.mjs +74 -0
  7. package/lib/cli/agents/providers/codex.mjs +83 -0
  8. package/lib/cli/agents/providers/shared.mjs +134 -0
  9. package/lib/cli/command-helpers.mjs +53 -25
  10. package/lib/cli/commands/investigate.mjs +87 -0
  11. package/lib/cli/entrypoint.mjs +3 -0
  12. package/lib/cli/presentation/colors.mjs +12 -0
  13. package/lib/cli/presentation/events-reporter.mjs +135 -0
  14. package/lib/cli/presentation/summary-box.mjs +11 -11
  15. package/lib/cli/presentation/tree-reporter.mjs +159 -0
  16. package/lib/cli/tui/run-app.mjs +1 -0
  17. package/lib/cli/tui/run-session-app.mjs +370 -0
  18. package/lib/cli/tui/run-session-state.mjs +481 -0
  19. package/lib/cli/tui/run-tree-state.mjs +1 -0
  20. package/lib/config-api/auth-fixtures.mjs +15 -10
  21. package/lib/discovery/index.mjs +1 -1
  22. package/lib/index.d.ts +5 -1
  23. package/lib/runner/orchestrator.mjs +1 -0
  24. package/lib/runtime/index.d.ts +138 -5
  25. package/lib/runtime/index.mjs +68 -2
  26. package/lib/runtime-src/k6/http-assertions.js +31 -1
  27. package/lib/runtime-src/k6/http-checks.js +120 -0
  28. package/lib/runtime-src/k6/http-suite-runtime.js +5 -1
  29. package/lib/runtime-src/k6/http.js +213 -23
  30. package/lib/runtime-src/shared/error-body.mjs +42 -0
  31. package/lib/runtime-src/shared/http-parsing.mjs +68 -0
  32. package/node_modules/@elench/next-analysis/package.json +1 -1
  33. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  34. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  35. package/node_modules/@elench/ts-analysis/package.json +1 -1
  36. package/package.json +7 -6
  37. package/lib/app/configs.test.mjs +0 -34
  38. package/lib/app/typecheck.test.mjs +0 -24
  39. package/lib/bundler/index.test.mjs +0 -164
  40. package/lib/cli/args.test.mjs +0 -110
  41. package/lib/cli/presentation/code-frames.test.mjs +0 -71
  42. package/lib/cli/presentation/run-reporter.test.mjs +0 -192
  43. package/lib/cli/presentation/summary-box.test.mjs +0 -43
  44. package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
  45. package/lib/config/database.test.mjs +0 -29
  46. package/lib/config/discovery.test.mjs +0 -276
  47. package/lib/config/env.test.mjs +0 -40
  48. package/lib/config/index.test.mjs +0 -44
  49. package/lib/config/paths.test.mjs +0 -27
  50. package/lib/config/runtime.test.mjs +0 -82
  51. package/lib/config/skip-config.test.mjs +0 -63
  52. package/lib/config-api/index.test.mjs +0 -344
  53. package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
  54. package/lib/coverage/backend-discovery.test.mjs +0 -61
  55. package/lib/coverage/evidence.test.mjs +0 -87
  56. package/lib/coverage/index.test.mjs +0 -715
  57. package/lib/coverage/routing.test.mjs +0 -36
  58. package/lib/coverage/shared.test.mjs +0 -72
  59. package/lib/database/fingerprint.test.mjs +0 -99
  60. package/lib/database/index.test.mjs +0 -95
  61. package/lib/database/naming.test.mjs +0 -39
  62. package/lib/database/state.test.mjs +0 -66
  63. package/lib/database/template-steps.test.mjs +0 -43
  64. package/lib/discovery/file-metadata.test.mjs +0 -51
  65. package/lib/discovery/index.test.mjs +0 -182
  66. package/lib/discovery/path-policy.test.mjs +0 -65
  67. package/lib/drizzle/index.test.mjs +0 -33
  68. package/lib/env/index.test.mjs +0 -82
  69. package/lib/history/index.test.mjs +0 -115
  70. package/lib/package.test.mjs +0 -59
  71. package/lib/playwright/index.test.mjs +0 -43
  72. package/lib/regressions/github.test.mjs +0 -324
  73. package/lib/regressions/index.test.mjs +0 -187
  74. package/lib/reporters/playwright.test.mjs +0 -167
  75. package/lib/runner/default-runtime-errors.test.mjs +0 -49
  76. package/lib/runner/execution-config.test.mjs +0 -67
  77. package/lib/runner/failure-details.test.mjs +0 -114
  78. package/lib/runner/formatting.test.mjs +0 -205
  79. package/lib/runner/metadata.test.mjs +0 -52
  80. package/lib/runner/planning.test.mjs +0 -371
  81. package/lib/runner/playwright-config.test.mjs +0 -78
  82. package/lib/runner/processes.test.mjs +0 -21
  83. package/lib/runner/regressions.test.mjs +0 -168
  84. package/lib/runner/reporting.test.mjs +0 -310
  85. package/lib/runner/results.test.mjs +0 -376
  86. package/lib/runner/runtime-manager.test.mjs +0 -252
  87. package/lib/runner/runtime-preparation.test.mjs +0 -141
  88. package/lib/runner/selection.test.mjs +0 -24
  89. package/lib/runner/setup-operations.test.mjs +0 -94
  90. package/lib/runner/state.test.mjs +0 -62
  91. package/lib/runner/suite-selection.test.mjs +0 -49
  92. package/lib/runner/template.test.mjs +0 -272
  93. package/lib/shared/build-config.test.mjs +0 -132
  94. package/lib/shared/configured-steps.test.mjs +0 -102
  95. package/lib/shared/execution-schema.test.mjs +0 -26
  96. package/lib/shared/file-timeout.test.mjs +0 -64
  97. package/lib/shared/test-context.test.mjs +0 -43
  98. package/lib/timing/index.test.mjs +0 -64
  99. package/lib/toolchains/index.test.mjs +0 -168
  100. package/lib/vitest/index.test.mjs +0 -20
@@ -1,344 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- auth,
4
- app,
5
- database,
6
- defineConfig,
7
- defineFile,
8
- toolchain,
9
- } from "./index.mjs";
10
- import { clearRuntimeContext, registerRuntimeContext } from "./runtime.mjs";
11
-
12
- describe("config api", () => {
13
- it("defines repo config plainly", () => {
14
- expect(defineConfig({ execution: { workers: 4 } })).toEqual({
15
- execution: { workers: 4 },
16
- });
17
- });
18
-
19
- it("builds generated auth fixture profiles with actor-aware sessions and headers", () => {
20
- const requests = [];
21
- registerRuntimeContext({
22
- env: {
23
- BASE: "http://api.test",
24
- routeParams: { "x-route": "route-a" },
25
- },
26
- http: {
27
- post(url, body, params) {
28
- const payload = JSON.parse(body);
29
- requests.push({ url, payload, params });
30
- const actor = payload.email.includes(".user-a@")
31
- ? "userA"
32
- : payload.email.includes(".user-b@")
33
- ? "userB"
34
- : "primary";
35
- return {
36
- status: url.endsWith("/signup") ? 201 : 200,
37
- body: JSON.stringify({
38
- data: {
39
- organizations: [{ id: `org-${actor}` }],
40
- },
41
- }),
42
- headers: {
43
- "set-cookie": [
44
- `fixture_session=jwt-${actor}; Path=/`,
45
- `fixture_refresh=refresh-${actor}; Path=/`,
46
- ],
47
- },
48
- };
49
- },
50
- },
51
- });
52
-
53
- const fixture = auth.fixture({
54
- contract: auth.contracts.jsonSession({
55
- authCookie: "fixture_session",
56
- refreshCookie: "fixture_refresh",
57
- organizationIdPath: "data.organizations[0].id",
58
- forwardedFor: "deterministic",
59
- organizationHeader: "X-Organization-Id",
60
- }),
61
- topology: auth.topologies.crossOrg({
62
- namespace: "fixture",
63
- actors: {
64
- primary: { org: "primary" },
65
- userA: { org: "primary" },
66
- userB: { org: "secondary" },
67
- },
68
- }),
69
- });
70
-
71
- const builtProfiles = fixture.profiles({
72
- default: auth.profile.actor("primary", {
73
- headers: {
74
- values: ({ actor }) => ({ "X-Testkit-Actor": actor || "primary" }),
75
- },
76
- }),
77
- dual: auth.profile.actors({
78
- primaryActor: "userA",
79
- actors: ["userA", "userB"],
80
- }),
81
- raw: auth.profile.raw({
82
- headers: {
83
- values: {
84
- "X-Testkit-Mode": "raw",
85
- },
86
- },
87
- }),
88
- });
89
-
90
- expect(fixture.topology.actors.primary.email).toBe("testkit+fixture.primary@example.test");
91
- expect(fixture.topology.actors.userA.name).toBe("Testkit Fixture User A");
92
- expect(fixture.topology.actors.userB.organizationName).toBe("Testkit Fixture Secondary Org");
93
-
94
- const defaultSetup = builtProfiles.default.auth.setup({ env: { BASE: "http://api.test", routeParams: { "x-route": "route-a" } } });
95
- expect(defaultSetup.actors.primary.organizationId).toBe("org-primary");
96
- expect(builtProfiles.default.auth.headers(defaultSetup, { env: { BASE: "http://api.test" } })).toEqual({
97
- Authorization: "Bearer jwt-primary",
98
- });
99
- expect(builtProfiles.default.headers(defaultSetup, { env: { BASE: "http://api.test" } })).toMatchObject({
100
- "Content-Type": "application/json",
101
- "X-Organization-Id": "org-primary",
102
- "X-Testkit-Actor": "primary",
103
- });
104
-
105
- const dualSetup = builtProfiles.dual.auth.setup({ env: { BASE: "http://api.test" } });
106
- expect(dualSetup.actors.userA.organizationId).toBe("org-userA");
107
- expect(dualSetup.actors.userB.organizationId).toBe("org-userB");
108
- expect(builtProfiles.dual.auth.headers(dualSetup)).toEqual({
109
- Authorization: "Bearer jwt-userA",
110
- });
111
-
112
- expect(builtProfiles.raw.rawHeaders(null, { env: { BASE: "http://api.test" } })).toMatchObject({
113
- "Content-Type": "application/json",
114
- "X-Testkit-Mode": "raw",
115
- });
116
-
117
- expect(requests[0].params.headers["x-route"]).toBe("route-a");
118
- expect(requests.map((entry) => entry.url)).toEqual([
119
- "http://api.test/api/v1/auth/signup",
120
- "http://api.test/api/v1/auth/login",
121
- "http://api.test/api/v1/auth/signup",
122
- "http://api.test/api/v1/auth/login",
123
- "http://api.test/api/v1/auth/signup",
124
- "http://api.test/api/v1/auth/login",
125
- ]);
126
- clearRuntimeContext();
127
- });
128
-
129
- it("builds a single-org topology with deterministic defaults", () => {
130
- const topology = auth.topologies.singleOrg({
131
- namespace: "sample-app",
132
- actors: ["primary", "reviewer"],
133
- });
134
-
135
- expect(topology.actors.primary.email).toBe("testkit+sample-app.primary@example.test");
136
- expect(topology.actors.reviewer.name).toBe("Testkit Sample App Reviewer");
137
- expect(topology.actors.primary.organizationName).toBe("Testkit Sample App Primary Org");
138
- });
139
-
140
- it("defines file-local metadata plainly", () => {
141
- expect(defineFile({ skip: "Auth is stubbed", locks: ["background-workers"] })).toEqual({
142
- skip: "Auth is stubbed",
143
- locks: ["background-workers"],
144
- });
145
- });
146
-
147
- it("builds a Next app preset for dev mode", () => {
148
- const config = app.next({ port: 3000 });
149
-
150
- expect(config.local.start).toBe("./node_modules/.bin/next dev -p {port}");
151
- });
152
-
153
- it("builds a Next app preset for start mode with managed runtime env defaults", () => {
154
- const config = app.next({ cwd: "frontend", port: 3000, mode: "start" });
155
-
156
- expect(config.local.start).toBe("./node_modules/.bin/next start --port {port}");
157
- expect(config.local.env).toMatchObject({
158
- NEXT_DIST_DIR: ".next-testkit/{runtimeId}/dist",
159
- NEXT_TSCONFIG_PATH: ".next-testkit/{runtimeId}/tsconfig.json",
160
- });
161
- expect(config.runtime.build).toEqual({
162
- kind: "next",
163
- cwd: "frontend",
164
- distDir: "dist",
165
- tsconfig: "tsconfig.json",
166
- inputs: undefined,
167
- });
168
- });
169
-
170
- it("allows Next start apps to disable managed builds explicitly", () => {
171
- const config = app.next({ cwd: "frontend", port: 3000, mode: "start", build: null });
172
-
173
- expect(config.runtime.build).toBeNull();
174
- expect(config.local.env).toMatchObject({
175
- NEXT_DIST_DIR: ".next-testkit/{runtimeId}/dist",
176
- NEXT_TSCONFIG_PATH: ".next-testkit/{runtimeId}/tsconfig.json",
177
- });
178
- });
179
-
180
- it("builds a Node app preset with tsc build defaults", () => {
181
- const config = app.node({ port: 3000, entry: "src/server.ts" });
182
-
183
- expect(config.local.start).toBe("node {prepareDir}/dist/server.js");
184
- expect(config.runtime.build).toEqual({
185
- kind: "tsc",
186
- cwd: ".",
187
- entry: "src/server.ts",
188
- tsconfig: "tsconfig.json",
189
- outDir: "dist",
190
- inputs: undefined,
191
- });
192
- });
193
-
194
- it("builds node toolchain profiles with a node kind", () => {
195
- expect(toolchain.node({ node: "20.19.5", install: "download" })).toEqual({
196
- kind: "node",
197
- node: "20.19.5",
198
- install: "download",
199
- });
200
- });
201
-
202
- it("builds declarative postgres database templates from plain objects", () => {
203
- expect(
204
- database.postgres({
205
- template: {
206
- inputs: ["db/schema.sql", "scripts/seed.ts"],
207
- schema: "db/schema.sql",
208
- seed: { kind: "command", run: "npm run db:seed" },
209
- verify: { kind: "module", target: "scripts/verify.ts#verifySeed" },
210
- },
211
- })
212
- ).toEqual({
213
- provider: "local",
214
- template: {
215
- inputs: ["db/schema.sql", "scripts/seed.ts"],
216
- migrate: [
217
- {
218
- kind: "sql-file",
219
- path: "db/schema.sql",
220
- },
221
- ],
222
- seed: [
223
- {
224
- kind: "command",
225
- run: "npm run db:seed",
226
- },
227
- ],
228
- verify: [
229
- {
230
- kind: "module",
231
- target: "scripts/verify.ts#verifySeed",
232
- },
233
- ],
234
- },
235
- });
236
- });
237
-
238
- it("prepends schema before explicit migrate steps and normalizes singletons to arrays", () => {
239
- expect(
240
- database.postgres({
241
- template: {
242
- schema: { kind: "sql-file", path: "db/schema.sql", cwd: "db" },
243
- migrate: { kind: "command", run: "echo migrate" },
244
- },
245
- })
246
- ).toEqual({
247
- provider: "local",
248
- template: {
249
- inputs: undefined,
250
- migrate: [
251
- {
252
- kind: "sql-file",
253
- path: "db/schema.sql",
254
- cwd: "db",
255
- },
256
- {
257
- kind: "command",
258
- run: "echo migrate",
259
- },
260
- ],
261
- seed: [],
262
- verify: [],
263
- },
264
- });
265
- });
266
-
267
- it("builds support database presets and expands database env bindings declaratively", () => {
268
- expect(
269
- database.fixture({
270
- reset: true,
271
- })
272
- ).toEqual({
273
- discovery: {
274
- roots: [".testkit-fixture"],
275
- },
276
- envFiles: undefined,
277
- local: false,
278
- database: {
279
- provider: "local",
280
- reset: true,
281
- },
282
- });
283
-
284
- const config = app.node({
285
- port: 3000,
286
- env: {
287
- values: { API_KEY: "test" },
288
- databases: {
289
- onix: { service: "catalog", prefix: "ONIX" },
290
- },
291
- },
292
- });
293
-
294
- expect(config.local.env).toEqual({
295
- API_KEY: "test",
296
- ONIX_DATABASE_HOST: "{dbHost:catalog}",
297
- ONIX_DATABASE_PORT: "{dbPort:catalog}",
298
- ONIX_DATABASE_NAME: "{dbName:catalog}",
299
- ONIX_DATABASE_USER: "{dbUser:catalog}",
300
- ONIX_DATABASE_PASSWORD: "{dbPassword:catalog}",
301
- ONIX_DATABASE_SSL: "0",
302
- });
303
- });
304
-
305
- it("rejects top-level database template lifecycle fields", () => {
306
- expect(() =>
307
- database.postgres({
308
- schema: "db/schema.sql",
309
- })
310
- ).toThrow(/no longer accepts top-level "schema"/);
311
- expect(() =>
312
- database.fixture({
313
- seed: { kind: "command", run: "npm run db:seed" },
314
- })
315
- ).toThrow(/no longer accepts top-level "seed"/);
316
- });
317
-
318
- it("does not leak preset-only helper fields into node app configs", () => {
319
- const config = app.node({
320
- port: 3000,
321
- entry: "src/server.ts",
322
- buildInputs: ["src", "package.json"],
323
- env: { values: { API_KEY: "test" } },
324
- readyPath: "/live",
325
- });
326
-
327
- expect(config).not.toHaveProperty("entry");
328
- expect(config).not.toHaveProperty("buildInputs");
329
- expect(config).not.toHaveProperty("readyPath");
330
- expect(config.local.env).toEqual({ API_KEY: "test" });
331
- expect(config.local.readyUrl).toBe("http://127.0.0.1:{port}/live");
332
- });
333
-
334
- it("rejects flat preset env maps in favor of env.values and env.databases", () => {
335
- expect(() =>
336
- app.node({
337
- port: 3000,
338
- env: {
339
- API_KEY: "test",
340
- },
341
- })
342
- ).toThrow(/Preset env only supports "values" and "databases"/);
343
- });
344
- });
@@ -1,58 +0,0 @@
1
- import fs from "fs";
2
- import os from "os";
3
- import path from "path";
4
- import { afterEach, describe, expect, it } from "vitest";
5
- import { writeNextRuntimeTsconfig } from "./next-runtime-tsconfig.mjs";
6
-
7
- const cleanups = [];
8
-
9
- afterEach(() => {
10
- while (cleanups.length > 0) {
11
- cleanups.pop()();
12
- }
13
- });
14
-
15
- describe("next runtime tsconfig helper", () => {
16
- it("writes a runtime-specific tsconfig for next builds", async () => {
17
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-next-tsconfig-"));
18
- cleanups.push(() => fs.rmSync(tempRoot, { recursive: true, force: true }));
19
-
20
- const serviceDir = path.join(tempRoot, "frontend");
21
- fs.mkdirSync(path.join(serviceDir, "src"), { recursive: true });
22
- fs.writeFileSync(path.join(serviceDir, "tsconfig.json"), "{}\n");
23
- fs.writeFileSync(path.join(serviceDir, "next-env.d.ts"), "// next\n");
24
-
25
- await writeNextRuntimeTsconfig({
26
- cwd: serviceDir,
27
- productDir: tempRoot,
28
- runtimeId: "runtime-1",
29
- });
30
-
31
- const generated = JSON.parse(
32
- fs.readFileSync(path.join(serviceDir, ".next-testkit", "runtime-1", "tsconfig.json"), "utf8")
33
- );
34
- expect(generated).toEqual({
35
- extends: "../../tsconfig.json",
36
- compilerOptions: {
37
- paths: {
38
- "@/*": ["../../src/*"],
39
- },
40
- },
41
- include: [
42
- "../../next-env.d.ts",
43
- "../../src/**/*.ts",
44
- "../../src/**/*.tsx",
45
- "../../src/**/*.mts",
46
- "dist/types/**/*.ts",
47
- "dist/dev/types/**/*.ts",
48
- ],
49
- exclude: [
50
- "node_modules",
51
- "../../src/**/__testkit__/**",
52
- "../../tests/**",
53
- ".next/cache",
54
- ".next/dev",
55
- ],
56
- });
57
- });
58
- });
@@ -1,61 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { extractBackendImports, extractDataImports } from "./backend-discovery.mjs";
3
-
4
- describe("coverage backend discovery", () => {
5
- it("tracks local import names while anchoring capabilities to exported names", () => {
6
- const content = [
7
- 'import { saveSettings as persistSettings } from "@/backend/server/settings";',
8
- 'import { loadProjects as fetchProjects } from "@/backend/data/projects";',
9
- ].join("\n");
10
-
11
- expect(
12
- extractBackendImports({
13
- serviceName: "web",
14
- serviceRoot: "/repo",
15
- filePath: "src/app/settings/actions.ts",
16
- content,
17
- resolveImport: () => "src/backend/server/settings.ts",
18
- })
19
- ).toEqual([
20
- {
21
- importName: "persistSettings",
22
- node: {
23
- id: "server_capability:src/backend/server/settings#saveSettings",
24
- kind: "server_capability",
25
- service: "web",
26
- label: "saveSettings",
27
- filePath: "src/backend/server/settings.ts",
28
- metadata: {
29
- specifier: "@/backend/server/settings",
30
- localName: "persistSettings",
31
- },
32
- },
33
- },
34
- ]);
35
-
36
- expect(
37
- extractDataImports({
38
- serviceName: "web",
39
- serviceRoot: "/repo",
40
- filePath: "src/backend/server/settings.ts",
41
- content,
42
- resolveImport: () => "src/backend/data/projects.ts",
43
- })
44
- ).toEqual([
45
- {
46
- importName: "fetchProjects",
47
- node: {
48
- id: "data_capability:src/backend/data/projects#loadProjects",
49
- kind: "data_capability",
50
- service: "web",
51
- label: "loadProjects",
52
- filePath: "src/backend/data/projects.ts",
53
- metadata: {
54
- specifier: "@/backend/data/projects",
55
- localName: "fetchProjects",
56
- },
57
- },
58
- },
59
- ]);
60
- });
61
- });
@@ -1,87 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- buildEvidenceDetailsFromTargets,
4
- extractPlaywrightTargetsFromContent,
5
- inferDataCapabilitiesFromOwner,
6
- } from "./evidence.mjs";
7
- import { extractPlaywrightVisitedRoutes } from "@elench/ts-analysis";
8
- import { DYNAMIC_SEGMENT_TOKEN } from "./shared.mjs";
9
-
10
- describe("coverage evidence helpers", () => {
11
- it("extracts and dedupes Playwright targets from source content", () => {
12
- const content = `
13
- await page.getByTestId("save-button").click();
14
- await page.locator('[data-testid="save-button"]').click();
15
- await page.getByTestId("drawer").click();
16
- `;
17
-
18
- expect(extractPlaywrightTargetsFromContent(content)).toEqual([
19
- { kind: "testId", value: "save-button", confidence: "high" },
20
- { kind: "testId", value: "drawer", confidence: "high" },
21
- ]);
22
- });
23
-
24
- it("builds compact evidence details only when meaningful signals exist", () => {
25
- expect(
26
- buildEvidenceDetailsFromTargets({
27
- routes: ["/campaigns"],
28
- requestPaths: ["/api/campaigns"],
29
- targets: [{ kind: "testId", value: "save-button", confidence: "high" }],
30
- })
31
- ).toEqual({
32
- route: "/campaigns",
33
- requestPaths: ["/api/campaigns"],
34
- targets: [{ kind: "testId", value: "save-button", confidence: "high" }],
35
- });
36
-
37
- expect(buildEvidenceDetailsFromTargets({ routes: [], requestPaths: [], targets: [] })).toBeUndefined();
38
- });
39
-
40
- it("infers DAL coverage from owner directories", () => {
41
- const dataCapabilities = [
42
- { id: "data_capability:src/backend/data/campaigns#saveCampaignRow", filePath: "src/backend/data/campaigns/index.ts" },
43
- { id: "data_capability:src/backend/data/users#saveUserRow", filePath: "src/backend/data/users/index.ts" },
44
- ];
45
-
46
- expect(inferDataCapabilitiesFromOwner("src/backend/data/campaigns", dataCapabilities)).toEqual([
47
- "data_capability:src/backend/data/campaigns#saveCampaignRow",
48
- ]);
49
- });
50
- });
51
-
52
- describe("extractPlaywrightVisitedRoutes", () => {
53
- it("extracts routes from page.goto() via AST", () => {
54
- expect(extractPlaywrightVisitedRoutes(`
55
- import { test } from "@playwright/test";
56
- test("nav", async ({ page }) => {
57
- await page.goto("/dashboard");
58
- await page.goto("/settings/profile?tab=general");
59
- await page.goto("/dashboard"); // duplicate
60
- await page.goto("https://external.com/path"); // external
61
- await page.goto("http://localhost:3000/events"); // same-origin absolute
62
- });
63
- `, {
64
- filePath: "nav.pw.testkit.ts",
65
- dynamicSegmentToken: DYNAMIC_SEGMENT_TOKEN,
66
- })).toEqual(["/dashboard", "/settings/profile", "/events"]);
67
- });
68
-
69
- it("handles property chain: foo.page.goto()", () => {
70
- expect(extractPlaywrightVisitedRoutes(`
71
- await this.page.goto("/projects");
72
- `, {
73
- filePath: "test.pw.testkit.ts",
74
- dynamicSegmentToken: DYNAMIC_SEGMENT_TOKEN,
75
- })).toEqual(["/projects"]);
76
- });
77
-
78
- it("normalizes dynamic goto expressions into route patterns", () => {
79
- expect(extractPlaywrightVisitedRoutes(`
80
- await page.goto(\`/projects/\${id}\`);
81
- await page.goto(someVar);
82
- `, {
83
- filePath: "test.pw.testkit.ts",
84
- dynamicSegmentToken: DYNAMIC_SEGMENT_TOKEN,
85
- })).toEqual([`/projects/${DYNAMIC_SEGMENT_TOKEN}`]);
86
- });
87
- });