qunitx-cli 0.18.0 → 0.19.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/cli.js +453 -81
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -387,9 +387,9 @@ var init_color = __esm({
|
|
|
387
387
|
|
|
388
388
|
// lib/utils/path-exists.ts
|
|
389
389
|
import fs2 from "node:fs/promises";
|
|
390
|
-
async function pathExists(
|
|
390
|
+
async function pathExists(path7) {
|
|
391
391
|
try {
|
|
392
|
-
await fs2.access(
|
|
392
|
+
await fs2.access(path7);
|
|
393
393
|
return true;
|
|
394
394
|
} catch {
|
|
395
395
|
return false;
|
|
@@ -547,7 +547,8 @@ function TAPDisplayTestResult(COUNTER, details) {
|
|
|
547
547
|
process.stdout.write(`ok ${COUNTER.testCount} ${details.fullName.join(" | ")} # skip
|
|
548
548
|
`);
|
|
549
549
|
} else if (details.status === "todo") {
|
|
550
|
-
|
|
550
|
+
COUNTER.todoCount = (COUNTER.todoCount ?? 0) + 1;
|
|
551
|
+
process.stdout.write(`not ok ${COUNTER.testCount} ${details.fullName.join(" | ")} # TODO
|
|
551
552
|
`);
|
|
552
553
|
} else if (details.status === "failed") {
|
|
553
554
|
COUNTER.failCount++;
|
|
@@ -743,8 +744,8 @@ var init_http = __esm({
|
|
|
743
744
|
});
|
|
744
745
|
}
|
|
745
746
|
/** Registers a GET route handler. */
|
|
746
|
-
get(
|
|
747
|
-
this.#registerRouteHandler("GET",
|
|
747
|
+
get(path7, handler) {
|
|
748
|
+
this.#registerRouteHandler("GET", path7, handler);
|
|
748
749
|
}
|
|
749
750
|
/**
|
|
750
751
|
* Starts listening on the given port (0 = OS-assigned).
|
|
@@ -775,30 +776,30 @@ var init_http = __esm({
|
|
|
775
776
|
});
|
|
776
777
|
}
|
|
777
778
|
/** Registers a POST route handler. */
|
|
778
|
-
post(
|
|
779
|
-
this.#registerRouteHandler("POST",
|
|
779
|
+
post(path7, handler) {
|
|
780
|
+
this.#registerRouteHandler("POST", path7, handler);
|
|
780
781
|
}
|
|
781
782
|
/** Registers a DELETE route handler. */
|
|
782
|
-
delete(
|
|
783
|
-
this.#registerRouteHandler("DELETE",
|
|
783
|
+
delete(path7, handler) {
|
|
784
|
+
this.#registerRouteHandler("DELETE", path7, handler);
|
|
784
785
|
}
|
|
785
786
|
/** Registers a PUT route handler. */
|
|
786
|
-
put(
|
|
787
|
-
this.#registerRouteHandler("PUT",
|
|
787
|
+
put(path7, handler) {
|
|
788
|
+
this.#registerRouteHandler("PUT", path7, handler);
|
|
788
789
|
}
|
|
789
790
|
/** Adds a middleware function to the chain. */
|
|
790
791
|
use(middleware) {
|
|
791
792
|
this.middleware.push(middleware);
|
|
792
793
|
}
|
|
793
|
-
#registerRouteHandler(method,
|
|
794
|
+
#registerRouteHandler(method, path7, handler) {
|
|
794
795
|
if (!this.routes[method]) {
|
|
795
796
|
this.routes[method] = {};
|
|
796
797
|
}
|
|
797
|
-
this.routes[method][
|
|
798
|
-
path:
|
|
798
|
+
this.routes[method][path7] = {
|
|
799
|
+
path: path7,
|
|
799
800
|
handler,
|
|
800
|
-
paramNames: this.#extractParamNames(
|
|
801
|
-
isWildcard:
|
|
801
|
+
paramNames: this.#extractParamNames(path7),
|
|
802
|
+
isWildcard: path7 === "/*"
|
|
802
803
|
};
|
|
803
804
|
}
|
|
804
805
|
#handleRequest(req, res) {
|
|
@@ -836,13 +837,13 @@ var init_http = __esm({
|
|
|
836
837
|
return null;
|
|
837
838
|
}
|
|
838
839
|
return routes[url] || Object.values(routes).find((route) => {
|
|
839
|
-
const { path:
|
|
840
|
-
if (!isWildcard && !
|
|
840
|
+
const { path: path7, isWildcard } = route;
|
|
841
|
+
if (!isWildcard && !path7.includes(":")) {
|
|
841
842
|
return false;
|
|
842
843
|
}
|
|
843
|
-
if (isWildcard || this.#matchPathSegments(
|
|
844
|
+
if (isWildcard || this.#matchPathSegments(path7, url)) {
|
|
844
845
|
if (route.paramNames.length > 0) {
|
|
845
|
-
const regexPattern = this.#buildRegexPattern(
|
|
846
|
+
const regexPattern = this.#buildRegexPattern(path7, route.paramNames);
|
|
846
847
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
847
848
|
const regexMatches = regex.exec(url);
|
|
848
849
|
if (regexMatches) {
|
|
@@ -854,8 +855,8 @@ var init_http = __esm({
|
|
|
854
855
|
return false;
|
|
855
856
|
}) || routes["/*"] || null;
|
|
856
857
|
}
|
|
857
|
-
#matchPathSegments(
|
|
858
|
-
const pathSegments =
|
|
858
|
+
#matchPathSegments(path7, url) {
|
|
859
|
+
const pathSegments = path7.split("/");
|
|
859
860
|
const urlSegments = url.split("/");
|
|
860
861
|
if (pathSegments.length !== urlSegments.length) {
|
|
861
862
|
return false;
|
|
@@ -872,14 +873,14 @@ var init_http = __esm({
|
|
|
872
873
|
}
|
|
873
874
|
return true;
|
|
874
875
|
}
|
|
875
|
-
#buildRegexPattern(
|
|
876
|
-
let regexPattern =
|
|
876
|
+
#buildRegexPattern(path7, _paramNames) {
|
|
877
|
+
let regexPattern = path7.replace(/:[^/]+/g, "([^/]+)");
|
|
877
878
|
regexPattern = regexPattern.replace(/\//g, "\\/");
|
|
878
879
|
return regexPattern;
|
|
879
880
|
}
|
|
880
|
-
#extractParamNames(
|
|
881
|
+
#extractParamNames(path7) {
|
|
881
882
|
const paramRegex = /:(\w+)/g;
|
|
882
|
-
const paramMatches =
|
|
883
|
+
const paramMatches = path7.match(paramRegex);
|
|
883
884
|
return paramMatches ? paramMatches.map((match) => match.slice(1)) : [];
|
|
884
885
|
}
|
|
885
886
|
#extractParams(route, _url) {
|
|
@@ -988,6 +989,23 @@ function setupWebServer(config, cachedContent) {
|
|
|
988
989
|
res.end(cachedContent.filteredTestCode);
|
|
989
990
|
});
|
|
990
991
|
server.get("/", async (_req, res) => {
|
|
992
|
+
if (cachedContent._buildError) {
|
|
993
|
+
const htmlContent2 = buildErrorHTML(cachedContent._buildError);
|
|
994
|
+
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
995
|
+
res.write(htmlContent2);
|
|
996
|
+
res.end();
|
|
997
|
+
return await fsPromise.writeFile(
|
|
998
|
+
`${config.projectRoot}/${config.output}/index.html`,
|
|
999
|
+
htmlContent2
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
if (cachedContent._noTestsWarning) {
|
|
1003
|
+
const htmlContent2 = buildNoTestsHTML(cachedContent._noTestsWarning);
|
|
1004
|
+
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
1005
|
+
res.write(htmlContent2);
|
|
1006
|
+
res.end();
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
991
1009
|
const TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
|
|
992
1010
|
const htmlContent = escapeAndInjectTestsToHTML(
|
|
993
1011
|
mainHTMLWithReplacedAssets,
|
|
@@ -1003,6 +1021,23 @@ function setupWebServer(config, cachedContent) {
|
|
|
1003
1021
|
);
|
|
1004
1022
|
});
|
|
1005
1023
|
server.get("/qunitx.html", async (_req, res) => {
|
|
1024
|
+
if (cachedContent._buildError) {
|
|
1025
|
+
const htmlContent2 = buildErrorHTML(cachedContent._buildError);
|
|
1026
|
+
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
1027
|
+
res.write(htmlContent2);
|
|
1028
|
+
res.end();
|
|
1029
|
+
return await fsPromise.writeFile(
|
|
1030
|
+
`${config.projectRoot}/${config.output}/qunitx.html`,
|
|
1031
|
+
htmlContent2
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
if (cachedContent._noTestsWarning) {
|
|
1035
|
+
const htmlContent2 = buildNoTestsHTML(cachedContent._noTestsWarning);
|
|
1036
|
+
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
1037
|
+
res.write(htmlContent2);
|
|
1038
|
+
res.end();
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1006
1041
|
const TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
|
|
1007
1042
|
const htmlContent = escapeAndInjectTestsToHTML(
|
|
1008
1043
|
mainHTMLWithReplacedAssets,
|
|
@@ -1163,7 +1198,15 @@ function testRuntimeToInject(port, config) {
|
|
|
1163
1198
|
|
|
1164
1199
|
if (!window.QUnit) {
|
|
1165
1200
|
console.log('QUnit not found after WebSocket connected');
|
|
1166
|
-
window.
|
|
1201
|
+
if (window.IS_PLAYWRIGHT) {
|
|
1202
|
+
// Signal the Playwright runner that the run is complete with 0 tests rather than
|
|
1203
|
+
// waiting for the inactivity timeout. The runner treats totalTests === 0 as a
|
|
1204
|
+
// "no tests registered" warning (not a failure), so this gives a fast, clean result.
|
|
1205
|
+
window.QUNIT_RESULT = { totalTests: 0, finishedTests: 0, failedTests: 0, currentTest: null };
|
|
1206
|
+
window.socket.send(JSON.stringify({ event: 'done', details: { passed: 0, failed: 0, runtime: 0 } }));
|
|
1207
|
+
} else {
|
|
1208
|
+
window.testTimeout = ${config.timeout};
|
|
1209
|
+
}
|
|
1167
1210
|
return;
|
|
1168
1211
|
}
|
|
1169
1212
|
|
|
@@ -1215,6 +1258,211 @@ function escapeAndInjectTestsToHTML(html, testRuntimeCode, testBundleUrl) {
|
|
|
1215
1258
|
return injectScript(html, `${testRuntimeCode}
|
|
1216
1259
|
<script src="${testBundleUrl}" async></script>`);
|
|
1217
1260
|
}
|
|
1261
|
+
function buildNoTestsHTML(files) {
|
|
1262
|
+
const escaped = files.map((f) => f.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")).join("\n");
|
|
1263
|
+
return `<!DOCTYPE html>
|
|
1264
|
+
<html lang="en">
|
|
1265
|
+
<head>
|
|
1266
|
+
<meta charset="utf-8">
|
|
1267
|
+
<meta name="viewport" content="width=device-width">
|
|
1268
|
+
<title>No Tests Registered \u2014 qunitx</title>
|
|
1269
|
+
<style>
|
|
1270
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1271
|
+
#qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-tests, #qunit-tests li {
|
|
1272
|
+
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
|
|
1273
|
+
}
|
|
1274
|
+
#qunit-header {
|
|
1275
|
+
padding: 0.5em 0 0.5em 1em;
|
|
1276
|
+
color: #C2CCD1;
|
|
1277
|
+
background-color: #0D3349;
|
|
1278
|
+
font-size: 1.5em;
|
|
1279
|
+
line-height: 1em;
|
|
1280
|
+
font-weight: 400;
|
|
1281
|
+
border-radius: 5px 5px 0 0;
|
|
1282
|
+
}
|
|
1283
|
+
#qunit-banner { height: 5px; background-color: #F0AD4E; }
|
|
1284
|
+
#qunit-userAgent {
|
|
1285
|
+
padding: 0.5em 1em;
|
|
1286
|
+
color: #fff;
|
|
1287
|
+
background-color: #EC971F;
|
|
1288
|
+
text-shadow: rgba(0,0,0,.3) 2px 2px 1px;
|
|
1289
|
+
font-size: small;
|
|
1290
|
+
}
|
|
1291
|
+
#qunit-tests { list-style: none; font-size: smaller; }
|
|
1292
|
+
#qunit-tests li.warn {
|
|
1293
|
+
display: list-item;
|
|
1294
|
+
padding: 0.4em 1em;
|
|
1295
|
+
border-bottom: 1px solid #fff;
|
|
1296
|
+
color: #000;
|
|
1297
|
+
background-color: #FCF8E3;
|
|
1298
|
+
border-left: 5px solid #F0AD4E;
|
|
1299
|
+
}
|
|
1300
|
+
#qunit-tests li.warn:last-child { border-radius: 0 0 5px 5px; }
|
|
1301
|
+
.qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #fff; border-radius: 5px; list-style: none; }
|
|
1302
|
+
.qunit-assert-list > li {
|
|
1303
|
+
padding: 5px;
|
|
1304
|
+
background-color: #FFF8DC;
|
|
1305
|
+
border-left: 10px solid #F0AD4E;
|
|
1306
|
+
color: #8A6D3B;
|
|
1307
|
+
}
|
|
1308
|
+
.qunit-assert-list pre {
|
|
1309
|
+
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
1310
|
+
font-size: 12px;
|
|
1311
|
+
line-height: 1.6;
|
|
1312
|
+
white-space: pre-wrap;
|
|
1313
|
+
word-break: break-word;
|
|
1314
|
+
color: #8A6D3B;
|
|
1315
|
+
margin: 0;
|
|
1316
|
+
}
|
|
1317
|
+
#qunit-testresult {
|
|
1318
|
+
padding: 0.5em 1em;
|
|
1319
|
+
color: #366097;
|
|
1320
|
+
background-color: #E2F0F7;
|
|
1321
|
+
border-bottom: 1px solid #fff;
|
|
1322
|
+
font-size: small;
|
|
1323
|
+
}
|
|
1324
|
+
.dots span { display: inline-block; animation: pulse 1.4s ease-in-out infinite; }
|
|
1325
|
+
.dots span:nth-child(2) { animation-delay: .2s; }
|
|
1326
|
+
.dots span:nth-child(3) { animation-delay: .4s; }
|
|
1327
|
+
@keyframes pulse { 0%,100% { opacity: .2; } 50% { opacity: 1; } }
|
|
1328
|
+
</style>
|
|
1329
|
+
</head>
|
|
1330
|
+
<body>
|
|
1331
|
+
<div id="qunit">
|
|
1332
|
+
<h1 id="qunit-header">qunitx</h1>
|
|
1333
|
+
<h2 id="qunit-banner"></h2>
|
|
1334
|
+
<div id="qunit-userAgent">Warning: No Tests Registered</div>
|
|
1335
|
+
<ol id="qunit-tests">
|
|
1336
|
+
<li class="warn">
|
|
1337
|
+
<strong>0 QUnit tests were registered in the bundled file(s)</strong>
|
|
1338
|
+
<ol class="qunit-assert-list">
|
|
1339
|
+
<li><pre>${escaped}</pre></li>
|
|
1340
|
+
</ol>
|
|
1341
|
+
</li>
|
|
1342
|
+
</ol>
|
|
1343
|
+
<div id="qunit-testresult">
|
|
1344
|
+
Watching for changes <span class="dots"><span>●</span><span>●</span><span>●</span></span>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
<script>
|
|
1348
|
+
if (location.port) {
|
|
1349
|
+
(function () {
|
|
1350
|
+
var retries = 0;
|
|
1351
|
+
function connect() {
|
|
1352
|
+
var ws = new WebSocket('ws://' + location.hostname + ':' + location.port);
|
|
1353
|
+
ws.addEventListener('message', function (e) { if (e.data === 'refresh') location.reload(true); });
|
|
1354
|
+
ws.addEventListener('close', function () { if (retries++ < 120) setTimeout(connect, 1000); });
|
|
1355
|
+
ws.addEventListener('error', function () { ws.close(); });
|
|
1356
|
+
}
|
|
1357
|
+
connect();
|
|
1358
|
+
})();
|
|
1359
|
+
}
|
|
1360
|
+
</script>
|
|
1361
|
+
</body>
|
|
1362
|
+
</html>`;
|
|
1363
|
+
}
|
|
1364
|
+
function buildErrorHTML(buildError) {
|
|
1365
|
+
const escaped = buildError.formatted.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1366
|
+
return `<!DOCTYPE html>
|
|
1367
|
+
<html lang="en">
|
|
1368
|
+
<head>
|
|
1369
|
+
<meta charset="utf-8">
|
|
1370
|
+
<meta name="viewport" content="width=device-width">
|
|
1371
|
+
<title>Build Error \u2014 qunitx</title>
|
|
1372
|
+
<style>
|
|
1373
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1374
|
+
#qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-tests, #qunit-tests li {
|
|
1375
|
+
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
|
|
1376
|
+
}
|
|
1377
|
+
#qunit-header {
|
|
1378
|
+
padding: 0.5em 0 0.5em 1em;
|
|
1379
|
+
color: #C2CCD1;
|
|
1380
|
+
background-color: #0D3349;
|
|
1381
|
+
font-size: 1.5em;
|
|
1382
|
+
line-height: 1em;
|
|
1383
|
+
font-weight: 400;
|
|
1384
|
+
border-radius: 5px 5px 0 0;
|
|
1385
|
+
}
|
|
1386
|
+
#qunit-banner { height: 5px; background-color: #EE5757; }
|
|
1387
|
+
#qunit-userAgent {
|
|
1388
|
+
padding: 0.5em 1em;
|
|
1389
|
+
color: #fff;
|
|
1390
|
+
background-color: #2B81AF;
|
|
1391
|
+
text-shadow: rgba(0,0,0,.5) 2px 2px 1px;
|
|
1392
|
+
font-size: small;
|
|
1393
|
+
}
|
|
1394
|
+
#qunit-tests { list-style: none; font-size: smaller; }
|
|
1395
|
+
#qunit-tests li.fail {
|
|
1396
|
+
display: list-item;
|
|
1397
|
+
padding: 0.4em 1em;
|
|
1398
|
+
border-bottom: 1px solid #fff;
|
|
1399
|
+
color: #000;
|
|
1400
|
+
background-color: #EE5757;
|
|
1401
|
+
}
|
|
1402
|
+
#qunit-tests li.fail:last-child { border-radius: 0 0 5px 5px; }
|
|
1403
|
+
.qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #fff; border-radius: 5px; list-style: none; }
|
|
1404
|
+
.qunit-assert-list > li {
|
|
1405
|
+
padding: 5px;
|
|
1406
|
+
background-color: #fff;
|
|
1407
|
+
border-left: 10px solid #EE5757;
|
|
1408
|
+
color: #710909;
|
|
1409
|
+
}
|
|
1410
|
+
.qunit-assert-list pre {
|
|
1411
|
+
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
1412
|
+
font-size: 12px;
|
|
1413
|
+
line-height: 1.6;
|
|
1414
|
+
white-space: pre-wrap;
|
|
1415
|
+
word-break: break-word;
|
|
1416
|
+
color: #710909;
|
|
1417
|
+
margin: 0;
|
|
1418
|
+
}
|
|
1419
|
+
#qunit-testresult {
|
|
1420
|
+
padding: 0.5em 1em;
|
|
1421
|
+
color: #366097;
|
|
1422
|
+
background-color: #E2F0F7;
|
|
1423
|
+
border-bottom: 1px solid #fff;
|
|
1424
|
+
font-size: small;
|
|
1425
|
+
}
|
|
1426
|
+
.dots span { display: inline-block; animation: pulse 1.4s ease-in-out infinite; }
|
|
1427
|
+
.dots span:nth-child(2) { animation-delay: .2s; }
|
|
1428
|
+
.dots span:nth-child(3) { animation-delay: .4s; }
|
|
1429
|
+
@keyframes pulse { 0%,100% { opacity: .2; } 50% { opacity: 1; } }
|
|
1430
|
+
</style>
|
|
1431
|
+
</head>
|
|
1432
|
+
<body>
|
|
1433
|
+
<div id="qunit">
|
|
1434
|
+
<h1 id="qunit-header">qunitx</h1>
|
|
1435
|
+
<h2 id="qunit-banner"></h2>
|
|
1436
|
+
<div id="qunit-userAgent">Build Error: ${buildError.type}</div>
|
|
1437
|
+
<ol id="qunit-tests">
|
|
1438
|
+
<li class="fail">
|
|
1439
|
+
<strong>esbuild failed to bundle test files</strong>
|
|
1440
|
+
<ol class="qunit-assert-list">
|
|
1441
|
+
<li><pre>${escaped}</pre></li>
|
|
1442
|
+
</ol>
|
|
1443
|
+
</li>
|
|
1444
|
+
</ol>
|
|
1445
|
+
<div id="qunit-testresult">
|
|
1446
|
+
Watching for changes <span class="dots"><span>●</span><span>●</span><span>●</span></span>
|
|
1447
|
+
</div>
|
|
1448
|
+
</div>
|
|
1449
|
+
<script>
|
|
1450
|
+
if (location.port) {
|
|
1451
|
+
(function () {
|
|
1452
|
+
var retries = 0;
|
|
1453
|
+
function connect() {
|
|
1454
|
+
var ws = new WebSocket('ws://' + location.hostname + ':' + location.port);
|
|
1455
|
+
ws.addEventListener('message', function (e) { if (e.data === 'refresh') location.reload(true); });
|
|
1456
|
+
ws.addEventListener('close', function () { if (retries++ < 120) setTimeout(connect, 1000); });
|
|
1457
|
+
ws.addEventListener('error', function () { ws.close(); });
|
|
1458
|
+
}
|
|
1459
|
+
connect();
|
|
1460
|
+
})();
|
|
1461
|
+
}
|
|
1462
|
+
</script>
|
|
1463
|
+
</body>
|
|
1464
|
+
</html>`;
|
|
1465
|
+
}
|
|
1218
1466
|
var fsPromise;
|
|
1219
1467
|
var init_web_server = __esm({
|
|
1220
1468
|
"lib/setup/web-server.ts"() {
|
|
@@ -1282,7 +1530,9 @@ async function setupBrowser(config, cachedContent, existingBrowser = null) {
|
|
|
1282
1530
|
perfLog(`browser.js: setupWebServer took ${Date.now() - setupStart}ms`);
|
|
1283
1531
|
const browser = resolvedExistingBrowser || await launchBrowser(config);
|
|
1284
1532
|
const pageStart = Date.now();
|
|
1285
|
-
const
|
|
1533
|
+
const isHeadedWatchMode = config.open === true && config.watch;
|
|
1534
|
+
const getPage = isHeadedWatchMode ? () => browser.contexts()[0]?.pages()[0] ?? browser.newPage() : () => browser.newPage();
|
|
1535
|
+
const [page] = await Promise.all([getPage(), bindServerToPort(server, config)]);
|
|
1286
1536
|
perfLog(`browser.js: newPage + bindServerToPort took ${Date.now() - pageStart}ms`);
|
|
1287
1537
|
await page.addInitScript(() => {
|
|
1288
1538
|
window.IS_PLAYWRIGHT = true;
|
|
@@ -1388,7 +1638,7 @@ var init_run_user_module = __esm({
|
|
|
1388
1638
|
});
|
|
1389
1639
|
|
|
1390
1640
|
// lib/tap/display-final-result.ts
|
|
1391
|
-
function TAPDisplayFinalResult({ testCount, passCount, skipCount, failCount }, timeTaken) {
|
|
1641
|
+
function TAPDisplayFinalResult({ testCount, passCount, skipCount, todoCount, failCount }, timeTaken) {
|
|
1392
1642
|
process.stdout.write("\n");
|
|
1393
1643
|
process.stdout.write(`1..${testCount}
|
|
1394
1644
|
`);
|
|
@@ -1397,6 +1647,8 @@ function TAPDisplayFinalResult({ testCount, passCount, skipCount, failCount }, t
|
|
|
1397
1647
|
process.stdout.write(`# pass ${passCount}
|
|
1398
1648
|
`);
|
|
1399
1649
|
process.stdout.write(`# skip ${skipCount}
|
|
1650
|
+
`);
|
|
1651
|
+
process.stdout.write(`# todo ${todoCount}
|
|
1400
1652
|
`);
|
|
1401
1653
|
process.stdout.write(`# fail ${failCount}
|
|
1402
1654
|
`);
|
|
@@ -1411,7 +1663,36 @@ var init_display_final_result = __esm({
|
|
|
1411
1663
|
|
|
1412
1664
|
// lib/commands/run/tests-in-browser.ts
|
|
1413
1665
|
import fs9 from "node:fs/promises";
|
|
1666
|
+
import path5 from "node:path";
|
|
1414
1667
|
import esbuild from "esbuild";
|
|
1668
|
+
function deriveBuildErrorType(error) {
|
|
1669
|
+
const msgs = error?.errors ?? [];
|
|
1670
|
+
const text = msgs[0]?.text ?? (error instanceof Error ? error.message : String(error));
|
|
1671
|
+
if (/could not resolve|cannot find module|no such file/i.test(text))
|
|
1672
|
+
return "Module Resolution Error";
|
|
1673
|
+
if (/unexpected token|expected .* but found|unterminated/i.test(text)) return "Syntax Error";
|
|
1674
|
+
if (/is not (defined|a function)|cannot read prop/i.test(text)) return "Reference Error";
|
|
1675
|
+
return "Build Error";
|
|
1676
|
+
}
|
|
1677
|
+
function formatBuildErrors(error) {
|
|
1678
|
+
const msgs = error?.errors ?? [];
|
|
1679
|
+
if (msgs.length > 0) {
|
|
1680
|
+
return msgs.map((msg, i) => {
|
|
1681
|
+
const loc = msg.location;
|
|
1682
|
+
const lineNum = loc ? String(loc.line) : "";
|
|
1683
|
+
const pad = loc ? " ".repeat(lineNum.length) : "";
|
|
1684
|
+
const locationLines = loc ? [
|
|
1685
|
+
` ${loc.file}:${loc.line}:${loc.column}`,
|
|
1686
|
+
` ${lineNum} \u2502 ${loc.lineText}`,
|
|
1687
|
+
` ${pad} \u2502 ${" ".repeat(loc.column)}${"~".repeat(Math.max(1, loc.length))}`
|
|
1688
|
+
] : [];
|
|
1689
|
+
const noteLines = msg.notes.filter((n) => n.text).map((n) => ` Note: ${n.text}`);
|
|
1690
|
+
return [`[${i + 1}] ${msg.text}`].concat(locationLines, noteLines).join("\n");
|
|
1691
|
+
}).join("\n\n");
|
|
1692
|
+
}
|
|
1693
|
+
const raw = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
1694
|
+
return raw.replace(/\x1b\[[0-9;]*[mGKH]/g, "").replace(/\r\n/g, "\n");
|
|
1695
|
+
}
|
|
1415
1696
|
async function buildTestBundle(config, cachedContent) {
|
|
1416
1697
|
const { projectRoot, output } = config;
|
|
1417
1698
|
const allTestFilePaths = Object.keys(config.fsTree);
|
|
@@ -1428,8 +1709,12 @@ async function buildTestBundle(config, cachedContent) {
|
|
|
1428
1709
|
contents: allTestFilePaths.map((filePath) => `import "${filePath}";`).join(""),
|
|
1429
1710
|
resolveDir: process.cwd()
|
|
1430
1711
|
},
|
|
1712
|
+
// Allow test files outside the project root (e.g. /tmp/my-test.ts) to import
|
|
1713
|
+
// packages from any node_modules on the ancestor chain of cwd — the same lookup
|
|
1714
|
+
// order Node itself uses when resolving require() from process.cwd().
|
|
1715
|
+
nodePaths: ancestorNodeModules(process.cwd()),
|
|
1431
1716
|
bundle: true,
|
|
1432
|
-
logLevel: "
|
|
1717
|
+
logLevel: "silent",
|
|
1433
1718
|
outfile,
|
|
1434
1719
|
keepNames: true,
|
|
1435
1720
|
legalComments: "none",
|
|
@@ -1441,26 +1726,47 @@ async function buildTestBundle(config, cachedContent) {
|
|
|
1441
1726
|
// all browsers and does not require changes to user test code.
|
|
1442
1727
|
footer: { js: 'window.dispatchEvent(new CustomEvent("qunitx:tests-ready"));' }
|
|
1443
1728
|
};
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1729
|
+
cachedContent._buildError = null;
|
|
1730
|
+
cachedContent._noTestsWarning = null;
|
|
1731
|
+
try {
|
|
1732
|
+
const [allTestCode] = await Promise.all([
|
|
1733
|
+
config.watch ? buildIncrementally(buildOptions, allTestFilePaths.join("\0"), cachedContent, needsDisk) : buildWithOverlayfsRetry(buildOptions, needsDisk),
|
|
1734
|
+
Promise.all(
|
|
1735
|
+
cachedContent.htmlPathsToRunTests.map(async (htmlPath) => {
|
|
1736
|
+
const targetPath = `${config.projectRoot}/${config.output}${htmlPath}`;
|
|
1737
|
+
if (htmlPath !== "/") {
|
|
1738
|
+
await fs9.rm(targetPath, { force: true, recursive: true });
|
|
1739
|
+
await fs9.mkdir(targetPath.split("/").slice(0, -1).join("/"), { recursive: true });
|
|
1740
|
+
}
|
|
1741
|
+
})
|
|
1742
|
+
)
|
|
1743
|
+
]);
|
|
1744
|
+
cachedContent.allTestCode = allTestCode;
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
cachedContent._buildError = {
|
|
1747
|
+
type: deriveBuildErrorType(error),
|
|
1748
|
+
formatted: formatBuildErrors(error)
|
|
1749
|
+
};
|
|
1750
|
+
await fs9.writeFile(
|
|
1751
|
+
`${projectRoot}/${output}/index.html`,
|
|
1752
|
+
buildErrorHTML(cachedContent._buildError)
|
|
1753
|
+
);
|
|
1754
|
+
throw error;
|
|
1755
|
+
}
|
|
1457
1756
|
}
|
|
1458
1757
|
async function runTestsInBrowser(config, cachedContent = {}, connections, targetTestFilesToFilter) {
|
|
1459
1758
|
const { projectRoot, output } = config;
|
|
1460
1759
|
const allTestFilePaths = Object.keys(config.fsTree);
|
|
1461
1760
|
const runHasFilter = !!targetTestFilesToFilter;
|
|
1462
1761
|
if (!config._groupMode) {
|
|
1463
|
-
config.COUNTER = {
|
|
1762
|
+
config.COUNTER = {
|
|
1763
|
+
testCount: 0,
|
|
1764
|
+
failCount: 0,
|
|
1765
|
+
skipCount: 0,
|
|
1766
|
+
todoCount: 0,
|
|
1767
|
+
passCount: 0,
|
|
1768
|
+
errorCount: 0
|
|
1769
|
+
};
|
|
1464
1770
|
}
|
|
1465
1771
|
config.lastRanTestFiles = targetTestFilesToFilter || allTestFilePaths;
|
|
1466
1772
|
try {
|
|
@@ -1492,6 +1798,20 @@ async function runTestsInBrowser(config, cachedContent = {}, connections, target
|
|
|
1492
1798
|
}
|
|
1493
1799
|
const TIME_TAKEN = TIME_COUNTER.stop();
|
|
1494
1800
|
if (!config._groupMode) {
|
|
1801
|
+
if (config.COUNTER.testCount === 0 && !cachedContent._buildError) {
|
|
1802
|
+
const displayFiles = allTestFilePaths.map(
|
|
1803
|
+
(f) => f.startsWith(`${projectRoot}/`) ? f.slice(projectRoot.length + 1) : f
|
|
1804
|
+
);
|
|
1805
|
+
cachedContent._noTestsWarning = displayFiles;
|
|
1806
|
+
const fileWord = allTestFilePaths.length === 1 ? "file" : "files";
|
|
1807
|
+
console.log(
|
|
1808
|
+
`# Warning: 0 tests registered \u2014 no QUnit test cases found in ${allTestFilePaths.length} ${fileWord}`
|
|
1809
|
+
);
|
|
1810
|
+
fs9.writeFile(`${projectRoot}/${output}/index.html`, buildNoTestsHTML(displayFiles)).catch(
|
|
1811
|
+
() => {
|
|
1812
|
+
}
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1495
1815
|
TAPDisplayFinalResult(config.COUNTER, TIME_TAKEN);
|
|
1496
1816
|
if (config.after) {
|
|
1497
1817
|
await runUserModule(`${process.cwd()}/${config.after}`, config.COUNTER, "after");
|
|
@@ -1507,8 +1827,18 @@ async function runTestsInBrowser(config, cachedContent = {}, connections, target
|
|
|
1507
1827
|
}
|
|
1508
1828
|
} catch (error) {
|
|
1509
1829
|
config.lastFailedTestFiles = config.lastRanTestFiles;
|
|
1510
|
-
console.log(error);
|
|
1511
1830
|
const exception = new BundleError(error);
|
|
1831
|
+
if (!cachedContent._buildError && error.errors?.length) {
|
|
1832
|
+
cachedContent._buildError = {
|
|
1833
|
+
type: deriveBuildErrorType(error),
|
|
1834
|
+
formatted: formatBuildErrors(error)
|
|
1835
|
+
};
|
|
1836
|
+
fs9.writeFile(
|
|
1837
|
+
`${projectRoot}/${output}/qunitx.html`,
|
|
1838
|
+
buildErrorHTML(cachedContent._buildError)
|
|
1839
|
+
).catch(() => {
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1512
1842
|
if (config.watch) {
|
|
1513
1843
|
console.log(`# ${exception}`);
|
|
1514
1844
|
} else {
|
|
@@ -1526,8 +1856,9 @@ function buildFilteredTests(filteredTests, outputPath, config) {
|
|
|
1526
1856
|
contents: filteredTests.map((filePath) => `import "${filePath}";`).join(""),
|
|
1527
1857
|
resolveDir: process.cwd()
|
|
1528
1858
|
},
|
|
1859
|
+
nodePaths: ancestorNodeModules(process.cwd()),
|
|
1529
1860
|
bundle: true,
|
|
1530
|
-
logLevel: "
|
|
1861
|
+
logLevel: "silent",
|
|
1531
1862
|
outfile: outputPath,
|
|
1532
1863
|
legalComments: "none",
|
|
1533
1864
|
target: esbuildTarget(config.browser),
|
|
@@ -1542,15 +1873,13 @@ async function runWithOverlayfsRetry(getContents, needsDisk) {
|
|
|
1542
1873
|
const MAX_RETRIES = 3;
|
|
1543
1874
|
const EMPTY_BUNDLE_THRESHOLD = 500;
|
|
1544
1875
|
let { result, js } = await getContents();
|
|
1876
|
+
const initialSize = js.length;
|
|
1545
1877
|
for (let retry = 1; retry <= MAX_RETRIES; retry++) {
|
|
1546
1878
|
if (js.length >= EMPTY_BUNDLE_THRESHOLD) break;
|
|
1547
|
-
console.log(
|
|
1548
|
-
`# [buildWithOverlayfsRetry] bundle is ${js.length} bytes (< ${EMPTY_BUNDLE_THRESHOLD}) on attempt ${retry}/${MAX_RETRIES} \u2014 overlayfs flush race, retrying in ${RETRY_DELAY_MS}ms`
|
|
1549
|
-
);
|
|
1550
1879
|
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
1551
1880
|
({ result, js } = await getContents());
|
|
1552
1881
|
}
|
|
1553
|
-
if (js.length < EMPTY_BUNDLE_THRESHOLD) {
|
|
1882
|
+
if (js.length < EMPTY_BUNDLE_THRESHOLD && js.length !== initialSize) {
|
|
1554
1883
|
console.log(
|
|
1555
1884
|
`# [buildWithOverlayfsRetry] bundle is ${js.length} bytes after ${MAX_RETRIES} retries \u2014 proceeding`
|
|
1556
1885
|
);
|
|
@@ -1641,13 +1970,15 @@ async function runTestInsideHTMLFile(filePath, { page, server, browser }, config
|
|
|
1641
1970
|
config._resetTestTimeout = null;
|
|
1642
1971
|
config._testRunDone = null;
|
|
1643
1972
|
}
|
|
1644
|
-
if (!QUNIT_RESULT
|
|
1973
|
+
if (!QUNIT_RESULT) {
|
|
1645
1974
|
if (targetError) console.log(targetError);
|
|
1646
1975
|
const wsReason = !wsConnected ? "WebSocket connection never received \u2014 Chrome may be CPU-starved or the page failed to load" : "WebSocket connected but no tests ran \u2014 QUnit may have failed to start";
|
|
1647
1976
|
console.log(`# TIMEOUT: ${wsReason}`);
|
|
1648
1977
|
console.log("BROWSER: runtime error thrown during executing tests");
|
|
1649
1978
|
console.error("BROWSER: runtime error thrown during executing tests");
|
|
1650
1979
|
await failOnNonWatchMode(config.watch, { server, browser }, config._groupMode);
|
|
1980
|
+
} else if (QUNIT_RESULT.totalTests === 0) {
|
|
1981
|
+
return;
|
|
1651
1982
|
} else if (QUNIT_RESULT.totalTests > QUNIT_RESULT.finishedTests) {
|
|
1652
1983
|
if (targetError) console.log(targetError);
|
|
1653
1984
|
console.log(
|
|
@@ -1673,7 +2004,7 @@ async function failOnNonWatchMode(watchMode = false, connections = {}, groupMode
|
|
|
1673
2004
|
process.exit(1);
|
|
1674
2005
|
}
|
|
1675
2006
|
}
|
|
1676
|
-
var BundleError;
|
|
2007
|
+
var BundleError, ancestorNodeModules;
|
|
1677
2008
|
var init_tests_in_browser = __esm({
|
|
1678
2009
|
"lib/commands/run/tests-in-browser.ts"() {
|
|
1679
2010
|
init_color();
|
|
@@ -1681,6 +2012,7 @@ var init_tests_in_browser = __esm({
|
|
|
1681
2012
|
init_time_counter();
|
|
1682
2013
|
init_run_user_module();
|
|
1683
2014
|
init_display_final_result();
|
|
2015
|
+
init_web_server();
|
|
1684
2016
|
BundleError = class extends Error {
|
|
1685
2017
|
constructor(message) {
|
|
1686
2018
|
super(message);
|
|
@@ -1688,13 +2020,16 @@ var init_tests_in_browser = __esm({
|
|
|
1688
2020
|
this.message = `esbuild Bundle Error: ${message}`.split("\n").join("\n# ");
|
|
1689
2021
|
}
|
|
1690
2022
|
};
|
|
2023
|
+
ancestorNodeModules = (dir) => dir.split(path5.sep).map(
|
|
2024
|
+
(_, i, parts) => path5.join(parts.slice(0, parts.length - i).join(path5.sep) || path5.sep, "node_modules")
|
|
2025
|
+
);
|
|
1691
2026
|
}
|
|
1692
2027
|
});
|
|
1693
2028
|
|
|
1694
2029
|
// lib/setup/file-watcher.ts
|
|
1695
2030
|
import fs10 from "node:fs";
|
|
1696
2031
|
import { stat, lstat } from "node:fs/promises";
|
|
1697
|
-
import
|
|
2032
|
+
import path6 from "node:path";
|
|
1698
2033
|
function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFunc) {
|
|
1699
2034
|
const extensions = config.extensions || ["js", "ts"];
|
|
1700
2035
|
const readyPromises = [];
|
|
@@ -1724,7 +2059,7 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
|
|
|
1724
2059
|
const lastChangeMs = {};
|
|
1725
2060
|
const childWatcher = fs10.watch(watchPath, { recursive: true }, async (eventType, filename) => {
|
|
1726
2061
|
if (!ready || !filename) return;
|
|
1727
|
-
const fullPath =
|
|
2062
|
+
const fullPath = filename === path6.basename(watchPath) ? watchPath : path6.join(watchPath, filename);
|
|
1728
2063
|
if (eventType === "change") {
|
|
1729
2064
|
if (!config._building) {
|
|
1730
2065
|
const now = Date.now();
|
|
@@ -1749,8 +2084,8 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
|
|
|
1749
2084
|
}
|
|
1750
2085
|
handleWatchEvent(config, extensions, event, fullPath, onEventFunc, onFinishFunc);
|
|
1751
2086
|
});
|
|
1752
|
-
const parentDir =
|
|
1753
|
-
const watchedBasename =
|
|
2087
|
+
const parentDir = path6.dirname(watchPath);
|
|
2088
|
+
const watchedBasename = path6.basename(watchPath);
|
|
1754
2089
|
let parentUnlinkFired = false;
|
|
1755
2090
|
const parentWatcher = fs10.watch(parentDir, async (eventType, filename) => {
|
|
1756
2091
|
if (!ready || filename !== watchedBasename || eventType !== "rename") return;
|
|
@@ -1825,7 +2160,8 @@ function handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFi
|
|
|
1825
2160
|
"#",
|
|
1826
2161
|
magenta().bold("==================================================================")
|
|
1827
2162
|
);
|
|
1828
|
-
|
|
2163
|
+
const displayPath = filePath.startsWith(config.projectRoot) ? filePath.slice(config.projectRoot.length) : filePath;
|
|
2164
|
+
console.log("#", colorEvent(event), displayPath);
|
|
1829
2165
|
console.log(
|
|
1830
2166
|
"#",
|
|
1831
2167
|
magenta().bold("==================================================================")
|
|
@@ -1852,13 +2188,13 @@ function handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFi
|
|
|
1852
2188
|
}
|
|
1853
2189
|
});
|
|
1854
2190
|
}
|
|
1855
|
-
function mutateFSTree(fsTree, event,
|
|
2191
|
+
function mutateFSTree(fsTree, event, path7) {
|
|
1856
2192
|
if (event === "add") {
|
|
1857
|
-
fsTree[
|
|
2193
|
+
fsTree[path7] = null;
|
|
1858
2194
|
} else if (event === "unlink") {
|
|
1859
|
-
delete fsTree[
|
|
2195
|
+
delete fsTree[path7];
|
|
1860
2196
|
} else if (event === "unlinkDir") {
|
|
1861
|
-
const dirPrefix =
|
|
2197
|
+
const dirPrefix = path7.endsWith("/") ? path7 : path7 + "/";
|
|
1862
2198
|
for (const treePath of Object.keys(fsTree)) {
|
|
1863
2199
|
if (treePath.startsWith(dirPrefix)) delete fsTree[treePath];
|
|
1864
2200
|
}
|
|
@@ -1990,14 +2326,18 @@ import { availableParallelism } from "node:os";
|
|
|
1990
2326
|
async function run(config) {
|
|
1991
2327
|
const cachedContent = await buildCachedContent(config, config.htmlPaths);
|
|
1992
2328
|
if (config.watch) {
|
|
1993
|
-
|
|
2329
|
+
const preBuildPromise = buildTestBundle(config, cachedContent);
|
|
2330
|
+
preBuildPromise.catch(() => {
|
|
2331
|
+
});
|
|
2332
|
+
cachedContent._preBuildPromise = preBuildPromise;
|
|
1994
2333
|
const [connections] = await Promise.all([
|
|
1995
2334
|
setupBrowser(config, cachedContent),
|
|
1996
2335
|
writeOutputStaticFiles(config, cachedContent)
|
|
1997
2336
|
]);
|
|
1998
2337
|
config.expressApp = connections.server;
|
|
1999
2338
|
setupKeyboardEvents(config, cachedContent, connections);
|
|
2000
|
-
|
|
2339
|
+
const isHeadedWatchMode = config.open === true && config.watch;
|
|
2340
|
+
if (config.open && !isHeadedWatchMode) {
|
|
2001
2341
|
void openOutputInBrowser(config);
|
|
2002
2342
|
}
|
|
2003
2343
|
if (config.before) {
|
|
@@ -2012,6 +2352,10 @@ async function run(config) {
|
|
|
2012
2352
|
]);
|
|
2013
2353
|
throw error;
|
|
2014
2354
|
}
|
|
2355
|
+
if (isHeadedWatchMode && (cachedContent._buildError || cachedContent._noTestsWarning)) {
|
|
2356
|
+
await connections.page.goto(`http://localhost:${config.port}/`, { waitUntil: "commit", timeout: 5e3 }).catch(() => {
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2015
2359
|
if (config.watch) {
|
|
2016
2360
|
const { ready: watcherReady } = setupFileWatchers(
|
|
2017
2361
|
config.testFileLookupPaths,
|
|
@@ -2035,7 +2379,13 @@ async function run(config) {
|
|
|
2035
2379
|
}
|
|
2036
2380
|
await runTestsInBrowser(config, cachedContent, connections, [file]);
|
|
2037
2381
|
},
|
|
2038
|
-
(_path, _event) =>
|
|
2382
|
+
async (_path, _event) => {
|
|
2383
|
+
connections.server.publish("refresh");
|
|
2384
|
+
if (isHeadedWatchMode && (cachedContent._buildError || cachedContent._noTestsWarning)) {
|
|
2385
|
+
await connections.page.goto(`http://localhost:${config.port}/`, { waitUntil: "commit", timeout: 5e3 }).catch(() => {
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2039
2389
|
);
|
|
2040
2390
|
await watcherReady;
|
|
2041
2391
|
}
|
|
@@ -2044,7 +2394,14 @@ async function run(config) {
|
|
|
2044
2394
|
const allFiles = Object.keys(config.fsTree);
|
|
2045
2395
|
const groupCount = Math.min(allFiles.length, availableParallelism());
|
|
2046
2396
|
const groups = splitIntoGroups(allFiles, groupCount);
|
|
2047
|
-
config.COUNTER = {
|
|
2397
|
+
config.COUNTER = {
|
|
2398
|
+
testCount: 0,
|
|
2399
|
+
failCount: 0,
|
|
2400
|
+
skipCount: 0,
|
|
2401
|
+
todoCount: 0,
|
|
2402
|
+
passCount: 0,
|
|
2403
|
+
errorCount: 0
|
|
2404
|
+
};
|
|
2048
2405
|
config.lastRanTestFiles = allFiles;
|
|
2049
2406
|
const groupConfigs = groups.map((groupFiles, i) => ({
|
|
2050
2407
|
...config,
|
|
@@ -2133,6 +2490,12 @@ async function run(config) {
|
|
|
2133
2490
|
config.COUNTER.failCount > 0 ? 1 : 0
|
|
2134
2491
|
);
|
|
2135
2492
|
process.exitCode = exitCode;
|
|
2493
|
+
if (config.COUNTER.testCount === 0 && exitCode === 0) {
|
|
2494
|
+
const fileWord = allFiles.length === 1 ? "file" : "files";
|
|
2495
|
+
console.log(
|
|
2496
|
+
`# Warning: 0 tests registered \u2014 no QUnit test cases found in ${allFiles.length} ${fileWord}`
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2136
2499
|
TAPDisplayFinalResult(config.COUNTER, TIME_COUNTER.stop());
|
|
2137
2500
|
if (config.after) {
|
|
2138
2501
|
await runUserModule(`${process.cwd()}/${config.after}`, config.COUNTER, "after");
|
|
@@ -2256,7 +2619,7 @@ init_color();
|
|
|
2256
2619
|
var package_default = {
|
|
2257
2620
|
name: "qunitx-cli",
|
|
2258
2621
|
type: "module",
|
|
2259
|
-
version: "0.
|
|
2622
|
+
version: "0.19.0",
|
|
2260
2623
|
description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
|
|
2261
2624
|
author: "Izel Nakri",
|
|
2262
2625
|
license: "MIT",
|
|
@@ -2488,17 +2851,17 @@ function pathToModuleName(filePath) {
|
|
|
2488
2851
|
async function generateTestFiles() {
|
|
2489
2852
|
const projectRoot = await findProjectRoot();
|
|
2490
2853
|
const moduleName = pathToModuleName(process.argv[3]);
|
|
2491
|
-
const
|
|
2492
|
-
if (await pathExists(
|
|
2493
|
-
console.log(`${
|
|
2854
|
+
const path7 = process.argv[3].endsWith(".js") || process.argv[3].endsWith(".ts") ? `${projectRoot}/${process.argv[3]}` : `${projectRoot}/${process.argv[3]}.js`;
|
|
2855
|
+
if (await pathExists(path7)) {
|
|
2856
|
+
console.log(`${path7} already exists!`);
|
|
2494
2857
|
return;
|
|
2495
2858
|
}
|
|
2496
2859
|
const testJSContent = await readTemplate("test.js");
|
|
2497
|
-
const targetFolderPaths =
|
|
2860
|
+
const targetFolderPaths = path7.split("/");
|
|
2498
2861
|
targetFolderPaths.pop();
|
|
2499
2862
|
await fs5.mkdir(targetFolderPaths.join("/"), { recursive: true });
|
|
2500
|
-
await fs5.writeFile(
|
|
2501
|
-
console.log(green(`${
|
|
2863
|
+
await fs5.writeFile(path7, testJSContent.replace("{{moduleName}}", moduleName));
|
|
2864
|
+
console.log(green(`${path7} written`));
|
|
2502
2865
|
}
|
|
2503
2866
|
|
|
2504
2867
|
// lib/setup/config.ts
|
|
@@ -2599,20 +2962,20 @@ function setupTestFilePaths(_projectRoot, inputs2) {
|
|
|
2599
2962
|
});
|
|
2600
2963
|
return result.map((metaItem) => metaItem.input);
|
|
2601
2964
|
}
|
|
2602
|
-
function pathIsFile(
|
|
2603
|
-
const inputs2 =
|
|
2965
|
+
function pathIsFile(path7) {
|
|
2966
|
+
const inputs2 = path7.split("/");
|
|
2604
2967
|
return inputs2[inputs2.length - 1].includes(".");
|
|
2605
2968
|
}
|
|
2606
2969
|
function pathIsIncludedInPaths(paths, targetPath) {
|
|
2607
|
-
return paths.some((
|
|
2608
|
-
if (
|
|
2970
|
+
return paths.some((path7) => {
|
|
2971
|
+
if (path7 === targetPath) {
|
|
2609
2972
|
return false;
|
|
2610
2973
|
}
|
|
2611
|
-
return matchesGlob(targetPath.input, buildGlobFormat(
|
|
2974
|
+
return matchesGlob(targetPath.input, buildGlobFormat(path7));
|
|
2612
2975
|
});
|
|
2613
2976
|
}
|
|
2614
|
-
function buildGlobFormat(
|
|
2615
|
-
return
|
|
2977
|
+
function buildGlobFormat(path7) {
|
|
2978
|
+
return path7.isFile ? path7.input : `${path7.input}/**`;
|
|
2616
2979
|
}
|
|
2617
2980
|
|
|
2618
2981
|
// lib/utils/parse-cli-flags.ts
|
|
@@ -2666,7 +3029,9 @@ function parseCliFlags(projectRoot) {
|
|
|
2666
3029
|
console.warn(`# Warning: Unknown flag "${arg}" \u2014 ignored`);
|
|
2667
3030
|
return result;
|
|
2668
3031
|
}
|
|
2669
|
-
result.inputs.add(
|
|
3032
|
+
result.inputs.add(
|
|
3033
|
+
arg.startsWith(projectRoot) || arg.startsWith("/") ? arg : `${process.cwd()}/${arg}`
|
|
3034
|
+
);
|
|
2670
3035
|
return result;
|
|
2671
3036
|
},
|
|
2672
3037
|
{ inputs: /* @__PURE__ */ new Set([]) }
|
|
@@ -2704,7 +3069,14 @@ async function setupConfig() {
|
|
|
2704
3069
|
testFileLookupPaths: setupTestFilePaths(projectRoot, inputs2),
|
|
2705
3070
|
lastFailedTestFiles: null,
|
|
2706
3071
|
lastRanTestFiles: null,
|
|
2707
|
-
COUNTER: {
|
|
3072
|
+
COUNTER: {
|
|
3073
|
+
testCount: 0,
|
|
3074
|
+
failCount: 0,
|
|
3075
|
+
skipCount: 0,
|
|
3076
|
+
todoCount: 0,
|
|
3077
|
+
passCount: 0,
|
|
3078
|
+
errorCount: 0
|
|
3079
|
+
},
|
|
2708
3080
|
_testRunDone: null,
|
|
2709
3081
|
_resetTestTimeout: null,
|
|
2710
3082
|
_onWsOpen: null,
|