@epikodelabs/testify 1.0.18 → 1.0.20
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 +4 -1
- package/bin/jasmine +7 -7
- package/bin/testify +73 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# testify
|
|
2
2
|
|
|
3
|
-
A flexible test runner for Jasmine that supports multiple execution environments with built-in TypeScript compilation, hot module reloading, and code coverage.
|
|
3
|
+
A flexible test runner for the Jasmine testing framework that supports multiple execution environments with built-in TypeScript compilation, hot module reloading, and code coverage.
|
|
4
4
|
|
|
5
5
|
> ⚖️ “*testify doesn't mock the browser. It invites the browser into the courtroom and asks it to testify under oath.*”
|
|
6
6
|
|
|
@@ -337,6 +337,9 @@ Or manually add this configuration:
|
|
|
337
337
|
],
|
|
338
338
|
"program": "${workspaceFolder}/node_modules/@epikodelabs/testify/bin/jasmine",
|
|
339
339
|
"args": ["--spec", "${file}"],
|
|
340
|
+
"env": {
|
|
341
|
+
"TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json"
|
|
342
|
+
},
|
|
340
343
|
"cwd": "${workspaceFolder}",
|
|
341
344
|
"console": "integratedTerminal",
|
|
342
345
|
"skipFiles": ["<node_internals>/**"]
|
package/bin/jasmine
CHANGED
|
@@ -68,12 +68,12 @@ const __vitePreload = function preload(baseModule, deps, importerUrl) {
|
|
|
68
68
|
const MAX_WIDTH = 63;
|
|
69
69
|
const ANSI_FULL_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
70
70
|
function wrapLine(text, width, indentation = 0, mode = "char") {
|
|
71
|
-
|
|
71
|
+
const normalized = text.replace(/\r?\n/g, "").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
72
72
|
const indent = " ".repeat(indentation);
|
|
73
73
|
const indentWidth = indent.length;
|
|
74
74
|
if (width <= indentWidth) width = indentWidth + 1;
|
|
75
75
|
const availableWidth = width - indentWidth;
|
|
76
|
-
return mode === "char" ? wrapByChar(
|
|
76
|
+
return mode === "char" ? wrapByChar(normalized, availableWidth, indent) : wrapByWord(normalized, availableWidth, indent);
|
|
77
77
|
}
|
|
78
78
|
function wrapByChar(text, available, indent) {
|
|
79
79
|
const lines = [];
|
|
@@ -186,8 +186,8 @@ class Logger {
|
|
|
186
186
|
// ─── REFORMAT (RESTORED) ─────────────────────────────────
|
|
187
187
|
reformat(text, opts) {
|
|
188
188
|
const { width, align = "left", padChar = " " } = opts;
|
|
189
|
-
const normalized = text.replace(/\r?\n/g, "
|
|
190
|
-
|
|
189
|
+
const normalized = text.replace(/\r?\n/g, "").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
190
|
+
let result = [];
|
|
191
191
|
let buf = "";
|
|
192
192
|
let vis = 0;
|
|
193
193
|
const tokens = normalized.split(
|
|
@@ -200,7 +200,7 @@ class Logger {
|
|
|
200
200
|
}
|
|
201
201
|
for (const ch of [...token]) {
|
|
202
202
|
if (vis >= width) {
|
|
203
|
-
|
|
203
|
+
result.push(this.applyPadding(buf, vis, width, align, padChar));
|
|
204
204
|
buf = "";
|
|
205
205
|
vis = 0;
|
|
206
206
|
}
|
|
@@ -209,9 +209,9 @@ class Logger {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
if (buf) {
|
|
212
|
-
|
|
212
|
+
result.push(this.applyPadding(buf, vis, width, align, padChar));
|
|
213
213
|
}
|
|
214
|
-
return
|
|
214
|
+
return result;
|
|
215
215
|
}
|
|
216
216
|
applyPadding(text, visible, width, align, padChar) {
|
|
217
217
|
const pad = Math.max(0, width - visible);
|
package/bin/testify
CHANGED
|
@@ -705,12 +705,59 @@ class ConsoleReporter {
|
|
|
705
705
|
const suiteName = suite.description;
|
|
706
706
|
let displayDots = this.getSpecDots(suite);
|
|
707
707
|
const prefix = " ";
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
let
|
|
713
|
-
|
|
708
|
+
const maxWidth = this.lineWidth;
|
|
709
|
+
const rawSuiteName = suiteName.replace(/\x1b\[[0-9;]*m/g, "");
|
|
710
|
+
let visibleNameLength = rawSuiteName.length;
|
|
711
|
+
let dotsLength = this.countVisualDots(displayDots);
|
|
712
|
+
let availableWidth = maxWidth - prefix.length;
|
|
713
|
+
let displayName = rawSuiteName;
|
|
714
|
+
if (visibleNameLength + dotsLength > availableWidth) {
|
|
715
|
+
let maxNameLen = Math.max(0, availableWidth - dotsLength);
|
|
716
|
+
if (maxNameLen > 0) {
|
|
717
|
+
if (visibleNameLength > maxNameLen) {
|
|
718
|
+
displayName = rawSuiteName.slice(0, Math.max(0, maxNameLen - 1)) + "…";
|
|
719
|
+
visibleNameLength = displayName.length;
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
displayName = "";
|
|
723
|
+
visibleNameLength = 0;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (visibleNameLength + dotsLength > availableWidth) {
|
|
727
|
+
let maxDotsLen = Math.max(0, availableWidth - visibleNameLength);
|
|
728
|
+
let plainDots = displayDots.replace(/\x1b\[[0-9;]*m/g, "");
|
|
729
|
+
[...displayDots.matchAll(/\x1b\[[0-9;]*m/g)];
|
|
730
|
+
let resultDots = "";
|
|
731
|
+
let i = plainDots.length - maxDotsLen;
|
|
732
|
+
if (i < 0) i = 0;
|
|
733
|
+
let skip = i;
|
|
734
|
+
let vis = 0;
|
|
735
|
+
for (let j = 0, k = 0; j < displayDots.length && vis < maxDotsLen; ) {
|
|
736
|
+
if (displayDots[j] === "\x1B") {
|
|
737
|
+
let ansiSeq = displayDots.slice(j).match(/^\x1b\[[0-9;]*m/);
|
|
738
|
+
if (ansiSeq) {
|
|
739
|
+
resultDots += ansiSeq[0];
|
|
740
|
+
j += ansiSeq[0].length;
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (skip > 0) {
|
|
745
|
+
if (displayDots[j] !== "\x1B") {
|
|
746
|
+
skip--;
|
|
747
|
+
}
|
|
748
|
+
j++;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
resultDots += displayDots[j];
|
|
752
|
+
vis++;
|
|
753
|
+
j++;
|
|
754
|
+
}
|
|
755
|
+
displayDots = resultDots;
|
|
756
|
+
dotsLength = this.countVisualDots(displayDots);
|
|
757
|
+
}
|
|
758
|
+
let minSpace = " ";
|
|
759
|
+
const spaces = " ".repeat(Math.max(0, maxWidth - prefix.length - visibleNameLength - dotsLength));
|
|
760
|
+
this.print(prefix + this.colored("brightBlue", displayName) + minSpace + spaces + displayDots);
|
|
714
761
|
if (!isFinal) {
|
|
715
762
|
this.print("\r");
|
|
716
763
|
}
|
|
@@ -1143,12 +1190,12 @@ class ConsoleReporter {
|
|
|
1143
1190
|
}
|
|
1144
1191
|
const ANSI_FULL_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
1145
1192
|
function wrapLine(text, width, indentation = 0, mode = "char") {
|
|
1146
|
-
|
|
1193
|
+
const normalized = text.replace(/\r?\n/g, "").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
1147
1194
|
const indent = " ".repeat(indentation);
|
|
1148
1195
|
const indentWidth = indent.length;
|
|
1149
1196
|
if (width <= indentWidth) width = indentWidth + 1;
|
|
1150
1197
|
const availableWidth = width - indentWidth;
|
|
1151
|
-
return mode === "char" ? wrapByChar(
|
|
1198
|
+
return mode === "char" ? wrapByChar(normalized, availableWidth, indent) : wrapByWord(normalized, availableWidth, indent);
|
|
1152
1199
|
}
|
|
1153
1200
|
function wrapByChar(text, available, indent) {
|
|
1154
1201
|
const lines = [];
|
|
@@ -1261,8 +1308,8 @@ class Logger {
|
|
|
1261
1308
|
// ─── REFORMAT (RESTORED) ─────────────────────────────────
|
|
1262
1309
|
reformat(text, opts) {
|
|
1263
1310
|
const { width, align = "left", padChar = " " } = opts;
|
|
1264
|
-
const normalized = text.replace(/\r?\n/g, "
|
|
1265
|
-
|
|
1311
|
+
const normalized = text.replace(/\r?\n/g, "").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
1312
|
+
let result = [];
|
|
1266
1313
|
let buf = "";
|
|
1267
1314
|
let vis = 0;
|
|
1268
1315
|
const tokens = normalized.split(
|
|
@@ -1275,7 +1322,7 @@ class Logger {
|
|
|
1275
1322
|
}
|
|
1276
1323
|
for (const ch of [...token]) {
|
|
1277
1324
|
if (vis >= width) {
|
|
1278
|
-
|
|
1325
|
+
result.push(this.applyPadding(buf, vis, width, align, padChar));
|
|
1279
1326
|
buf = "";
|
|
1280
1327
|
vis = 0;
|
|
1281
1328
|
}
|
|
@@ -1284,9 +1331,9 @@ class Logger {
|
|
|
1284
1331
|
}
|
|
1285
1332
|
}
|
|
1286
1333
|
if (buf) {
|
|
1287
|
-
|
|
1334
|
+
result.push(this.applyPadding(buf, vis, width, align, padChar));
|
|
1288
1335
|
}
|
|
1289
|
-
return
|
|
1336
|
+
return result;
|
|
1290
1337
|
}
|
|
1291
1338
|
applyPadding(text, visible, width, align, padChar) {
|
|
1292
1339
|
const pad = Math.max(0, width - visible);
|
|
@@ -1501,6 +1548,8 @@ class BrowserManager {
|
|
|
1501
1548
|
});
|
|
1502
1549
|
}
|
|
1503
1550
|
void browser.close().catch(() => {
|
|
1551
|
+
}).finally(() => {
|
|
1552
|
+
process.exit(0);
|
|
1504
1553
|
});
|
|
1505
1554
|
};
|
|
1506
1555
|
const sigintHandler = () => abortRun();
|
|
@@ -2947,6 +2996,17 @@ export async function runTests(reporter) {
|
|
|
2947
2996
|
process.exit(1);
|
|
2948
2997
|
});
|
|
2949
2998
|
|
|
2999
|
+
// Only attach SIGINT/SIGTERM handlers if running as CLI entry
|
|
3000
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
3001
|
+
function onExit(signal) {
|
|
3002
|
+
console.log(\`
|
|
3003
|
+
⚙️ Caught \${signal}. Cleaning up...\`);
|
|
3004
|
+
process.exit(0);
|
|
3005
|
+
}
|
|
3006
|
+
process.on('SIGINT', onExit);
|
|
3007
|
+
process.on('SIGTERM', onExit);
|
|
3008
|
+
}
|
|
3009
|
+
|
|
2950
3010
|
(async function () {
|
|
2951
3011
|
try {
|
|
2952
3012
|
// Load jasmine-core from testify's own node_modules
|