@jterrazz/test 6.3.0 → 6.4.2
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/index.cjs +76 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -21
- package/dist/index.d.ts +51 -21
- package/dist/index.js +76 -39
- package/dist/index.js.map +1 -1
- package/dist/intercept.cjs +3 -3
- package/dist/intercept.cjs.map +1 -1
- package/dist/intercept.d.cts +3 -3
- package/dist/intercept.d.ts +3 -3
- package/dist/intercept.js +3 -3
- package/dist/intercept.js.map +1 -1
- package/dist/{server.cjs → intercept2.cjs} +2 -2
- package/dist/intercept2.cjs.map +1 -0
- package/dist/{server.js → intercept2.js} +2 -2
- package/dist/intercept2.js.map +1 -0
- package/dist/mock-of.cjs +2 -2
- package/dist/mock-of.cjs.map +1 -1
- package/dist/mock-of.d.cts +2 -2
- package/dist/mock-of.d.ts +2 -2
- package/dist/mock-of.js +2 -2
- package/dist/mock-of.js.map +1 -1
- package/dist/sqlite.adapter.cjs.map +1 -1
- package/dist/sqlite.adapter.d.cts +3 -3
- package/dist/sqlite.adapter.d.ts +3 -3
- package/dist/sqlite.adapter.js.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/server.cjs.map +0 -1
- package/dist/server.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { readdir } from "node:fs/promises";
|
|
8
8
|
import { parse } from "yaml";
|
|
9
|
-
//#region src/adapters/exec.adapter.ts
|
|
9
|
+
//#region src/builder/cli/adapters/exec.adapter.ts
|
|
10
10
|
/**
|
|
11
11
|
* Build a child-process env from the parent env plus user overrides.
|
|
12
12
|
* `null` overrides delete keys (e.g. `INIT_CWD: null`).
|
|
@@ -103,7 +103,7 @@ var ExecAdapter = class {
|
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
105
|
//#endregion
|
|
106
|
-
//#region src/adapters/fetch.adapter.ts
|
|
106
|
+
//#region src/builder/http/adapters/fetch.adapter.ts
|
|
107
107
|
/**
|
|
108
108
|
* Server adapter that sends real HTTP requests via the Fetch API.
|
|
109
109
|
* Used by the `e2e()` specification runner to hit a live server.
|
|
@@ -136,7 +136,7 @@ var FetchAdapter = class {
|
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
138
|
//#endregion
|
|
139
|
-
//#region src/adapters/hono.adapter.ts
|
|
139
|
+
//#region src/builder/http/adapters/hono.adapter.ts
|
|
140
140
|
/**
|
|
141
141
|
* Server adapter that dispatches requests in-process through a Hono app instance.
|
|
142
142
|
* Used by the `integration()` specification runner -- no network overhead.
|
|
@@ -169,21 +169,7 @@ var HonoAdapter = class {
|
|
|
169
169
|
}
|
|
170
170
|
};
|
|
171
171
|
//#endregion
|
|
172
|
-
//#region src/
|
|
173
|
-
/**
|
|
174
|
-
* Extract text blocks from output that contain a pattern.
|
|
175
|
-
* Splits by blank lines (how linter/compiler output is structured),
|
|
176
|
-
* returns only blocks matching the pattern.
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* expect(grep(result.stdout, "unused-var.ts")).toContain("no-unused-vars")
|
|
180
|
-
* expect(grep(result.stdout, "valid/sorted.ts")).not.toContain("sort-imports")
|
|
181
|
-
*/
|
|
182
|
-
function grep(output, pattern) {
|
|
183
|
-
return output.replace(/\x1b\[[0-9;]*m/g, "").split(/\n\s*\n/).filter((block) => block.includes(pattern)).join("\n\n");
|
|
184
|
-
}
|
|
185
|
-
//#endregion
|
|
186
|
-
//#region src/utilities/directory.ts
|
|
172
|
+
//#region src/builder/common/directory.ts
|
|
187
173
|
/**
|
|
188
174
|
* Default ignore patterns — paths that should never appear in a tracked snapshot.
|
|
189
175
|
* Each entry is matched against any path segment OR a path prefix.
|
|
@@ -252,7 +238,7 @@ async function diffDirectories(expectedRoot, actualRoot, options = {}) {
|
|
|
252
238
|
};
|
|
253
239
|
}
|
|
254
240
|
//#endregion
|
|
255
|
-
//#region src/
|
|
241
|
+
//#region src/builder/common/reporter.ts
|
|
256
242
|
const GREEN = "\x1B[32m";
|
|
257
243
|
const RED = "\x1B[31m";
|
|
258
244
|
const DIM = "\x1B[2m";
|
|
@@ -390,7 +376,7 @@ function normalizeOutput(str) {
|
|
|
390
376
|
return stripAnsi(str).replace(/localhost:\d+/g, "localhost:PORT").replace(/\d+ms/g, "Xms").replace(/\d+\.\d+s/g, "X.Xs").trim();
|
|
391
377
|
}
|
|
392
378
|
//#endregion
|
|
393
|
-
//#region src/builder/directory-accessor.ts
|
|
379
|
+
//#region src/builder/common/directory-accessor.ts
|
|
394
380
|
/**
|
|
395
381
|
* Detect whether the user wants to update snapshots — `true` for any of:
|
|
396
382
|
* - vitest run with `-u` / `--update`
|
|
@@ -444,7 +430,21 @@ var DirectoryAccessor = class {
|
|
|
444
430
|
}
|
|
445
431
|
};
|
|
446
432
|
//#endregion
|
|
447
|
-
//#region src/builder/
|
|
433
|
+
//#region src/builder/common/grep.ts
|
|
434
|
+
/**
|
|
435
|
+
* Extract text blocks from output that contain a pattern.
|
|
436
|
+
* Splits by blank lines (how linter/compiler output is structured),
|
|
437
|
+
* returns only blocks matching the pattern.
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* expect(grep(result.stdout, "unused-var.ts")).toContain("no-unused-vars")
|
|
441
|
+
* expect(grep(result.stdout, "valid/sorted.ts")).not.toContain("sort-imports")
|
|
442
|
+
*/
|
|
443
|
+
function grep(output, pattern) {
|
|
444
|
+
return output.replace(/\x1b\[[0-9;]*m/g, "").split(/\n\s*\n/).filter((block) => block.includes(pattern)).join("\n\n");
|
|
445
|
+
}
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region src/builder/common/response-accessor.ts
|
|
448
448
|
/** Accessor for an HTTP response body with file-based assertion support. */
|
|
449
449
|
var ResponseAccessor = class {
|
|
450
450
|
body;
|
|
@@ -465,7 +465,7 @@ var ResponseAccessor = class {
|
|
|
465
465
|
}
|
|
466
466
|
};
|
|
467
467
|
//#endregion
|
|
468
|
-
//#region src/builder/table-assertion.ts
|
|
468
|
+
//#region src/builder/common/table-assertion.ts
|
|
469
469
|
/** Assertion helper for verifying database table contents after a specification run. */
|
|
470
470
|
var TableAssertion = class {
|
|
471
471
|
tableName;
|
|
@@ -494,7 +494,7 @@ var TableAssertion = class {
|
|
|
494
494
|
}
|
|
495
495
|
};
|
|
496
496
|
//#endregion
|
|
497
|
-
//#region src/builder/
|
|
497
|
+
//#region src/builder/common/result.ts
|
|
498
498
|
/**
|
|
499
499
|
* The outcome of a single specification run.
|
|
500
500
|
* Provides accessors for CLI output, HTTP responses, files, directories, and database tables.
|
|
@@ -591,6 +591,7 @@ var SpecificationBuilder = class {
|
|
|
591
591
|
config;
|
|
592
592
|
fixtures = [];
|
|
593
593
|
intercepts = [];
|
|
594
|
+
jobName = null;
|
|
594
595
|
label;
|
|
595
596
|
mocks = [];
|
|
596
597
|
projectName = null;
|
|
@@ -750,6 +751,18 @@ var SpecificationBuilder = class {
|
|
|
750
751
|
return this;
|
|
751
752
|
}
|
|
752
753
|
/**
|
|
754
|
+
* Execute a named job registered via the app() factory.
|
|
755
|
+
*
|
|
756
|
+
* @param name - The job name to trigger (must match a registered JobHandle.name).
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* spec('pipeline').intercept(openai.chat(), openai.response({...})).job('report-refresh').run();
|
|
760
|
+
*/
|
|
761
|
+
job(name) {
|
|
762
|
+
this.jobName = name;
|
|
763
|
+
return this;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
753
766
|
* Execute the specification: run seeds, copy fixtures, then perform the
|
|
754
767
|
* configured action (HTTP or CLI).
|
|
755
768
|
*
|
|
@@ -761,8 +774,14 @@ var SpecificationBuilder = class {
|
|
|
761
774
|
async run() {
|
|
762
775
|
const hasHttpAction = this.request !== null;
|
|
763
776
|
const hasCliAction = this.commandArgs !== null || this.spawnConfig !== null;
|
|
764
|
-
|
|
765
|
-
|
|
777
|
+
const hasJobAction = this.jobName !== null;
|
|
778
|
+
const actionCount = [
|
|
779
|
+
hasHttpAction,
|
|
780
|
+
hasCliAction,
|
|
781
|
+
hasJobAction
|
|
782
|
+
].filter(Boolean).length;
|
|
783
|
+
if (actionCount === 0) throw new Error(`Specification "${this.label}": no action defined. Call .get(), .post(), .exec(), .job(), etc. before .run()`);
|
|
784
|
+
if (actionCount > 1) throw new Error(`Specification "${this.label}": cannot mix action types (.get/.post, .exec/.spawn, .job)`);
|
|
766
785
|
let workDir = null;
|
|
767
786
|
if (hasCliAction) workDir = this.prepareWorkDir();
|
|
768
787
|
if (this.config.databases) for (const db of this.config.databases.values()) await db.reset();
|
|
@@ -780,11 +799,12 @@ var SpecificationBuilder = class {
|
|
|
780
799
|
if (this.fixtures.length > 0 && workDir) for (const entry of this.fixtures) cpSync(resolve(this.testDir, "fixtures", entry.file), resolve(workDir, entry.file), { recursive: true });
|
|
781
800
|
let cleanupIntercepts = null;
|
|
782
801
|
if (this.intercepts.length > 0) {
|
|
783
|
-
const { registerIntercepts } = await import("./
|
|
802
|
+
const { registerIntercepts } = await import("./intercept2.js");
|
|
784
803
|
cleanupIntercepts = await registerIntercepts(this.intercepts);
|
|
785
804
|
}
|
|
786
805
|
try {
|
|
787
806
|
if (hasHttpAction) return await this.runHttpAction();
|
|
807
|
+
if (hasJobAction) return await this.runJobAction();
|
|
788
808
|
return await this.runCliAction(workDir);
|
|
789
809
|
} finally {
|
|
790
810
|
if (cleanupIntercepts) cleanupIntercepts();
|
|
@@ -826,6 +846,19 @@ var SpecificationBuilder = class {
|
|
|
826
846
|
testDir: this.testDir
|
|
827
847
|
});
|
|
828
848
|
}
|
|
849
|
+
async runJobAction() {
|
|
850
|
+
if (!this.config.jobs?.length) throw new Error("Job actions require jobs registered via app(() => ({ server, jobs }))");
|
|
851
|
+
const job = this.config.jobs.find((j) => j.name === this.jobName);
|
|
852
|
+
if (!job) {
|
|
853
|
+
const available = this.config.jobs.map((j) => j.name).join(", ");
|
|
854
|
+
throw new Error(`job("${this.jobName}"): not found. Available: ${available}`);
|
|
855
|
+
}
|
|
856
|
+
await job.execute();
|
|
857
|
+
return new SpecificationResult({
|
|
858
|
+
config: this.config,
|
|
859
|
+
testDir: this.testDir
|
|
860
|
+
});
|
|
861
|
+
}
|
|
829
862
|
async runCliAction(workDir) {
|
|
830
863
|
if (!this.config.command) throw new Error("CLI actions require a command adapter (use cli())");
|
|
831
864
|
const env = this.resolveEnv(workDir);
|
|
@@ -859,7 +892,7 @@ function getCallerDir() {
|
|
|
859
892
|
if (!match) continue;
|
|
860
893
|
const filePath = match[1];
|
|
861
894
|
if (filePath.includes("node_modules")) continue;
|
|
862
|
-
if (filePath.includes("/src/builder/") || filePath.includes("/src/
|
|
895
|
+
if (filePath.includes("/src/builder/") || filePath.includes("/src/spec/")) continue;
|
|
863
896
|
return resolve(filePath, "..");
|
|
864
897
|
}
|
|
865
898
|
throw new Error("Cannot detect caller directory from stack trace");
|
|
@@ -1051,7 +1084,7 @@ var DockerAssertion = class {
|
|
|
1051
1084
|
}
|
|
1052
1085
|
};
|
|
1053
1086
|
//#endregion
|
|
1054
|
-
//#region src/adapters/compose.adapter.ts
|
|
1087
|
+
//#region src/infra/adapters/compose.adapter.ts
|
|
1055
1088
|
/**
|
|
1056
1089
|
* Start the full compose stack and stop it all on cleanup.
|
|
1057
1090
|
* Supports per-worker project names for parallel execution.
|
|
@@ -1098,7 +1131,7 @@ var ComposeStackAdapter = class {
|
|
|
1098
1131
|
}
|
|
1099
1132
|
};
|
|
1100
1133
|
//#endregion
|
|
1101
|
-
//#region src/adapters/testcontainers.adapter.ts
|
|
1134
|
+
//#region src/infra/adapters/testcontainers.adapter.ts
|
|
1102
1135
|
/**
|
|
1103
1136
|
* Container adapter using testcontainers.
|
|
1104
1137
|
* Wraps a GenericContainer for programmatic container lifecycle.
|
|
@@ -1158,7 +1191,7 @@ var TestcontainersAdapter = class {
|
|
|
1158
1191
|
}
|
|
1159
1192
|
};
|
|
1160
1193
|
//#endregion
|
|
1161
|
-
//#region src/
|
|
1194
|
+
//#region src/infra/compose-parser.ts
|
|
1162
1195
|
/**
|
|
1163
1196
|
* Detect the service type from the image name.
|
|
1164
1197
|
*/
|
|
@@ -1231,7 +1264,7 @@ function parseComposeFile(filePath) {
|
|
|
1231
1264
|
};
|
|
1232
1265
|
}
|
|
1233
1266
|
//#endregion
|
|
1234
|
-
//#region src/
|
|
1267
|
+
//#region src/infra/orchestrator.ts
|
|
1235
1268
|
/**
|
|
1236
1269
|
* Orchestrator for test infrastructure.
|
|
1237
1270
|
* Integration: starts services via testcontainers.
|
|
@@ -1440,7 +1473,7 @@ var Orchestrator = class {
|
|
|
1440
1473
|
}
|
|
1441
1474
|
};
|
|
1442
1475
|
//#endregion
|
|
1443
|
-
//#region src/
|
|
1476
|
+
//#region src/spec/resolve.ts
|
|
1444
1477
|
/**
|
|
1445
1478
|
* Resolve root — if relative, resolves from the caller's directory.
|
|
1446
1479
|
*/
|
|
@@ -1454,7 +1487,7 @@ function resolveProjectRoot(root) {
|
|
|
1454
1487
|
const match = line.match(/at\s+(?:.*?\()?(?:file:\/\/)?([^:)]+):\d+:\d+/);
|
|
1455
1488
|
if (!match) continue;
|
|
1456
1489
|
const filePath = match[1];
|
|
1457
|
-
if (filePath.includes("node_modules") || filePath.includes("/src/
|
|
1490
|
+
if (filePath.includes("node_modules") || filePath.includes("/src/spec/")) continue;
|
|
1458
1491
|
return resolve(filePath, "..", root);
|
|
1459
1492
|
}
|
|
1460
1493
|
}
|
|
@@ -1472,7 +1505,7 @@ function resolveCommand(command, root) {
|
|
|
1472
1505
|
return command;
|
|
1473
1506
|
}
|
|
1474
1507
|
//#endregion
|
|
1475
|
-
//#region src/
|
|
1508
|
+
//#region src/spec/spec.ts
|
|
1476
1509
|
/**
|
|
1477
1510
|
* Create a specification runner for the given target.
|
|
1478
1511
|
*
|
|
@@ -1520,12 +1553,16 @@ async function startApp(target, options) {
|
|
|
1520
1553
|
const key = svc.composeName ?? svc.type;
|
|
1521
1554
|
servicesMap[key] = svc;
|
|
1522
1555
|
}
|
|
1523
|
-
const
|
|
1556
|
+
const factoryResult = target.factory(servicesMap);
|
|
1557
|
+
const isComposite = Array.isArray(factoryResult.jobs);
|
|
1558
|
+
const honoApp = isComposite ? factoryResult.server : factoryResult;
|
|
1559
|
+
const jobs = isComposite ? factoryResult.jobs : void 0;
|
|
1524
1560
|
const database = orchestrator.getDatabase() ?? void 0;
|
|
1525
1561
|
const databases = orchestrator.getDatabases();
|
|
1526
1562
|
const runner = createSpecificationRunner({
|
|
1527
1563
|
database,
|
|
1528
1564
|
databases: databases.size > 0 ? databases : void 0,
|
|
1565
|
+
jobs,
|
|
1529
1566
|
server: new HonoAdapter(honoApp)
|
|
1530
1567
|
});
|
|
1531
1568
|
runner.cleanup = async () => {
|
|
@@ -1594,7 +1631,7 @@ async function startCommand(target, options) {
|
|
|
1594
1631
|
return runner;
|
|
1595
1632
|
}
|
|
1596
1633
|
//#endregion
|
|
1597
|
-
//#region src/
|
|
1634
|
+
//#region src/spec/targets.ts
|
|
1598
1635
|
/**
|
|
1599
1636
|
* Test against an in-process Hono app. The factory receives started services
|
|
1600
1637
|
* so you can wire connection strings into your app/DI container.
|
|
@@ -1644,7 +1681,7 @@ function command(bin) {
|
|
|
1644
1681
|
};
|
|
1645
1682
|
}
|
|
1646
1683
|
//#endregion
|
|
1647
|
-
//#region src/
|
|
1684
|
+
//#region src/spec/legacy-cli.ts
|
|
1648
1685
|
/**
|
|
1649
1686
|
* Create a CLI specification runner.
|
|
1650
1687
|
* Runs CLI commands against fixture projects. Optionally starts infrastructure.
|
|
@@ -1679,7 +1716,7 @@ async function cli(options) {
|
|
|
1679
1716
|
return runner;
|
|
1680
1717
|
}
|
|
1681
1718
|
//#endregion
|
|
1682
|
-
//#region src/
|
|
1719
|
+
//#region src/spec/legacy-e2e.ts
|
|
1683
1720
|
/**
|
|
1684
1721
|
* Create an E2E specification runner.
|
|
1685
1722
|
* Starts full docker compose stack. App URL and database auto-detected.
|
|
@@ -1705,7 +1742,7 @@ async function e2e(options = {}) {
|
|
|
1705
1742
|
return runner;
|
|
1706
1743
|
}
|
|
1707
1744
|
//#endregion
|
|
1708
|
-
//#region src/
|
|
1745
|
+
//#region src/spec/legacy-integration.ts
|
|
1709
1746
|
/**
|
|
1710
1747
|
* Create an integration specification runner.
|
|
1711
1748
|
* Starts infra containers via testcontainers, app runs in-process.
|