@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.cjs
CHANGED
|
@@ -8,7 +8,7 @@ let node_os = require("node:os");
|
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
10
10
|
let yaml = require("yaml");
|
|
11
|
-
//#region src/adapters/exec.adapter.ts
|
|
11
|
+
//#region src/builder/cli/adapters/exec.adapter.ts
|
|
12
12
|
/**
|
|
13
13
|
* Build a child-process env from the parent env plus user overrides.
|
|
14
14
|
* `null` overrides delete keys (e.g. `INIT_CWD: null`).
|
|
@@ -105,7 +105,7 @@ var ExecAdapter = class {
|
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
//#endregion
|
|
108
|
-
//#region src/adapters/fetch.adapter.ts
|
|
108
|
+
//#region src/builder/http/adapters/fetch.adapter.ts
|
|
109
109
|
/**
|
|
110
110
|
* Server adapter that sends real HTTP requests via the Fetch API.
|
|
111
111
|
* Used by the `e2e()` specification runner to hit a live server.
|
|
@@ -138,7 +138,7 @@ var FetchAdapter = class {
|
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
//#endregion
|
|
141
|
-
//#region src/adapters/hono.adapter.ts
|
|
141
|
+
//#region src/builder/http/adapters/hono.adapter.ts
|
|
142
142
|
/**
|
|
143
143
|
* Server adapter that dispatches requests in-process through a Hono app instance.
|
|
144
144
|
* Used by the `integration()` specification runner -- no network overhead.
|
|
@@ -171,21 +171,7 @@ var HonoAdapter = class {
|
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
173
|
//#endregion
|
|
174
|
-
//#region src/
|
|
175
|
-
/**
|
|
176
|
-
* Extract text blocks from output that contain a pattern.
|
|
177
|
-
* Splits by blank lines (how linter/compiler output is structured),
|
|
178
|
-
* returns only blocks matching the pattern.
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* expect(grep(result.stdout, "unused-var.ts")).toContain("no-unused-vars")
|
|
182
|
-
* expect(grep(result.stdout, "valid/sorted.ts")).not.toContain("sort-imports")
|
|
183
|
-
*/
|
|
184
|
-
function grep(output, pattern) {
|
|
185
|
-
return output.replace(/\x1b\[[0-9;]*m/g, "").split(/\n\s*\n/).filter((block) => block.includes(pattern)).join("\n\n");
|
|
186
|
-
}
|
|
187
|
-
//#endregion
|
|
188
|
-
//#region src/utilities/directory.ts
|
|
174
|
+
//#region src/builder/common/directory.ts
|
|
189
175
|
/**
|
|
190
176
|
* Default ignore patterns — paths that should never appear in a tracked snapshot.
|
|
191
177
|
* Each entry is matched against any path segment OR a path prefix.
|
|
@@ -254,7 +240,7 @@ async function diffDirectories(expectedRoot, actualRoot, options = {}) {
|
|
|
254
240
|
};
|
|
255
241
|
}
|
|
256
242
|
//#endregion
|
|
257
|
-
//#region src/
|
|
243
|
+
//#region src/builder/common/reporter.ts
|
|
258
244
|
const GREEN = "\x1B[32m";
|
|
259
245
|
const RED = "\x1B[31m";
|
|
260
246
|
const DIM = "\x1B[2m";
|
|
@@ -392,7 +378,7 @@ function normalizeOutput(str) {
|
|
|
392
378
|
return stripAnsi(str).replace(/localhost:\d+/g, "localhost:PORT").replace(/\d+ms/g, "Xms").replace(/\d+\.\d+s/g, "X.Xs").trim();
|
|
393
379
|
}
|
|
394
380
|
//#endregion
|
|
395
|
-
//#region src/builder/directory-accessor.ts
|
|
381
|
+
//#region src/builder/common/directory-accessor.ts
|
|
396
382
|
/**
|
|
397
383
|
* Detect whether the user wants to update snapshots — `true` for any of:
|
|
398
384
|
* - vitest run with `-u` / `--update`
|
|
@@ -446,7 +432,21 @@ var DirectoryAccessor = class {
|
|
|
446
432
|
}
|
|
447
433
|
};
|
|
448
434
|
//#endregion
|
|
449
|
-
//#region src/builder/
|
|
435
|
+
//#region src/builder/common/grep.ts
|
|
436
|
+
/**
|
|
437
|
+
* Extract text blocks from output that contain a pattern.
|
|
438
|
+
* Splits by blank lines (how linter/compiler output is structured),
|
|
439
|
+
* returns only blocks matching the pattern.
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* expect(grep(result.stdout, "unused-var.ts")).toContain("no-unused-vars")
|
|
443
|
+
* expect(grep(result.stdout, "valid/sorted.ts")).not.toContain("sort-imports")
|
|
444
|
+
*/
|
|
445
|
+
function grep(output, pattern) {
|
|
446
|
+
return output.replace(/\x1b\[[0-9;]*m/g, "").split(/\n\s*\n/).filter((block) => block.includes(pattern)).join("\n\n");
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/builder/common/response-accessor.ts
|
|
450
450
|
/** Accessor for an HTTP response body with file-based assertion support. */
|
|
451
451
|
var ResponseAccessor = class {
|
|
452
452
|
body;
|
|
@@ -467,7 +467,7 @@ var ResponseAccessor = class {
|
|
|
467
467
|
}
|
|
468
468
|
};
|
|
469
469
|
//#endregion
|
|
470
|
-
//#region src/builder/table-assertion.ts
|
|
470
|
+
//#region src/builder/common/table-assertion.ts
|
|
471
471
|
/** Assertion helper for verifying database table contents after a specification run. */
|
|
472
472
|
var TableAssertion = class {
|
|
473
473
|
tableName;
|
|
@@ -496,7 +496,7 @@ var TableAssertion = class {
|
|
|
496
496
|
}
|
|
497
497
|
};
|
|
498
498
|
//#endregion
|
|
499
|
-
//#region src/builder/
|
|
499
|
+
//#region src/builder/common/result.ts
|
|
500
500
|
/**
|
|
501
501
|
* The outcome of a single specification run.
|
|
502
502
|
* Provides accessors for CLI output, HTTP responses, files, directories, and database tables.
|
|
@@ -593,6 +593,7 @@ var SpecificationBuilder = class {
|
|
|
593
593
|
config;
|
|
594
594
|
fixtures = [];
|
|
595
595
|
intercepts = [];
|
|
596
|
+
jobName = null;
|
|
596
597
|
label;
|
|
597
598
|
mocks = [];
|
|
598
599
|
projectName = null;
|
|
@@ -752,6 +753,18 @@ var SpecificationBuilder = class {
|
|
|
752
753
|
return this;
|
|
753
754
|
}
|
|
754
755
|
/**
|
|
756
|
+
* Execute a named job registered via the app() factory.
|
|
757
|
+
*
|
|
758
|
+
* @param name - The job name to trigger (must match a registered JobHandle.name).
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* spec('pipeline').intercept(openai.chat(), openai.response({...})).job('report-refresh').run();
|
|
762
|
+
*/
|
|
763
|
+
job(name) {
|
|
764
|
+
this.jobName = name;
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
755
768
|
* Execute the specification: run seeds, copy fixtures, then perform the
|
|
756
769
|
* configured action (HTTP or CLI).
|
|
757
770
|
*
|
|
@@ -763,8 +776,14 @@ var SpecificationBuilder = class {
|
|
|
763
776
|
async run() {
|
|
764
777
|
const hasHttpAction = this.request !== null;
|
|
765
778
|
const hasCliAction = this.commandArgs !== null || this.spawnConfig !== null;
|
|
766
|
-
|
|
767
|
-
|
|
779
|
+
const hasJobAction = this.jobName !== null;
|
|
780
|
+
const actionCount = [
|
|
781
|
+
hasHttpAction,
|
|
782
|
+
hasCliAction,
|
|
783
|
+
hasJobAction
|
|
784
|
+
].filter(Boolean).length;
|
|
785
|
+
if (actionCount === 0) throw new Error(`Specification "${this.label}": no action defined. Call .get(), .post(), .exec(), .job(), etc. before .run()`);
|
|
786
|
+
if (actionCount > 1) throw new Error(`Specification "${this.label}": cannot mix action types (.get/.post, .exec/.spawn, .job)`);
|
|
768
787
|
let workDir = null;
|
|
769
788
|
if (hasCliAction) workDir = this.prepareWorkDir();
|
|
770
789
|
if (this.config.databases) for (const db of this.config.databases.values()) await db.reset();
|
|
@@ -782,11 +801,12 @@ var SpecificationBuilder = class {
|
|
|
782
801
|
if (this.fixtures.length > 0 && workDir) for (const entry of this.fixtures) (0, node_fs.cpSync)((0, node_path.resolve)(this.testDir, "fixtures", entry.file), (0, node_path.resolve)(workDir, entry.file), { recursive: true });
|
|
783
802
|
let cleanupIntercepts = null;
|
|
784
803
|
if (this.intercepts.length > 0) {
|
|
785
|
-
const { registerIntercepts } = await Promise.resolve().then(() => require("./
|
|
804
|
+
const { registerIntercepts } = await Promise.resolve().then(() => require("./intercept2.cjs"));
|
|
786
805
|
cleanupIntercepts = await registerIntercepts(this.intercepts);
|
|
787
806
|
}
|
|
788
807
|
try {
|
|
789
808
|
if (hasHttpAction) return await this.runHttpAction();
|
|
809
|
+
if (hasJobAction) return await this.runJobAction();
|
|
790
810
|
return await this.runCliAction(workDir);
|
|
791
811
|
} finally {
|
|
792
812
|
if (cleanupIntercepts) cleanupIntercepts();
|
|
@@ -828,6 +848,19 @@ var SpecificationBuilder = class {
|
|
|
828
848
|
testDir: this.testDir
|
|
829
849
|
});
|
|
830
850
|
}
|
|
851
|
+
async runJobAction() {
|
|
852
|
+
if (!this.config.jobs?.length) throw new Error("Job actions require jobs registered via app(() => ({ server, jobs }))");
|
|
853
|
+
const job = this.config.jobs.find((j) => j.name === this.jobName);
|
|
854
|
+
if (!job) {
|
|
855
|
+
const available = this.config.jobs.map((j) => j.name).join(", ");
|
|
856
|
+
throw new Error(`job("${this.jobName}"): not found. Available: ${available}`);
|
|
857
|
+
}
|
|
858
|
+
await job.execute();
|
|
859
|
+
return new SpecificationResult({
|
|
860
|
+
config: this.config,
|
|
861
|
+
testDir: this.testDir
|
|
862
|
+
});
|
|
863
|
+
}
|
|
831
864
|
async runCliAction(workDir) {
|
|
832
865
|
if (!this.config.command) throw new Error("CLI actions require a command adapter (use cli())");
|
|
833
866
|
const env = this.resolveEnv(workDir);
|
|
@@ -861,7 +894,7 @@ function getCallerDir() {
|
|
|
861
894
|
if (!match) continue;
|
|
862
895
|
const filePath = match[1];
|
|
863
896
|
if (filePath.includes("node_modules")) continue;
|
|
864
|
-
if (filePath.includes("/src/builder/") || filePath.includes("/src/
|
|
897
|
+
if (filePath.includes("/src/builder/") || filePath.includes("/src/spec/")) continue;
|
|
865
898
|
return (0, node_path.resolve)(filePath, "..");
|
|
866
899
|
}
|
|
867
900
|
throw new Error("Cannot detect caller directory from stack trace");
|
|
@@ -1053,7 +1086,7 @@ var DockerAssertion = class {
|
|
|
1053
1086
|
}
|
|
1054
1087
|
};
|
|
1055
1088
|
//#endregion
|
|
1056
|
-
//#region src/adapters/compose.adapter.ts
|
|
1089
|
+
//#region src/infra/adapters/compose.adapter.ts
|
|
1057
1090
|
/**
|
|
1058
1091
|
* Start the full compose stack and stop it all on cleanup.
|
|
1059
1092
|
* Supports per-worker project names for parallel execution.
|
|
@@ -1100,7 +1133,7 @@ var ComposeStackAdapter = class {
|
|
|
1100
1133
|
}
|
|
1101
1134
|
};
|
|
1102
1135
|
//#endregion
|
|
1103
|
-
//#region src/adapters/testcontainers.adapter.ts
|
|
1136
|
+
//#region src/infra/adapters/testcontainers.adapter.ts
|
|
1104
1137
|
/**
|
|
1105
1138
|
* Container adapter using testcontainers.
|
|
1106
1139
|
* Wraps a GenericContainer for programmatic container lifecycle.
|
|
@@ -1160,7 +1193,7 @@ var TestcontainersAdapter = class {
|
|
|
1160
1193
|
}
|
|
1161
1194
|
};
|
|
1162
1195
|
//#endregion
|
|
1163
|
-
//#region src/
|
|
1196
|
+
//#region src/infra/compose-parser.ts
|
|
1164
1197
|
/**
|
|
1165
1198
|
* Detect the service type from the image name.
|
|
1166
1199
|
*/
|
|
@@ -1233,7 +1266,7 @@ function parseComposeFile(filePath) {
|
|
|
1233
1266
|
};
|
|
1234
1267
|
}
|
|
1235
1268
|
//#endregion
|
|
1236
|
-
//#region src/
|
|
1269
|
+
//#region src/infra/orchestrator.ts
|
|
1237
1270
|
/**
|
|
1238
1271
|
* Orchestrator for test infrastructure.
|
|
1239
1272
|
* Integration: starts services via testcontainers.
|
|
@@ -1442,7 +1475,7 @@ var Orchestrator = class {
|
|
|
1442
1475
|
}
|
|
1443
1476
|
};
|
|
1444
1477
|
//#endregion
|
|
1445
|
-
//#region src/
|
|
1478
|
+
//#region src/spec/resolve.ts
|
|
1446
1479
|
/**
|
|
1447
1480
|
* Resolve root — if relative, resolves from the caller's directory.
|
|
1448
1481
|
*/
|
|
@@ -1456,7 +1489,7 @@ function resolveProjectRoot(root) {
|
|
|
1456
1489
|
const match = line.match(/at\s+(?:.*?\()?(?:file:\/\/)?([^:)]+):\d+:\d+/);
|
|
1457
1490
|
if (!match) continue;
|
|
1458
1491
|
const filePath = match[1];
|
|
1459
|
-
if (filePath.includes("node_modules") || filePath.includes("/src/
|
|
1492
|
+
if (filePath.includes("node_modules") || filePath.includes("/src/spec/")) continue;
|
|
1460
1493
|
return (0, node_path.resolve)(filePath, "..", root);
|
|
1461
1494
|
}
|
|
1462
1495
|
}
|
|
@@ -1474,7 +1507,7 @@ function resolveCommand(command, root) {
|
|
|
1474
1507
|
return command;
|
|
1475
1508
|
}
|
|
1476
1509
|
//#endregion
|
|
1477
|
-
//#region src/
|
|
1510
|
+
//#region src/spec/spec.ts
|
|
1478
1511
|
/**
|
|
1479
1512
|
* Create a specification runner for the given target.
|
|
1480
1513
|
*
|
|
@@ -1522,12 +1555,16 @@ async function startApp(target, options) {
|
|
|
1522
1555
|
const key = svc.composeName ?? svc.type;
|
|
1523
1556
|
servicesMap[key] = svc;
|
|
1524
1557
|
}
|
|
1525
|
-
const
|
|
1558
|
+
const factoryResult = target.factory(servicesMap);
|
|
1559
|
+
const isComposite = Array.isArray(factoryResult.jobs);
|
|
1560
|
+
const honoApp = isComposite ? factoryResult.server : factoryResult;
|
|
1561
|
+
const jobs = isComposite ? factoryResult.jobs : void 0;
|
|
1526
1562
|
const database = orchestrator.getDatabase() ?? void 0;
|
|
1527
1563
|
const databases = orchestrator.getDatabases();
|
|
1528
1564
|
const runner = createSpecificationRunner({
|
|
1529
1565
|
database,
|
|
1530
1566
|
databases: databases.size > 0 ? databases : void 0,
|
|
1567
|
+
jobs,
|
|
1531
1568
|
server: new HonoAdapter(honoApp)
|
|
1532
1569
|
});
|
|
1533
1570
|
runner.cleanup = async () => {
|
|
@@ -1596,7 +1633,7 @@ async function startCommand(target, options) {
|
|
|
1596
1633
|
return runner;
|
|
1597
1634
|
}
|
|
1598
1635
|
//#endregion
|
|
1599
|
-
//#region src/
|
|
1636
|
+
//#region src/spec/targets.ts
|
|
1600
1637
|
/**
|
|
1601
1638
|
* Test against an in-process Hono app. The factory receives started services
|
|
1602
1639
|
* so you can wire connection strings into your app/DI container.
|
|
@@ -1646,7 +1683,7 @@ function command(bin) {
|
|
|
1646
1683
|
};
|
|
1647
1684
|
}
|
|
1648
1685
|
//#endregion
|
|
1649
|
-
//#region src/
|
|
1686
|
+
//#region src/spec/legacy-cli.ts
|
|
1650
1687
|
/**
|
|
1651
1688
|
* Create a CLI specification runner.
|
|
1652
1689
|
* Runs CLI commands against fixture projects. Optionally starts infrastructure.
|
|
@@ -1681,7 +1718,7 @@ async function cli(options) {
|
|
|
1681
1718
|
return runner;
|
|
1682
1719
|
}
|
|
1683
1720
|
//#endregion
|
|
1684
|
-
//#region src/
|
|
1721
|
+
//#region src/spec/legacy-e2e.ts
|
|
1685
1722
|
/**
|
|
1686
1723
|
* Create an E2E specification runner.
|
|
1687
1724
|
* Starts full docker compose stack. App URL and database auto-detected.
|
|
@@ -1707,7 +1744,7 @@ async function e2e(options = {}) {
|
|
|
1707
1744
|
return runner;
|
|
1708
1745
|
}
|
|
1709
1746
|
//#endregion
|
|
1710
|
-
//#region src/
|
|
1747
|
+
//#region src/spec/legacy-integration.ts
|
|
1711
1748
|
/**
|
|
1712
1749
|
* Create an integration specification runner.
|
|
1713
1750
|
* Starts infra containers via testcontainers, app runs in-process.
|