@jterrazz/test 3.3.0 → 3.4.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.
- package/dist/index.cjs +79 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +80 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -118,6 +118,15 @@ interface CommandResult {
|
|
|
118
118
|
stdout: string;
|
|
119
119
|
stderr: string;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Options for spawning a long-running process.
|
|
123
|
+
*/
|
|
124
|
+
interface SpawnOptions {
|
|
125
|
+
/** Resolve when stdout/stderr contains this string. */
|
|
126
|
+
waitFor: string;
|
|
127
|
+
/** Kill the process after this many milliseconds. */
|
|
128
|
+
timeout: number;
|
|
129
|
+
}
|
|
121
130
|
/**
|
|
122
131
|
* Abstract CLI interface for specification runners.
|
|
123
132
|
* Implement this to plug in your command execution strategy.
|
|
@@ -125,6 +134,8 @@ interface CommandResult {
|
|
|
125
134
|
interface CommandPort {
|
|
126
135
|
/** Execute a CLI command with the given arguments in the given working directory. */
|
|
127
136
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
137
|
+
/** Spawn a long-running process and wait for a pattern or timeout. */
|
|
138
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
128
139
|
}
|
|
129
140
|
//#endregion
|
|
130
141
|
//#region src/specification/ports/server.port.d.ts
|
|
@@ -200,6 +211,7 @@ declare class SpecificationBuilder {
|
|
|
200
211
|
private projectName;
|
|
201
212
|
private request;
|
|
202
213
|
private seeds;
|
|
214
|
+
private spawnConfig;
|
|
203
215
|
private testDir;
|
|
204
216
|
constructor(config: SpecificationConfig, testDir: string, label: string);
|
|
205
217
|
seed(file: string, options?: {
|
|
@@ -212,7 +224,8 @@ declare class SpecificationBuilder {
|
|
|
212
224
|
post(path: string, bodyFile?: string): this;
|
|
213
225
|
put(path: string, bodyFile?: string): this;
|
|
214
226
|
delete(path: string): this;
|
|
215
|
-
exec(args: string): this;
|
|
227
|
+
exec(args: string | string[]): this;
|
|
228
|
+
spawn(args: string, options: SpawnOptions): this;
|
|
216
229
|
run(): Promise<SpecificationResult>;
|
|
217
230
|
private prepareWorkDir;
|
|
218
231
|
private runHttpAction;
|
|
@@ -289,13 +302,14 @@ declare function redis(options?: RedisOptions): RedisHandle;
|
|
|
289
302
|
//#endregion
|
|
290
303
|
//#region src/specification/adapters/exec.adapter.d.ts
|
|
291
304
|
/**
|
|
292
|
-
* Executes CLI commands via execSync.
|
|
305
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
293
306
|
* Used by cli() for local command execution.
|
|
294
307
|
*/
|
|
295
308
|
declare class ExecAdapter implements CommandPort {
|
|
296
309
|
private command;
|
|
297
310
|
constructor(command: string);
|
|
298
311
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
312
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
299
313
|
}
|
|
300
314
|
//#endregion
|
|
301
315
|
//#region src/specification/adapters/fetch.adapter.d.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,15 @@ interface CommandResult {
|
|
|
118
118
|
stdout: string;
|
|
119
119
|
stderr: string;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Options for spawning a long-running process.
|
|
123
|
+
*/
|
|
124
|
+
interface SpawnOptions {
|
|
125
|
+
/** Resolve when stdout/stderr contains this string. */
|
|
126
|
+
waitFor: string;
|
|
127
|
+
/** Kill the process after this many milliseconds. */
|
|
128
|
+
timeout: number;
|
|
129
|
+
}
|
|
121
130
|
/**
|
|
122
131
|
* Abstract CLI interface for specification runners.
|
|
123
132
|
* Implement this to plug in your command execution strategy.
|
|
@@ -125,6 +134,8 @@ interface CommandResult {
|
|
|
125
134
|
interface CommandPort {
|
|
126
135
|
/** Execute a CLI command with the given arguments in the given working directory. */
|
|
127
136
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
137
|
+
/** Spawn a long-running process and wait for a pattern or timeout. */
|
|
138
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
128
139
|
}
|
|
129
140
|
//#endregion
|
|
130
141
|
//#region src/specification/ports/server.port.d.ts
|
|
@@ -200,6 +211,7 @@ declare class SpecificationBuilder {
|
|
|
200
211
|
private projectName;
|
|
201
212
|
private request;
|
|
202
213
|
private seeds;
|
|
214
|
+
private spawnConfig;
|
|
203
215
|
private testDir;
|
|
204
216
|
constructor(config: SpecificationConfig, testDir: string, label: string);
|
|
205
217
|
seed(file: string, options?: {
|
|
@@ -212,7 +224,8 @@ declare class SpecificationBuilder {
|
|
|
212
224
|
post(path: string, bodyFile?: string): this;
|
|
213
225
|
put(path: string, bodyFile?: string): this;
|
|
214
226
|
delete(path: string): this;
|
|
215
|
-
exec(args: string): this;
|
|
227
|
+
exec(args: string | string[]): this;
|
|
228
|
+
spawn(args: string, options: SpawnOptions): this;
|
|
216
229
|
run(): Promise<SpecificationResult>;
|
|
217
230
|
private prepareWorkDir;
|
|
218
231
|
private runHttpAction;
|
|
@@ -289,13 +302,14 @@ declare function redis(options?: RedisOptions): RedisHandle;
|
|
|
289
302
|
//#endregion
|
|
290
303
|
//#region src/specification/adapters/exec.adapter.d.ts
|
|
291
304
|
/**
|
|
292
|
-
* Executes CLI commands via execSync.
|
|
305
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
293
306
|
* Used by cli() for local command execution.
|
|
294
307
|
*/
|
|
295
308
|
declare class ExecAdapter implements CommandPort {
|
|
296
309
|
private command;
|
|
297
310
|
constructor(command: string);
|
|
298
311
|
exec(args: string, cwd: string): Promise<CommandResult>;
|
|
312
|
+
spawn(args: string, cwd: string, options: SpawnOptions): Promise<CommandResult>;
|
|
299
313
|
}
|
|
300
314
|
//#endregion
|
|
301
315
|
//#region src/specification/adapters/fetch.adapter.d.ts
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import MockDatePackage from "mockdate";
|
|
|
4
4
|
import { mockDeep } from "vitest-mock-extended";
|
|
5
5
|
import { cpSync, existsSync, mkdtempSync, readFileSync } from "node:fs";
|
|
6
6
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
7
|
-
import { execSync } from "node:child_process";
|
|
7
|
+
import { execSync, spawn } from "node:child_process";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
//#region src/mocking/mock-of-date.ts
|
|
10
10
|
const mockOfDate = MockDatePackage;
|
|
@@ -4899,7 +4899,7 @@ var Orchestrator = class {
|
|
|
4899
4899
|
//#endregion
|
|
4900
4900
|
//#region src/specification/adapters/exec.adapter.ts
|
|
4901
4901
|
/**
|
|
4902
|
-
* Executes CLI commands via execSync.
|
|
4902
|
+
* Executes CLI commands via execSync (blocking) or spawn (long-running).
|
|
4903
4903
|
* Used by cli() for local command execution.
|
|
4904
4904
|
*/
|
|
4905
4905
|
var ExecAdapter = class {
|
|
@@ -4908,12 +4908,17 @@ var ExecAdapter = class {
|
|
|
4908
4908
|
this.command = command;
|
|
4909
4909
|
}
|
|
4910
4910
|
async exec(args, cwd) {
|
|
4911
|
+
const env = {
|
|
4912
|
+
...process.env,
|
|
4913
|
+
INIT_CWD: void 0
|
|
4914
|
+
};
|
|
4911
4915
|
try {
|
|
4912
4916
|
return {
|
|
4913
4917
|
exitCode: 0,
|
|
4914
4918
|
stdout: execSync(`${this.command} ${args}`, {
|
|
4915
4919
|
cwd,
|
|
4916
4920
|
encoding: "utf8",
|
|
4921
|
+
env,
|
|
4917
4922
|
stdio: [
|
|
4918
4923
|
"pipe",
|
|
4919
4924
|
"pipe",
|
|
@@ -4930,6 +4935,55 @@ var ExecAdapter = class {
|
|
|
4930
4935
|
};
|
|
4931
4936
|
}
|
|
4932
4937
|
}
|
|
4938
|
+
async spawn(args, cwd, options) {
|
|
4939
|
+
const env = {
|
|
4940
|
+
...process.env,
|
|
4941
|
+
INIT_CWD: void 0
|
|
4942
|
+
};
|
|
4943
|
+
return new Promise((resolve) => {
|
|
4944
|
+
let stdout = "";
|
|
4945
|
+
let stderr = "";
|
|
4946
|
+
let resolved = false;
|
|
4947
|
+
const child = spawn(this.command, args.split(/\s+/).filter(Boolean), {
|
|
4948
|
+
cwd,
|
|
4949
|
+
env,
|
|
4950
|
+
stdio: [
|
|
4951
|
+
"pipe",
|
|
4952
|
+
"pipe",
|
|
4953
|
+
"pipe"
|
|
4954
|
+
]
|
|
4955
|
+
});
|
|
4956
|
+
const finish = (exitCode) => {
|
|
4957
|
+
if (resolved) return;
|
|
4958
|
+
resolved = true;
|
|
4959
|
+
child.kill("SIGTERM");
|
|
4960
|
+
resolve({
|
|
4961
|
+
exitCode,
|
|
4962
|
+
stdout,
|
|
4963
|
+
stderr
|
|
4964
|
+
});
|
|
4965
|
+
};
|
|
4966
|
+
let patternMatched = false;
|
|
4967
|
+
const checkPattern = () => {
|
|
4968
|
+
if (!patternMatched && (stdout.includes(options.waitFor) || stderr.includes(options.waitFor))) {
|
|
4969
|
+
patternMatched = true;
|
|
4970
|
+
finish(0);
|
|
4971
|
+
}
|
|
4972
|
+
};
|
|
4973
|
+
child.stdout?.on("data", (data) => {
|
|
4974
|
+
stdout += data.toString();
|
|
4975
|
+
checkPattern();
|
|
4976
|
+
});
|
|
4977
|
+
child.stderr?.on("data", (data) => {
|
|
4978
|
+
stderr += data.toString();
|
|
4979
|
+
checkPattern();
|
|
4980
|
+
});
|
|
4981
|
+
child.on("exit", (code) => {
|
|
4982
|
+
if (!patternMatched) finish(code === 0 ? 1 : code ?? 1);
|
|
4983
|
+
});
|
|
4984
|
+
setTimeout(() => finish(124), options.timeout);
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4933
4987
|
};
|
|
4934
4988
|
//#endregion
|
|
4935
4989
|
//#region src/specification/adapters/fetch.adapter.ts
|
|
@@ -5088,6 +5142,7 @@ var SpecificationBuilder = class {
|
|
|
5088
5142
|
projectName = null;
|
|
5089
5143
|
request = null;
|
|
5090
5144
|
seeds = [];
|
|
5145
|
+
spawnConfig = null;
|
|
5091
5146
|
testDir;
|
|
5092
5147
|
constructor(config, testDir, label) {
|
|
5093
5148
|
this.config = config;
|
|
@@ -5147,11 +5202,18 @@ var SpecificationBuilder = class {
|
|
|
5147
5202
|
this.commandArgs = args;
|
|
5148
5203
|
return this;
|
|
5149
5204
|
}
|
|
5205
|
+
spawn(args, options) {
|
|
5206
|
+
this.spawnConfig = {
|
|
5207
|
+
args,
|
|
5208
|
+
options
|
|
5209
|
+
};
|
|
5210
|
+
return this;
|
|
5211
|
+
}
|
|
5150
5212
|
async run() {
|
|
5151
5213
|
const hasHttpAction = this.request !== null;
|
|
5152
|
-
const hasCliAction = this.commandArgs !== null;
|
|
5214
|
+
const hasCliAction = this.commandArgs !== null || this.spawnConfig !== null;
|
|
5153
5215
|
if (!hasHttpAction && !hasCliAction) throw new Error(`Specification "${this.label}": no action defined. Call .get(), .post(), .exec(), etc. before .run()`);
|
|
5154
|
-
if (hasHttpAction && hasCliAction) throw new Error(`Specification "${this.label}": cannot mix HTTP (.get/.post) and CLI (.exec) actions`);
|
|
5216
|
+
if (hasHttpAction && hasCliAction) throw new Error(`Specification "${this.label}": cannot mix HTTP (.get/.post) and CLI (.exec/.spawn) actions`);
|
|
5155
5217
|
let workDir = null;
|
|
5156
5218
|
if (hasCliAction) workDir = this.prepareWorkDir();
|
|
5157
5219
|
if (this.config.databases) for (const db of this.config.databases.values()) await db.reset();
|
|
@@ -5198,8 +5260,21 @@ var SpecificationBuilder = class {
|
|
|
5198
5260
|
}
|
|
5199
5261
|
async runCliAction(workDir) {
|
|
5200
5262
|
if (!this.config.command) throw new Error("CLI actions require a command adapter (use cli())");
|
|
5263
|
+
let commandResult;
|
|
5264
|
+
if (this.spawnConfig) commandResult = await this.config.command.spawn(this.spawnConfig.args, workDir, this.spawnConfig.options);
|
|
5265
|
+
else if (Array.isArray(this.commandArgs)) {
|
|
5266
|
+
commandResult = {
|
|
5267
|
+
exitCode: 0,
|
|
5268
|
+
stdout: "",
|
|
5269
|
+
stderr: ""
|
|
5270
|
+
};
|
|
5271
|
+
for (const args of this.commandArgs) {
|
|
5272
|
+
commandResult = await this.config.command.exec(args, workDir);
|
|
5273
|
+
if (commandResult.exitCode !== 0) break;
|
|
5274
|
+
}
|
|
5275
|
+
} else commandResult = await this.config.command.exec(this.commandArgs, workDir);
|
|
5201
5276
|
return new SpecificationResult({
|
|
5202
|
-
commandResult
|
|
5277
|
+
commandResult,
|
|
5203
5278
|
config: this.config,
|
|
5204
5279
|
testDir: this.testDir,
|
|
5205
5280
|
workDir
|