@elench/testkit 0.1.83 → 0.1.85
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/lib/cli/agents/investigation-interpreter.mjs +320 -0
- package/lib/cli/agents/investigation-log.mjs +37 -0
- package/lib/cli/agents/providers/codex.mjs +1 -1
- package/lib/cli/presentation/tree-reporter.mjs +33 -1
- package/lib/cli/tui/run-session-app.mjs +73 -11
- package/lib/cli/tui/run-session-state.mjs +29 -5
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +7 -6
- package/lib/app/configs.test.mjs +0 -34
- package/lib/app/typecheck.test.mjs +0 -24
- package/lib/bundler/index.test.mjs +0 -164
- package/lib/cli/agents/investigation-context.test.mjs +0 -144
- package/lib/cli/agents/providers/claude.test.mjs +0 -95
- package/lib/cli/agents/providers/codex.test.mjs +0 -93
- package/lib/cli/args.test.mjs +0 -110
- package/lib/cli/command-helpers.test.mjs +0 -122
- package/lib/cli/commands/investigate.test.mjs +0 -83
- package/lib/cli/presentation/code-frames.test.mjs +0 -71
- package/lib/cli/presentation/events-reporter.test.mjs +0 -73
- package/lib/cli/presentation/run-reporter.test.mjs +0 -192
- package/lib/cli/presentation/summary-box.test.mjs +0 -60
- package/lib/cli/presentation/terminal-layout.test.mjs +0 -23
- package/lib/cli/presentation/tree-reporter.test.mjs +0 -166
- package/lib/cli/tui/run-session-app.test.mjs +0 -50
- package/lib/cli/tui/run-tree-state.test.mjs +0 -324
- package/lib/config/database.test.mjs +0 -29
- package/lib/config/discovery.test.mjs +0 -276
- package/lib/config/env.test.mjs +0 -40
- package/lib/config/index.test.mjs +0 -44
- package/lib/config/paths.test.mjs +0 -27
- package/lib/config/runtime.test.mjs +0 -82
- package/lib/config/skip-config.test.mjs +0 -63
- package/lib/config-api/index.test.mjs +0 -398
- package/lib/config-api/next-runtime-tsconfig.test.mjs +0 -58
- package/lib/coverage/backend-discovery.test.mjs +0 -61
- package/lib/coverage/evidence.test.mjs +0 -87
- package/lib/coverage/index.test.mjs +0 -715
- package/lib/coverage/routing.test.mjs +0 -36
- package/lib/coverage/shared.test.mjs +0 -72
- package/lib/database/fingerprint.test.mjs +0 -99
- package/lib/database/index.test.mjs +0 -95
- package/lib/database/naming.test.mjs +0 -39
- package/lib/database/state.test.mjs +0 -66
- package/lib/database/template-steps.test.mjs +0 -43
- package/lib/discovery/file-metadata.test.mjs +0 -51
- package/lib/discovery/index.test.mjs +0 -182
- package/lib/discovery/path-policy.test.mjs +0 -65
- package/lib/drizzle/index.test.mjs +0 -33
- package/lib/env/index.test.mjs +0 -82
- package/lib/history/index.test.mjs +0 -115
- package/lib/package.test.mjs +0 -59
- package/lib/playwright/index.test.mjs +0 -43
- package/lib/regressions/github.test.mjs +0 -324
- package/lib/regressions/index.test.mjs +0 -187
- package/lib/reporters/playwright.test.mjs +0 -167
- package/lib/runner/default-runtime-errors.test.mjs +0 -49
- package/lib/runner/execution-config.test.mjs +0 -67
- package/lib/runner/failure-details.test.mjs +0 -114
- package/lib/runner/formatting.test.mjs +0 -205
- package/lib/runner/metadata.test.mjs +0 -52
- package/lib/runner/planning.test.mjs +0 -371
- package/lib/runner/playwright-config.test.mjs +0 -78
- package/lib/runner/processes.test.mjs +0 -21
- package/lib/runner/regressions.test.mjs +0 -168
- package/lib/runner/reporting.test.mjs +0 -310
- package/lib/runner/results.test.mjs +0 -376
- package/lib/runner/runtime-manager.test.mjs +0 -252
- package/lib/runner/runtime-preparation.test.mjs +0 -141
- package/lib/runner/selection.test.mjs +0 -24
- package/lib/runner/setup-operations.test.mjs +0 -94
- package/lib/runner/state.test.mjs +0 -62
- package/lib/runner/suite-selection.test.mjs +0 -49
- package/lib/runner/template.test.mjs +0 -272
- package/lib/runtime-src/k6/http-checks.test.mjs +0 -120
- package/lib/runtime-src/k6/http.test.mjs +0 -205
- package/lib/runtime-src/shared/http-parsing.test.mjs +0 -69
- package/lib/shared/build-config.test.mjs +0 -132
- package/lib/shared/configured-steps.test.mjs +0 -102
- package/lib/shared/execution-schema.test.mjs +0 -26
- package/lib/shared/file-timeout.test.mjs +0 -64
- package/lib/shared/test-context.test.mjs +0 -43
- package/lib/timing/index.test.mjs +0 -64
- package/lib/toolchains/index.test.mjs +0 -168
- package/lib/vitest/index.test.mjs +0 -20
|
@@ -1,398 +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("treats signup as best-effort when login still succeeds", () => {
|
|
141
|
-
registerRuntimeContext({
|
|
142
|
-
env: {
|
|
143
|
-
BASE: "http://api.test",
|
|
144
|
-
routeParams: {},
|
|
145
|
-
},
|
|
146
|
-
http: {
|
|
147
|
-
post(url, body) {
|
|
148
|
-
const payload = JSON.parse(body);
|
|
149
|
-
if (url.endsWith("/signup")) {
|
|
150
|
-
return {
|
|
151
|
-
status: 500,
|
|
152
|
-
body: JSON.stringify({ error: "Failed to create account" }),
|
|
153
|
-
headers: {},
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
status: 200,
|
|
159
|
-
body: JSON.stringify({
|
|
160
|
-
data: {
|
|
161
|
-
organizations: [{ id: "org-primary" }],
|
|
162
|
-
},
|
|
163
|
-
}),
|
|
164
|
-
headers: {
|
|
165
|
-
"set-cookie": ["fixture_session=jwt-primary; Path=/"],
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const fixture = auth.fixture({
|
|
173
|
-
contract: auth.contracts.jsonSession({
|
|
174
|
-
authCookie: "fixture_session",
|
|
175
|
-
organizationIdPath: "data.organizations[0].id",
|
|
176
|
-
}),
|
|
177
|
-
topology: auth.topologies.singleOrg({
|
|
178
|
-
namespace: "signup-race",
|
|
179
|
-
actors: ["primary"],
|
|
180
|
-
}),
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const profiles = fixture.profiles({
|
|
184
|
-
default: auth.profile.actor("primary"),
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const setup = profiles.default.auth.setup({ env: { BASE: "http://api.test", routeParams: {} } });
|
|
188
|
-
expect(setup.actors.primary.organizationId).toBe("org-primary");
|
|
189
|
-
expect(setup.actors.primary.jwt).toBe("jwt-primary");
|
|
190
|
-
|
|
191
|
-
clearRuntimeContext();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("defines file-local metadata plainly", () => {
|
|
195
|
-
expect(defineFile({ skip: "Auth is stubbed", locks: ["background-workers"] })).toEqual({
|
|
196
|
-
skip: "Auth is stubbed",
|
|
197
|
-
locks: ["background-workers"],
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("builds a Next app preset for dev mode", () => {
|
|
202
|
-
const config = app.next({ port: 3000 });
|
|
203
|
-
|
|
204
|
-
expect(config.local.start).toBe("./node_modules/.bin/next dev -p {port}");
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("builds a Next app preset for start mode with managed runtime env defaults", () => {
|
|
208
|
-
const config = app.next({ cwd: "frontend", port: 3000, mode: "start" });
|
|
209
|
-
|
|
210
|
-
expect(config.local.start).toBe("./node_modules/.bin/next start --port {port}");
|
|
211
|
-
expect(config.local.env).toMatchObject({
|
|
212
|
-
NEXT_DIST_DIR: ".next-testkit/{runtimeId}/dist",
|
|
213
|
-
NEXT_TSCONFIG_PATH: ".next-testkit/{runtimeId}/tsconfig.json",
|
|
214
|
-
});
|
|
215
|
-
expect(config.runtime.build).toEqual({
|
|
216
|
-
kind: "next",
|
|
217
|
-
cwd: "frontend",
|
|
218
|
-
distDir: "dist",
|
|
219
|
-
tsconfig: "tsconfig.json",
|
|
220
|
-
inputs: undefined,
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("allows Next start apps to disable managed builds explicitly", () => {
|
|
225
|
-
const config = app.next({ cwd: "frontend", port: 3000, mode: "start", build: null });
|
|
226
|
-
|
|
227
|
-
expect(config.runtime.build).toBeNull();
|
|
228
|
-
expect(config.local.env).toMatchObject({
|
|
229
|
-
NEXT_DIST_DIR: ".next-testkit/{runtimeId}/dist",
|
|
230
|
-
NEXT_TSCONFIG_PATH: ".next-testkit/{runtimeId}/tsconfig.json",
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("builds a Node app preset with tsc build defaults", () => {
|
|
235
|
-
const config = app.node({ port: 3000, entry: "src/server.ts" });
|
|
236
|
-
|
|
237
|
-
expect(config.local.start).toBe("node {prepareDir}/dist/server.js");
|
|
238
|
-
expect(config.runtime.build).toEqual({
|
|
239
|
-
kind: "tsc",
|
|
240
|
-
cwd: ".",
|
|
241
|
-
entry: "src/server.ts",
|
|
242
|
-
tsconfig: "tsconfig.json",
|
|
243
|
-
outDir: "dist",
|
|
244
|
-
inputs: undefined,
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it("builds node toolchain profiles with a node kind", () => {
|
|
249
|
-
expect(toolchain.node({ node: "20.19.5", install: "download" })).toEqual({
|
|
250
|
-
kind: "node",
|
|
251
|
-
node: "20.19.5",
|
|
252
|
-
install: "download",
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it("builds declarative postgres database templates from plain objects", () => {
|
|
257
|
-
expect(
|
|
258
|
-
database.postgres({
|
|
259
|
-
template: {
|
|
260
|
-
inputs: ["db/schema.sql", "scripts/seed.ts"],
|
|
261
|
-
schema: "db/schema.sql",
|
|
262
|
-
seed: { kind: "command", run: "npm run db:seed" },
|
|
263
|
-
verify: { kind: "module", target: "scripts/verify.ts#verifySeed" },
|
|
264
|
-
},
|
|
265
|
-
})
|
|
266
|
-
).toEqual({
|
|
267
|
-
provider: "local",
|
|
268
|
-
template: {
|
|
269
|
-
inputs: ["db/schema.sql", "scripts/seed.ts"],
|
|
270
|
-
migrate: [
|
|
271
|
-
{
|
|
272
|
-
kind: "sql-file",
|
|
273
|
-
path: "db/schema.sql",
|
|
274
|
-
},
|
|
275
|
-
],
|
|
276
|
-
seed: [
|
|
277
|
-
{
|
|
278
|
-
kind: "command",
|
|
279
|
-
run: "npm run db:seed",
|
|
280
|
-
},
|
|
281
|
-
],
|
|
282
|
-
verify: [
|
|
283
|
-
{
|
|
284
|
-
kind: "module",
|
|
285
|
-
target: "scripts/verify.ts#verifySeed",
|
|
286
|
-
},
|
|
287
|
-
],
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it("prepends schema before explicit migrate steps and normalizes singletons to arrays", () => {
|
|
293
|
-
expect(
|
|
294
|
-
database.postgres({
|
|
295
|
-
template: {
|
|
296
|
-
schema: { kind: "sql-file", path: "db/schema.sql", cwd: "db" },
|
|
297
|
-
migrate: { kind: "command", run: "echo migrate" },
|
|
298
|
-
},
|
|
299
|
-
})
|
|
300
|
-
).toEqual({
|
|
301
|
-
provider: "local",
|
|
302
|
-
template: {
|
|
303
|
-
inputs: undefined,
|
|
304
|
-
migrate: [
|
|
305
|
-
{
|
|
306
|
-
kind: "sql-file",
|
|
307
|
-
path: "db/schema.sql",
|
|
308
|
-
cwd: "db",
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
kind: "command",
|
|
312
|
-
run: "echo migrate",
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
seed: [],
|
|
316
|
-
verify: [],
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
it("builds support database presets and expands database env bindings declaratively", () => {
|
|
322
|
-
expect(
|
|
323
|
-
database.fixture({
|
|
324
|
-
reset: true,
|
|
325
|
-
})
|
|
326
|
-
).toEqual({
|
|
327
|
-
discovery: {
|
|
328
|
-
roots: [".testkit-fixture"],
|
|
329
|
-
},
|
|
330
|
-
envFiles: undefined,
|
|
331
|
-
local: false,
|
|
332
|
-
database: {
|
|
333
|
-
provider: "local",
|
|
334
|
-
reset: true,
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const config = app.node({
|
|
339
|
-
port: 3000,
|
|
340
|
-
env: {
|
|
341
|
-
values: { API_KEY: "test" },
|
|
342
|
-
databases: {
|
|
343
|
-
onix: { service: "catalog", prefix: "ONIX" },
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
expect(config.local.env).toEqual({
|
|
349
|
-
API_KEY: "test",
|
|
350
|
-
ONIX_DATABASE_HOST: "{dbHost:catalog}",
|
|
351
|
-
ONIX_DATABASE_PORT: "{dbPort:catalog}",
|
|
352
|
-
ONIX_DATABASE_NAME: "{dbName:catalog}",
|
|
353
|
-
ONIX_DATABASE_USER: "{dbUser:catalog}",
|
|
354
|
-
ONIX_DATABASE_PASSWORD: "{dbPassword:catalog}",
|
|
355
|
-
ONIX_DATABASE_SSL: "0",
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it("rejects top-level database template lifecycle fields", () => {
|
|
360
|
-
expect(() =>
|
|
361
|
-
database.postgres({
|
|
362
|
-
schema: "db/schema.sql",
|
|
363
|
-
})
|
|
364
|
-
).toThrow(/no longer accepts top-level "schema"/);
|
|
365
|
-
expect(() =>
|
|
366
|
-
database.fixture({
|
|
367
|
-
seed: { kind: "command", run: "npm run db:seed" },
|
|
368
|
-
})
|
|
369
|
-
).toThrow(/no longer accepts top-level "seed"/);
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it("does not leak preset-only helper fields into node app configs", () => {
|
|
373
|
-
const config = app.node({
|
|
374
|
-
port: 3000,
|
|
375
|
-
entry: "src/server.ts",
|
|
376
|
-
buildInputs: ["src", "package.json"],
|
|
377
|
-
env: { values: { API_KEY: "test" } },
|
|
378
|
-
readyPath: "/live",
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
expect(config).not.toHaveProperty("entry");
|
|
382
|
-
expect(config).not.toHaveProperty("buildInputs");
|
|
383
|
-
expect(config).not.toHaveProperty("readyPath");
|
|
384
|
-
expect(config.local.env).toEqual({ API_KEY: "test" });
|
|
385
|
-
expect(config.local.readyUrl).toBe("http://127.0.0.1:{port}/live");
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("rejects flat preset env maps in favor of env.values and env.databases", () => {
|
|
389
|
-
expect(() =>
|
|
390
|
-
app.node({
|
|
391
|
-
port: 3000,
|
|
392
|
-
env: {
|
|
393
|
-
API_KEY: "test",
|
|
394
|
-
},
|
|
395
|
-
})
|
|
396
|
-
).toThrow(/Preset env only supports "values" and "databases"/);
|
|
397
|
-
});
|
|
398
|
-
});
|
|
@@ -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
|
-
});
|