@jterrazz/test 4.0.0 → 5.0.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/README.md +52 -43
- package/dist/index.cjs +49 -276
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -105
- package/dist/index.d.ts +43 -105
- package/dist/index.js +49 -277
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -220,20 +220,6 @@ function formatStartupReport(mode, services, app) {
|
|
|
220
220
|
lines.push("");
|
|
221
221
|
return lines.join("\n");
|
|
222
222
|
}
|
|
223
|
-
function formatStatusError(expectedStatus, receivedStatus, request, responseBody) {
|
|
224
|
-
const lines = [];
|
|
225
|
-
lines.push(`Expected status: ${GREEN}${expectedStatus}${RESET}`);
|
|
226
|
-
lines.push(`Received status: ${RED}${receivedStatus}${RESET}`);
|
|
227
|
-
lines.push("");
|
|
228
|
-
lines.push(`${DIM}${request.method} ${request.path}${RESET}`);
|
|
229
|
-
if (request.body) lines.push(formatJson(request.body, DIM));
|
|
230
|
-
if (responseBody) {
|
|
231
|
-
lines.push("");
|
|
232
|
-
lines.push(`${DIM}Response:${RESET}`);
|
|
233
|
-
lines.push(formatJson(responseBody, RED));
|
|
234
|
-
}
|
|
235
|
-
return lines.join("\n");
|
|
236
|
-
}
|
|
237
223
|
function formatTableDiff(table, columns, expected, actual) {
|
|
238
224
|
const lines = [];
|
|
239
225
|
lines.push(`Table "${table}" mismatch`);
|
|
@@ -282,66 +268,9 @@ function formatResponseDiff(file, expected, actual) {
|
|
|
282
268
|
}
|
|
283
269
|
return lines.join("\n");
|
|
284
270
|
}
|
|
285
|
-
function formatExitCodeError(expected, received, stdout, stderr) {
|
|
286
|
-
const lines = [];
|
|
287
|
-
lines.push(`Expected exit code: ${GREEN}${expected}${RESET}`);
|
|
288
|
-
lines.push(`Received exit code: ${RED}${received}${RESET}`);
|
|
289
|
-
if (stdout.trim()) {
|
|
290
|
-
lines.push("");
|
|
291
|
-
lines.push(`${DIM}stdout:${RESET}`);
|
|
292
|
-
for (const line of stdout.trim().split("\n").slice(-15)) lines.push(` ${DIM}${line}${RESET}`);
|
|
293
|
-
}
|
|
294
|
-
if (stderr.trim()) {
|
|
295
|
-
lines.push("");
|
|
296
|
-
lines.push(`${DIM}stderr:${RESET}`);
|
|
297
|
-
for (const line of stderr.trim().split("\n").slice(-15)) lines.push(` ${RED}${line}${RESET}`);
|
|
298
|
-
}
|
|
299
|
-
return lines.join("\n");
|
|
300
|
-
}
|
|
301
|
-
function formatStdoutDiff(file, expected, actual) {
|
|
302
|
-
const lines = [];
|
|
303
|
-
lines.push(`Output mismatch (${file})`);
|
|
304
|
-
lines.push("");
|
|
305
|
-
lines.push(`${GREEN}- Expected${RESET}`);
|
|
306
|
-
lines.push(`${RED}+ Received${RESET}`);
|
|
307
|
-
lines.push("");
|
|
308
|
-
const expectedLines = expected.split("\n");
|
|
309
|
-
const actualLines = actual.split("\n");
|
|
310
|
-
const maxLines = Math.max(expectedLines.length, actualLines.length);
|
|
311
|
-
for (let i = 0; i < maxLines; i++) {
|
|
312
|
-
const exp = expectedLines[i];
|
|
313
|
-
const act = actualLines[i];
|
|
314
|
-
if (exp === act) lines.push(` ${exp}`);
|
|
315
|
-
else {
|
|
316
|
-
if (exp !== void 0) lines.push(`${GREEN}- ${exp}${RESET}`);
|
|
317
|
-
if (act !== void 0) lines.push(`${RED}+ ${act}${RESET}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return lines.join("\n");
|
|
321
|
-
}
|
|
322
|
-
function formatFileMissing(path) {
|
|
323
|
-
return `Expected file to exist: ${RED}${path}${RESET}`;
|
|
324
|
-
}
|
|
325
|
-
function formatFileUnexpected(path) {
|
|
326
|
-
return `Expected file NOT to exist: ${RED}${path}${RESET}`;
|
|
327
|
-
}
|
|
328
|
-
function formatFileContentMismatch(path, expected, actual) {
|
|
329
|
-
const lines = [];
|
|
330
|
-
lines.push(`File "${path}" does not contain expected content`);
|
|
331
|
-
lines.push("");
|
|
332
|
-
lines.push(`${GREEN}Expected to contain:${RESET}`);
|
|
333
|
-
lines.push(` ${GREEN}${expected}${RESET}`);
|
|
334
|
-
lines.push("");
|
|
335
|
-
lines.push(`${RED}Actual content (first 20 lines):${RESET}`);
|
|
336
|
-
for (const line of actual.split("\n").slice(0, 20)) lines.push(` ${DIM}${line}${RESET}`);
|
|
337
|
-
return lines.join("\n");
|
|
338
|
-
}
|
|
339
271
|
function rowLabel(n) {
|
|
340
272
|
return n === 1 ? "1 row" : `${n} rows`;
|
|
341
273
|
}
|
|
342
|
-
function formatJson(value, color) {
|
|
343
|
-
return JSON.stringify(value, null, 2).split("\n").map((line) => `${color}${line}${RESET}`).join("\n");
|
|
344
|
-
}
|
|
345
274
|
function formatRow(row) {
|
|
346
275
|
return row.map((v) => String(v ?? "null")).join(" | ");
|
|
347
276
|
}
|
|
@@ -837,205 +766,35 @@ var HonoAdapter = class {
|
|
|
837
766
|
}
|
|
838
767
|
};
|
|
839
768
|
//#endregion
|
|
840
|
-
//#region src/specification/
|
|
841
|
-
|
|
842
|
-
* Base assertion that handles .not negation.
|
|
843
|
-
* Subclasses call this.assert(condition, message, negatedMessage) for each predicate.
|
|
844
|
-
*/
|
|
845
|
-
var BaseAssertion = class {
|
|
846
|
-
negated = false;
|
|
847
|
-
get not() {
|
|
848
|
-
const clone = Object.create(Object.getPrototypeOf(this));
|
|
849
|
-
Object.assign(clone, this);
|
|
850
|
-
clone.negated = !this.negated;
|
|
851
|
-
return clone;
|
|
852
|
-
}
|
|
853
|
-
assert(condition, message, negatedMessage) {
|
|
854
|
-
if (this.negated) {
|
|
855
|
-
if (condition) throw new Error(negatedMessage);
|
|
856
|
-
} else if (!condition) throw new Error(message);
|
|
857
|
-
}
|
|
858
|
-
};
|
|
859
|
-
//#endregion
|
|
860
|
-
//#region src/specification/assertions/file.ts
|
|
861
|
-
/**
|
|
862
|
-
* Assertions on a file in the working directory.
|
|
863
|
-
* Usage: result.file("dist/index.js").toExist()
|
|
864
|
-
*/
|
|
865
|
-
var FileAssertion = class extends BaseAssertion {
|
|
866
|
-
filePath;
|
|
867
|
-
resolvedPath;
|
|
868
|
-
constructor(filePath, workDir) {
|
|
869
|
-
super();
|
|
870
|
-
this.filePath = filePath;
|
|
871
|
-
this.resolvedPath = resolve(workDir, filePath);
|
|
872
|
-
}
|
|
873
|
-
toExist() {
|
|
874
|
-
const exists = existsSync(this.resolvedPath);
|
|
875
|
-
this.assert(exists, formatFileMissing(this.filePath), formatFileUnexpected(this.filePath));
|
|
876
|
-
}
|
|
877
|
-
toContain(expected) {
|
|
878
|
-
if (!existsSync(this.resolvedPath)) {
|
|
879
|
-
if (this.negated) return;
|
|
880
|
-
throw new Error(formatFileMissing(this.filePath));
|
|
881
|
-
}
|
|
882
|
-
const content = readFileSync(this.resolvedPath, "utf8");
|
|
883
|
-
const found = content.includes(expected);
|
|
884
|
-
this.assert(found, formatFileContentMismatch(this.filePath, expected, content), `Expected file "${this.filePath}" NOT to contain "${expected}"`);
|
|
885
|
-
}
|
|
886
|
-
toMatch(pattern) {
|
|
887
|
-
if (!existsSync(this.resolvedPath)) {
|
|
888
|
-
if (this.negated) return;
|
|
889
|
-
throw new Error(formatFileMissing(this.filePath));
|
|
890
|
-
}
|
|
891
|
-
const content = readFileSync(this.resolvedPath, "utf8");
|
|
892
|
-
const found = pattern.test(content);
|
|
893
|
-
this.assert(found, `Expected file "${this.filePath}" to match: ${pattern}\n\nActual content:\n${content.slice(0, 500)}`, `Expected file "${this.filePath}" NOT to match: ${pattern}`);
|
|
894
|
-
}
|
|
895
|
-
};
|
|
896
|
-
//#endregion
|
|
897
|
-
//#region src/specification/assertions/response.ts
|
|
898
|
-
/**
|
|
899
|
-
* Assertions on an HTTP response body.
|
|
900
|
-
* Usage: result.response.toMatchFile("expected.json")
|
|
901
|
-
*/
|
|
902
|
-
var ResponseAssertion = class extends BaseAssertion {
|
|
903
|
-
body;
|
|
904
|
-
testDir;
|
|
905
|
-
constructor(body, testDir) {
|
|
906
|
-
super();
|
|
907
|
-
this.body = body;
|
|
908
|
-
this.testDir = testDir;
|
|
909
|
-
}
|
|
910
|
-
toMatchFile(file) {
|
|
911
|
-
const expected = JSON.parse(readFileSync(resolve(this.testDir, "responses", file), "utf8"));
|
|
912
|
-
const match = JSON.stringify(this.body) === JSON.stringify(expected);
|
|
913
|
-
this.assert(match, formatResponseDiff(file, expected, this.body), `Expected response NOT to match file "${file}", but it did`);
|
|
914
|
-
}
|
|
915
|
-
toContain(subset) {
|
|
916
|
-
const bodyStr = JSON.stringify(this.body);
|
|
917
|
-
const subsetStr = JSON.stringify(subset);
|
|
918
|
-
const bodyObj = typeof this.body === "object" && this.body !== null ? this.body : {};
|
|
919
|
-
const match = Object.entries(subset).every(([key, value]) => JSON.stringify(bodyObj[key]) === JSON.stringify(value));
|
|
920
|
-
this.assert(match, `Expected response to contain: ${subsetStr}\n\nActual response:\n${bodyStr}`, `Expected response NOT to contain: ${subsetStr}`);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
//#endregion
|
|
924
|
-
//#region src/specification/assertions/string.ts
|
|
925
|
-
/**
|
|
926
|
-
* Assertions on a string (stdout, stderr, response body).
|
|
927
|
-
* Usage: result.stdout.toContain("hello")
|
|
928
|
-
*/
|
|
929
|
-
var StringAssertion = class extends BaseAssertion {
|
|
930
|
-
actual;
|
|
931
|
-
label;
|
|
932
|
-
testDir;
|
|
933
|
-
constructor(actual, label, testDir) {
|
|
934
|
-
super();
|
|
935
|
-
this.actual = actual;
|
|
936
|
-
this.label = label;
|
|
937
|
-
this.testDir = testDir;
|
|
938
|
-
}
|
|
939
|
-
toContain(expected, options) {
|
|
940
|
-
if (options?.near) {
|
|
941
|
-
const found = this.containsNear(expected, options.near);
|
|
942
|
-
this.assert(found, `Expected ${this.label} to contain "${expected}" near "${options.near}"\n\n${this.label}:\n${this.truncate(this.actual)}`, `Expected ${this.label} NOT to contain "${expected}" near "${options.near}", but it was found`);
|
|
943
|
-
} else {
|
|
944
|
-
const found = this.actual.includes(expected);
|
|
945
|
-
this.assert(found, `Expected ${this.label} to contain: "${expected}"\n\nActual ${this.label}:\n${this.truncate(this.actual)}`, `Expected ${this.label} NOT to contain: "${expected}"`);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
toMatch(pattern) {
|
|
949
|
-
const found = pattern.test(this.actual);
|
|
950
|
-
this.assert(found, `Expected ${this.label} to match: ${pattern}\n\nActual ${this.label}:\n${this.truncate(this.actual)}`, `Expected ${this.label} NOT to match: ${pattern}`);
|
|
951
|
-
}
|
|
952
|
-
toMatchFile(file) {
|
|
953
|
-
if (!this.testDir) throw new Error("toMatchFile requires a test directory context");
|
|
954
|
-
const expected = readFileSync(resolve(this.testDir, "expected", file), "utf8").trim();
|
|
955
|
-
const actual = this.actual.trim();
|
|
956
|
-
const match = actual === expected;
|
|
957
|
-
this.assert(match, formatStdoutDiff(file, expected, actual), `Expected ${this.label} NOT to match file "${file}", but it did`);
|
|
958
|
-
}
|
|
959
|
-
toBeEmpty() {
|
|
960
|
-
const empty = this.actual.trim() === "";
|
|
961
|
-
this.assert(empty, `Expected ${this.label} to be empty\n\nActual ${this.label}:\n${this.truncate(this.actual)}`, `Expected ${this.label} NOT to be empty`);
|
|
962
|
-
}
|
|
963
|
-
containsNear(target, near, proximity = 500) {
|
|
964
|
-
const clean = this.stripAnsi(this.actual);
|
|
965
|
-
const nearLower = near.toLowerCase();
|
|
966
|
-
const targetLower = target.toLowerCase();
|
|
967
|
-
let searchFrom = 0;
|
|
968
|
-
while (true) {
|
|
969
|
-
const idx = clean.toLowerCase().indexOf(nearLower, searchFrom);
|
|
970
|
-
if (idx === -1) break;
|
|
971
|
-
const windowStart = Math.max(0, idx - proximity);
|
|
972
|
-
const windowEnd = Math.min(clean.length, idx + nearLower.length + proximity);
|
|
973
|
-
if (clean.substring(windowStart, windowEnd).toLowerCase().includes(targetLower)) return true;
|
|
974
|
-
searchFrom = idx + 1;
|
|
975
|
-
}
|
|
976
|
-
return false;
|
|
977
|
-
}
|
|
978
|
-
stripAnsi(str) {
|
|
979
|
-
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
980
|
-
}
|
|
981
|
-
truncate(str, maxLines = 20) {
|
|
982
|
-
const lines = str.split("\n");
|
|
983
|
-
if (lines.length <= maxLines) return str;
|
|
984
|
-
return `${lines.slice(0, maxLines).join("\n")}\n... (${lines.length - maxLines} more lines)`;
|
|
985
|
-
}
|
|
986
|
-
};
|
|
987
|
-
//#endregion
|
|
988
|
-
//#region src/specification/assertions/table.ts
|
|
989
|
-
/**
|
|
990
|
-
* Assertions on a database table.
|
|
991
|
-
* Usage: await result.table("users").toMatch({ columns: ["name"], rows: [["Alice"]] })
|
|
992
|
-
*/
|
|
993
|
-
var TableAssertion = class extends BaseAssertion {
|
|
769
|
+
//#region src/specification/specification.ts
|
|
770
|
+
var TableAssertion = class {
|
|
994
771
|
tableName;
|
|
995
772
|
db;
|
|
996
773
|
constructor(tableName, db) {
|
|
997
|
-
super();
|
|
998
774
|
this.tableName = tableName;
|
|
999
775
|
this.db = db;
|
|
1000
776
|
}
|
|
1001
777
|
async toMatch(expected) {
|
|
1002
778
|
const actual = await this.db.query(this.tableName, expected.columns);
|
|
1003
|
-
|
|
1004
|
-
this.assert(match, formatTableDiff(this.tableName, expected.columns, expected.rows, actual), `Expected table "${this.tableName}" NOT to match, but it did`);
|
|
779
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected.rows)) throw new Error(formatTableDiff(this.tableName, expected.columns, expected.rows, actual));
|
|
1005
780
|
}
|
|
1006
781
|
async toBeEmpty() {
|
|
1007
782
|
const actual = await this.db.query(this.tableName, ["*"]);
|
|
1008
|
-
|
|
1009
|
-
this.assert(empty, `Expected table "${this.tableName}" to be empty, but it has ${actual.length} rows`, `Expected table "${this.tableName}" NOT to be empty, but it is`);
|
|
783
|
+
if (actual.length !== 0) throw new Error(`Expected table "${this.tableName}" to be empty, but it has ${actual.length} rows`);
|
|
1010
784
|
}
|
|
1011
785
|
};
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
var ValueAssertion = class extends BaseAssertion {
|
|
1019
|
-
actual;
|
|
1020
|
-
label;
|
|
1021
|
-
context;
|
|
1022
|
-
constructor(actual, label, context) {
|
|
1023
|
-
super();
|
|
1024
|
-
this.actual = actual;
|
|
1025
|
-
this.label = label;
|
|
1026
|
-
this.context = context;
|
|
786
|
+
var ResponseAccessor = class {
|
|
787
|
+
body;
|
|
788
|
+
testDir;
|
|
789
|
+
constructor(body, testDir) {
|
|
790
|
+
this.body = body;
|
|
791
|
+
this.testDir = testDir;
|
|
1027
792
|
}
|
|
1028
|
-
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
if (this.label === "exit code" && this.context?.stdout !== void 0) message = formatExitCodeError(expected, this.actual, this.context.stdout ?? "", this.context.stderr ?? "");
|
|
1032
|
-
else if (this.label === "status" && this.context?.request) message = formatStatusError(expected, this.actual, this.context.request, this.context.responseBody);
|
|
1033
|
-
else message = `Expected ${this.label}: ${expected}\nReceived ${this.label}: ${this.actual}`;
|
|
1034
|
-
this.assert(match, message, `Expected ${this.label} NOT to be ${expected}, but it was`);
|
|
793
|
+
toMatchFile(file) {
|
|
794
|
+
const expected = JSON.parse(readFileSync(resolve(this.testDir, "responses", file), "utf8"));
|
|
795
|
+
if (JSON.stringify(this.body) !== JSON.stringify(expected)) throw new Error(formatResponseDiff(file, expected, this.body));
|
|
1035
796
|
}
|
|
1036
797
|
};
|
|
1037
|
-
//#endregion
|
|
1038
|
-
//#region src/specification/specification.ts
|
|
1039
798
|
var SpecificationResult = class {
|
|
1040
799
|
commandResult;
|
|
1041
800
|
config;
|
|
@@ -1053,32 +812,34 @@ var SpecificationResult = class {
|
|
|
1053
812
|
}
|
|
1054
813
|
get exitCode() {
|
|
1055
814
|
if (!this.commandResult) throw new Error(".exitCode requires a CLI action (.exec())");
|
|
1056
|
-
return
|
|
1057
|
-
stderr: this.commandResult.stderr,
|
|
1058
|
-
stdout: this.commandResult.stdout
|
|
1059
|
-
});
|
|
815
|
+
return this.commandResult.exitCode;
|
|
1060
816
|
}
|
|
1061
817
|
get status() {
|
|
1062
|
-
if (!this.responseData
|
|
1063
|
-
return
|
|
1064
|
-
request: this.requestInfo,
|
|
1065
|
-
responseBody: this.responseData.body
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
|
-
get response() {
|
|
1069
|
-
if (!this.responseData) throw new Error(".response requires an HTTP action (.get(), .post(), etc.)");
|
|
1070
|
-
return new ResponseAssertion(this.responseData.body, this.testDir);
|
|
818
|
+
if (!this.responseData) throw new Error(".status requires an HTTP action (.get(), .post(), etc.)");
|
|
819
|
+
return this.responseData.status;
|
|
1071
820
|
}
|
|
1072
821
|
get stdout() {
|
|
1073
822
|
if (!this.commandResult) throw new Error(".stdout requires a CLI action (.exec())");
|
|
1074
|
-
return
|
|
823
|
+
return this.commandResult.stdout;
|
|
1075
824
|
}
|
|
1076
825
|
get stderr() {
|
|
1077
826
|
if (!this.commandResult) throw new Error(".stderr requires a CLI action (.exec())");
|
|
1078
|
-
return
|
|
827
|
+
return this.commandResult.stderr;
|
|
828
|
+
}
|
|
829
|
+
get response() {
|
|
830
|
+
if (!this.responseData) throw new Error(".response requires an HTTP action (.get(), .post(), etc.)");
|
|
831
|
+
return new ResponseAccessor(this.responseData.body, this.testDir);
|
|
1079
832
|
}
|
|
1080
833
|
file(path) {
|
|
1081
|
-
|
|
834
|
+
const resolvedPath = resolve(this.workDir ?? this.testDir, path);
|
|
835
|
+
const exists = existsSync(resolvedPath);
|
|
836
|
+
return {
|
|
837
|
+
get content() {
|
|
838
|
+
if (!exists) throw new Error(`File not found: ${path}`);
|
|
839
|
+
return readFileSync(resolvedPath, "utf8");
|
|
840
|
+
},
|
|
841
|
+
exists
|
|
842
|
+
};
|
|
1082
843
|
}
|
|
1083
844
|
table(tableName, options) {
|
|
1084
845
|
const db = this.resolveDatabase(options?.service);
|
|
@@ -1191,6 +952,7 @@ var SpecificationBuilder = class {
|
|
|
1191
952
|
return this.runCliAction(workDir);
|
|
1192
953
|
}
|
|
1193
954
|
prepareWorkDir() {
|
|
955
|
+
if (!this.projectName && this.fixtures.length === 0) return this.config.fixturesRoot ?? process.cwd();
|
|
1194
956
|
const tempDir = mkdtempSync(resolve(tmpdir(), "spec-cli-"));
|
|
1195
957
|
if (this.projectName && this.config.fixturesRoot) {
|
|
1196
958
|
const projectDir = resolve(this.config.fixturesRoot, this.projectName);
|
|
@@ -1222,8 +984,8 @@ var SpecificationBuilder = class {
|
|
|
1222
984
|
else if (Array.isArray(this.commandArgs)) {
|
|
1223
985
|
commandResult = {
|
|
1224
986
|
exitCode: 0,
|
|
1225
|
-
|
|
1226
|
-
|
|
987
|
+
stderr: "",
|
|
988
|
+
stdout: ""
|
|
1227
989
|
};
|
|
1228
990
|
for (const args of this.commandArgs) {
|
|
1229
991
|
commandResult = await this.config.command.exec(args, workDir);
|
|
@@ -1252,16 +1014,26 @@ function getCallerDir() {
|
|
|
1252
1014
|
}
|
|
1253
1015
|
throw new Error("Cannot detect caller directory from stack trace");
|
|
1254
1016
|
}
|
|
1255
|
-
/**
|
|
1256
|
-
* Create a specification runner.
|
|
1257
|
-
* Automatically detects the test directory from the call site.
|
|
1258
|
-
*/
|
|
1259
1017
|
function createSpecificationRunner(config) {
|
|
1260
1018
|
return (label) => {
|
|
1261
1019
|
return new SpecificationBuilder(config, getCallerDir(), label);
|
|
1262
1020
|
};
|
|
1263
1021
|
}
|
|
1264
1022
|
//#endregion
|
|
1023
|
+
//#region src/specification/grep.ts
|
|
1024
|
+
/**
|
|
1025
|
+
* Extract text blocks from output that contain a pattern.
|
|
1026
|
+
* Splits by blank lines (how linter/compiler output is structured),
|
|
1027
|
+
* returns only blocks matching the pattern.
|
|
1028
|
+
*
|
|
1029
|
+
* @example
|
|
1030
|
+
* expect(grep(result.stdout, "unused-var.ts")).toContain("no-unused-vars")
|
|
1031
|
+
* expect(grep(result.stdout, "valid/sorted.ts")).not.toContain("sort-imports")
|
|
1032
|
+
*/
|
|
1033
|
+
function grep(output, pattern) {
|
|
1034
|
+
return output.replace(/\x1b\[[0-9;]*m/g, "").split(/\n\s*\n/).filter((block) => block.includes(pattern)).join("\n\n");
|
|
1035
|
+
}
|
|
1036
|
+
//#endregion
|
|
1265
1037
|
//#region src/specification/index.ts
|
|
1266
1038
|
/**
|
|
1267
1039
|
* Resolve root — if relative, resolves from the caller's directory.
|
|
@@ -1380,6 +1152,6 @@ async function cli(options) {
|
|
|
1380
1152
|
return runner;
|
|
1381
1153
|
}
|
|
1382
1154
|
//#endregion
|
|
1383
|
-
export { ExecAdapter, FetchAdapter, HonoAdapter, Orchestrator, cli, e2e, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, stripAnsi };
|
|
1155
|
+
export { ExecAdapter, FetchAdapter, HonoAdapter, Orchestrator, cli, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, stripAnsi };
|
|
1384
1156
|
|
|
1385
1157
|
//# sourceMappingURL=index.js.map
|