@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 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
- text = text.replace(/\r?\n/g, " ").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
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(text, availableWidth, indent) : wrapByWord(text, availableWidth, indent);
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, " ").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
190
- const lines = [];
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
- lines.push(this.applyPadding(buf, vis, width, align, padChar));
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
- lines.push(this.applyPadding(buf, vis, width, align, padChar));
212
+ result.push(this.applyPadding(buf, vis, width, align, padChar));
213
213
  }
214
- return lines;
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 availableWidth = this.lineWidth - prefix.length;
709
- let displayName = suiteName;
710
- const suiteNameLength = displayName.replace(/\.\.\.$/, "").length + (displayName.includes("...") ? 3 : 0);
711
- const dotsLength = this.countVisualDots(displayDots);
712
- let padding = " ".repeat(Math.max(0, availableWidth - suiteNameLength - dotsLength));
713
- this.print(prefix + this.colored("brightBlue", displayName) + padding + displayDots);
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
- text = text.replace(/\r?\n/g, " ").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
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(text, availableWidth, indent) : wrapByWord(text, availableWidth, indent);
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, " ").replace(/[\uFEFF\xA0\t]/g, " ").replace(/\s{2,}/g, " ").trim();
1265
- const lines = [];
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
- lines.push(this.applyPadding(buf, vis, width, align, padChar));
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
- lines.push(this.applyPadding(buf, vis, width, align, padChar));
1334
+ result.push(this.applyPadding(buf, vis, width, align, padChar));
1288
1335
  }
1289
- return lines;
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epikodelabs/testify",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Serve and run your Jasmine specs in a browser",
5
5
  "type": "module",
6
6
  "bin": {