qunitx-cli 0.19.0 → 0.19.1
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 +81 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -93,7 +93,7 @@ async function cleanupBrowserDir(dirPath) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
const deadline = Date.now() +
|
|
96
|
+
const deadline = Date.now() + 5e3;
|
|
97
97
|
while (Date.now() < deadline) {
|
|
98
98
|
const removed = await fs.rm(dirPath, { recursive: true, force: true }).then(() => true).catch(() => false);
|
|
99
99
|
if (removed) break;
|
|
@@ -908,7 +908,7 @@ function setupWebServer(config, cachedContent) {
|
|
|
908
908
|
);
|
|
909
909
|
server.wss.on("connection", function connection(socket) {
|
|
910
910
|
socket.on("message", function message(data) {
|
|
911
|
-
const { event, details, abort } = JSON.parse(data);
|
|
911
|
+
const { event, details, qunitResult, abort } = JSON.parse(data);
|
|
912
912
|
if (event === "wsOpen") {
|
|
913
913
|
config._phase = "loading";
|
|
914
914
|
config._onWsOpen?.();
|
|
@@ -941,6 +941,7 @@ function setupWebServer(config, cachedContent) {
|
|
|
941
941
|
TAPDisplayTestResult(config.COUNTER, details);
|
|
942
942
|
} else if (event === "done") {
|
|
943
943
|
config._phase = "done";
|
|
944
|
+
config._lastQUnitResult = qunitResult ?? null;
|
|
944
945
|
if (config.debug && config._groupMode) {
|
|
945
946
|
process.stdout.write(
|
|
946
947
|
`# group done: ${details.passed} passed, ${details.failed} failed (${details.runtime}ms)
|
|
@@ -968,7 +969,11 @@ function setupWebServer(config, cachedContent) {
|
|
|
968
969
|
return;
|
|
969
970
|
}
|
|
970
971
|
config._onTestsJsServed?.();
|
|
971
|
-
res.writeHead(200, {
|
|
972
|
+
res.writeHead(200, {
|
|
973
|
+
"Content-Type": "application/javascript",
|
|
974
|
+
"Cache-Control": "no-store",
|
|
975
|
+
"Content-Length": bytes
|
|
976
|
+
});
|
|
972
977
|
res.end(cachedContent.allTestCode);
|
|
973
978
|
});
|
|
974
979
|
server.get("/filtered-tests.js", (_req, res) => {
|
|
@@ -985,7 +990,11 @@ function setupWebServer(config, cachedContent) {
|
|
|
985
990
|
return;
|
|
986
991
|
}
|
|
987
992
|
config._onTestsJsServed?.();
|
|
988
|
-
res.writeHead(200, {
|
|
993
|
+
res.writeHead(200, {
|
|
994
|
+
"Content-Type": "application/javascript",
|
|
995
|
+
"Cache-Control": "no-store",
|
|
996
|
+
"Content-Length": bytes
|
|
997
|
+
});
|
|
989
998
|
res.end(cachedContent.filteredTestCode);
|
|
990
999
|
});
|
|
991
1000
|
server.get("/", async (_req, res) => {
|
|
@@ -1006,10 +1015,9 @@ function setupWebServer(config, cachedContent) {
|
|
|
1006
1015
|
res.end();
|
|
1007
1016
|
return;
|
|
1008
1017
|
}
|
|
1009
|
-
const TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
|
|
1010
1018
|
const htmlContent = escapeAndInjectTestsToHTML(
|
|
1011
1019
|
mainHTMLWithReplacedAssets,
|
|
1012
|
-
|
|
1020
|
+
testRuntimeToInject(config.port, config),
|
|
1013
1021
|
"./tests.js"
|
|
1014
1022
|
);
|
|
1015
1023
|
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
@@ -1038,10 +1046,9 @@ function setupWebServer(config, cachedContent) {
|
|
|
1038
1046
|
res.end();
|
|
1039
1047
|
return;
|
|
1040
1048
|
}
|
|
1041
|
-
const TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
|
|
1042
1049
|
const htmlContent = escapeAndInjectTestsToHTML(
|
|
1043
1050
|
mainHTMLWithReplacedAssets,
|
|
1044
|
-
|
|
1051
|
+
testRuntimeToInject(config.port, config),
|
|
1045
1052
|
"./filtered-tests.js"
|
|
1046
1053
|
);
|
|
1047
1054
|
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
@@ -1055,10 +1062,9 @@ function setupWebServer(config, cachedContent) {
|
|
|
1055
1062
|
server.get("/*", async (req, res) => {
|
|
1056
1063
|
const possibleDynamicHTML = cachedContent.dynamicContentHTMLs[`${config.projectRoot}${req.path}`];
|
|
1057
1064
|
if (possibleDynamicHTML) {
|
|
1058
|
-
const TEST_RUNTIME_TO_INJECT = testRuntimeToInject(config.port, config);
|
|
1059
1065
|
const htmlContent = escapeAndInjectTestsToHTML(
|
|
1060
1066
|
possibleDynamicHTML,
|
|
1061
|
-
|
|
1067
|
+
testRuntimeToInject(config.port, config),
|
|
1062
1068
|
"/tests.js"
|
|
1063
1069
|
);
|
|
1064
1070
|
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-store" });
|
|
@@ -1215,11 +1221,6 @@ function testRuntimeToInject(port, config) {
|
|
|
1215
1221
|
window.socket.send(JSON.stringify({ event: 'connection' }));
|
|
1216
1222
|
}
|
|
1217
1223
|
});
|
|
1218
|
-
window.QUnit.moduleStart((details) => { // NOTE: might be useful in future for hanged module tracking
|
|
1219
|
-
if (window.IS_PLAYWRIGHT) {
|
|
1220
|
-
window.socket.send(JSON.stringify({ event: 'moduleStart', details: details }, getCircularReplacer()));
|
|
1221
|
-
}
|
|
1222
|
-
});
|
|
1223
1224
|
window.QUnit.on('testStart', (details) => {
|
|
1224
1225
|
window.QUNIT_RESULT.totalTests++;
|
|
1225
1226
|
window.QUNIT_RESULT.currentTest = details.fullName.join(' | ');
|
|
@@ -1239,7 +1240,7 @@ function testRuntimeToInject(port, config) {
|
|
|
1239
1240
|
});
|
|
1240
1241
|
window.QUnit.done((details) => {
|
|
1241
1242
|
if (window.IS_PLAYWRIGHT) {
|
|
1242
|
-
window.socket.send(JSON.stringify({ event: 'done', details: details, abort: window.abortQUnit }, getCircularReplacer()));
|
|
1243
|
+
window.socket.send(JSON.stringify({ event: 'done', details: details, qunitResult: window.QUNIT_RESULT, abort: window.abortQUnit }, getCircularReplacer()));
|
|
1243
1244
|
// Do NOT set testTimeout here. The WS 'done' event (testsDone promise) is the
|
|
1244
1245
|
// canonical completion signal for Playwright runs. waitForFunction is reserved
|
|
1245
1246
|
// for true timeouts (test hangs) where testTimeout increments naturally via setInterval.
|
|
@@ -1537,16 +1538,25 @@ async function setupBrowser(config, cachedContent, existingBrowser = null) {
|
|
|
1537
1538
|
await page.addInitScript(() => {
|
|
1538
1539
|
window.IS_PLAYWRIGHT = true;
|
|
1539
1540
|
});
|
|
1540
|
-
|
|
1541
|
+
config._pendingConsoleHandlers = /* @__PURE__ */ new Set();
|
|
1542
|
+
page.on("console", (msg) => {
|
|
1541
1543
|
const type = msg.type();
|
|
1542
1544
|
const alwaysShow = type === "warning" || type === "error";
|
|
1543
1545
|
if (!alwaysShow && !config.debug) return;
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1546
|
+
const handler = (async () => {
|
|
1547
|
+
try {
|
|
1548
|
+
const values = await Promise.all(
|
|
1549
|
+
msg.args().map(
|
|
1550
|
+
(arg) => arg.jsonValue().catch(() => arg.evaluate((v) => JSON.stringify(v)).then(JSON.parse))
|
|
1551
|
+
)
|
|
1552
|
+
);
|
|
1553
|
+
console.log(...values);
|
|
1554
|
+
} catch {
|
|
1555
|
+
console.log(msg.text());
|
|
1556
|
+
}
|
|
1557
|
+
})();
|
|
1558
|
+
config._pendingConsoleHandlers.add(handler);
|
|
1559
|
+
handler.finally(() => config._pendingConsoleHandlers?.delete(handler));
|
|
1550
1560
|
});
|
|
1551
1561
|
page.on("pageerror", (error) => {
|
|
1552
1562
|
console.error(error.toString());
|
|
@@ -1712,7 +1722,7 @@ async function buildTestBundle(config, cachedContent) {
|
|
|
1712
1722
|
// Allow test files outside the project root (e.g. /tmp/my-test.ts) to import
|
|
1713
1723
|
// packages from any node_modules on the ancestor chain of cwd — the same lookup
|
|
1714
1724
|
// order Node itself uses when resolving require() from process.cwd().
|
|
1715
|
-
nodePaths:
|
|
1725
|
+
nodePaths: ANCESTOR_NODE_MODULES,
|
|
1716
1726
|
bundle: true,
|
|
1717
1727
|
logLevel: "silent",
|
|
1718
1728
|
outfile,
|
|
@@ -1817,6 +1827,7 @@ async function runTestsInBrowser(config, cachedContent = {}, connections, target
|
|
|
1817
1827
|
await runUserModule(`${process.cwd()}/${config.after}`, config.COUNTER, "after");
|
|
1818
1828
|
}
|
|
1819
1829
|
if (!config.watch) {
|
|
1830
|
+
await flushConsoleHandlers(config._pendingConsoleHandlers);
|
|
1820
1831
|
await Promise.all([
|
|
1821
1832
|
connections.server && connections.server.close(),
|
|
1822
1833
|
connections.browser && connections.browser.close()
|
|
@@ -1856,7 +1867,7 @@ function buildFilteredTests(filteredTests, outputPath, config) {
|
|
|
1856
1867
|
contents: filteredTests.map((filePath) => `import "${filePath}";`).join(""),
|
|
1857
1868
|
resolveDir: process.cwd()
|
|
1858
1869
|
},
|
|
1859
|
-
nodePaths:
|
|
1870
|
+
nodePaths: ANCESTOR_NODE_MODULES,
|
|
1860
1871
|
bundle: true,
|
|
1861
1872
|
logLevel: "silent",
|
|
1862
1873
|
outfile: outputPath,
|
|
@@ -1958,7 +1969,7 @@ async function runTestInsideHTMLFile(filePath, { page, server, browser }, config
|
|
|
1958
1969
|
clearTimeout(timeoutHandle);
|
|
1959
1970
|
timeoutHandle = setTimeout(resolveTestRace, startupMs);
|
|
1960
1971
|
await testRaceResult;
|
|
1961
|
-
QUNIT_RESULT = await page.evaluate(() => window.QUNIT_RESULT);
|
|
1972
|
+
QUNIT_RESULT = config._lastQUnitResult ?? await page.evaluate(() => window.QUNIT_RESULT);
|
|
1962
1973
|
} catch (error) {
|
|
1963
1974
|
targetError = error;
|
|
1964
1975
|
console.log(error);
|
|
@@ -1969,6 +1980,7 @@ async function runTestInsideHTMLFile(filePath, { page, server, browser }, config
|
|
|
1969
1980
|
config._onTestsJsServed = null;
|
|
1970
1981
|
config._resetTestTimeout = null;
|
|
1971
1982
|
config._testRunDone = null;
|
|
1983
|
+
config._lastQUnitResult = null;
|
|
1972
1984
|
}
|
|
1973
1985
|
if (!QUNIT_RESULT) {
|
|
1974
1986
|
if (targetError) console.log(targetError);
|
|
@@ -1976,7 +1988,12 @@ async function runTestInsideHTMLFile(filePath, { page, server, browser }, config
|
|
|
1976
1988
|
console.log(`# TIMEOUT: ${wsReason}`);
|
|
1977
1989
|
console.log("BROWSER: runtime error thrown during executing tests");
|
|
1978
1990
|
console.error("BROWSER: runtime error thrown during executing tests");
|
|
1979
|
-
await failOnNonWatchMode(
|
|
1991
|
+
await failOnNonWatchMode(
|
|
1992
|
+
config.watch,
|
|
1993
|
+
{ server, browser },
|
|
1994
|
+
config._groupMode,
|
|
1995
|
+
config._pendingConsoleHandlers
|
|
1996
|
+
);
|
|
1980
1997
|
} else if (QUNIT_RESULT.totalTests === 0) {
|
|
1981
1998
|
return;
|
|
1982
1999
|
} else if (QUNIT_RESULT.totalTests > QUNIT_RESULT.finishedTests) {
|
|
@@ -1986,16 +2003,22 @@ async function runTestInsideHTMLFile(filePath, { page, server, browser }, config
|
|
|
1986
2003
|
);
|
|
1987
2004
|
console.log(`BROWSER: TEST TIMED OUT: ${QUNIT_RESULT.currentTest}`);
|
|
1988
2005
|
console.error(`BROWSER: TEST TIMED OUT: ${QUNIT_RESULT.currentTest}`);
|
|
1989
|
-
await failOnNonWatchMode(
|
|
2006
|
+
await failOnNonWatchMode(
|
|
2007
|
+
config.watch,
|
|
2008
|
+
{ server, browser },
|
|
2009
|
+
config._groupMode,
|
|
2010
|
+
config._pendingConsoleHandlers
|
|
2011
|
+
);
|
|
1990
2012
|
} else if (QUNIT_RESULT.failedTests > config.COUNTER.failCount) {
|
|
1991
2013
|
config.COUNTER.failCount = QUNIT_RESULT.failedTests;
|
|
1992
2014
|
}
|
|
1993
2015
|
}
|
|
1994
|
-
async function failOnNonWatchMode(watchMode = false, connections = {}, groupMode = false) {
|
|
2016
|
+
async function failOnNonWatchMode(watchMode = false, connections = {}, groupMode = false, pendingHandlers) {
|
|
1995
2017
|
if (!watchMode) {
|
|
1996
2018
|
if (groupMode) {
|
|
1997
2019
|
throw new Error("Browser test run failed");
|
|
1998
2020
|
}
|
|
2021
|
+
await flushConsoleHandlers(pendingHandlers);
|
|
1999
2022
|
await Promise.all([
|
|
2000
2023
|
connections.server && connections.server.close(),
|
|
2001
2024
|
connections.browser && connections.browser.close()
|
|
@@ -2004,7 +2027,12 @@ async function failOnNonWatchMode(watchMode = false, connections = {}, groupMode
|
|
|
2004
2027
|
process.exit(1);
|
|
2005
2028
|
}
|
|
2006
2029
|
}
|
|
2007
|
-
|
|
2030
|
+
async function flushConsoleHandlers(handlers, deadline = Date.now() + 2e3) {
|
|
2031
|
+
if (!handlers || handlers.size === 0 || Date.now() >= deadline) return;
|
|
2032
|
+
await Promise.allSettled([...handlers]);
|
|
2033
|
+
return flushConsoleHandlers(handlers, deadline);
|
|
2034
|
+
}
|
|
2035
|
+
var BundleError, ancestorNodeModules, ANCESTOR_NODE_MODULES;
|
|
2008
2036
|
var init_tests_in_browser = __esm({
|
|
2009
2037
|
"lib/commands/run/tests-in-browser.ts"() {
|
|
2010
2038
|
init_color();
|
|
@@ -2023,6 +2051,7 @@ var init_tests_in_browser = __esm({
|
|
|
2023
2051
|
ancestorNodeModules = (dir) => dir.split(path5.sep).map(
|
|
2024
2052
|
(_, i, parts) => path5.join(parts.slice(0, parts.length - i).join(path5.sep) || path5.sep, "node_modules")
|
|
2025
2053
|
);
|
|
2054
|
+
ANCESTOR_NODE_MODULES = ancestorNodeModules(process.cwd());
|
|
2026
2055
|
}
|
|
2027
2056
|
});
|
|
2028
2057
|
|
|
@@ -2209,7 +2238,7 @@ var CHANGE_DEDUPE_MS;
|
|
|
2209
2238
|
var init_file_watcher = __esm({
|
|
2210
2239
|
"lib/setup/file-watcher.ts"() {
|
|
2211
2240
|
init_color();
|
|
2212
|
-
CHANGE_DEDUPE_MS =
|
|
2241
|
+
CHANGE_DEDUPE_MS = 10;
|
|
2213
2242
|
}
|
|
2214
2243
|
});
|
|
2215
2244
|
|
|
@@ -2393,7 +2422,7 @@ async function run(config) {
|
|
|
2393
2422
|
} else {
|
|
2394
2423
|
const allFiles = Object.keys(config.fsTree);
|
|
2395
2424
|
const groupCount = Math.min(allFiles.length, availableParallelism());
|
|
2396
|
-
const groups = splitIntoGroups(allFiles, groupCount);
|
|
2425
|
+
const groups = await splitIntoGroups(allFiles, groupCount);
|
|
2397
2426
|
config.COUNTER = {
|
|
2398
2427
|
testCount: 0,
|
|
2399
2428
|
failCount: 0,
|
|
@@ -2462,6 +2491,7 @@ async function run(config) {
|
|
|
2462
2491
|
try {
|
|
2463
2492
|
await runTestsInBrowser(groupConfig, groupCachedContents[i], connections);
|
|
2464
2493
|
} finally {
|
|
2494
|
+
await flushConsoleHandlers(groupConfig._pendingConsoleHandlers);
|
|
2465
2495
|
await Promise.all([
|
|
2466
2496
|
connections.server && connections.server.close(),
|
|
2467
2497
|
connections.page && // Unref'd: the keepAlive interval above holds the event loop open, so this
|
|
@@ -2567,10 +2597,24 @@ async function addCachedContentMainHTML(projectRoot, cachedContent) {
|
|
|
2567
2597
|
}
|
|
2568
2598
|
return cachedContent;
|
|
2569
2599
|
}
|
|
2570
|
-
function splitIntoGroups(files, groupCount) {
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2600
|
+
async function splitIntoGroups(files, groupCount) {
|
|
2601
|
+
const withSizes = await Promise.all(
|
|
2602
|
+
files.map(
|
|
2603
|
+
(f) => fs12.stat(f).then(({ size }) => ({ f, size })).catch(() => ({ f, size: 0 }))
|
|
2604
|
+
)
|
|
2605
|
+
);
|
|
2606
|
+
return withSizes.sort((a, b) => b.size - a.size).reduce(
|
|
2607
|
+
(groups, { f, size }) => {
|
|
2608
|
+
const { idx } = groups.reduce(
|
|
2609
|
+
(min, { total }, i) => total < min.total ? { idx: i, total } : min,
|
|
2610
|
+
{ idx: 0, total: groups[0].total }
|
|
2611
|
+
);
|
|
2612
|
+
groups[idx].files.push(f);
|
|
2613
|
+
groups[idx].total += size;
|
|
2614
|
+
return groups;
|
|
2615
|
+
},
|
|
2616
|
+
Array.from({ length: groupCount }, () => ({ files: [], total: 0 }))
|
|
2617
|
+
).flatMap((g) => g.files.length > 0 ? [g.files] : []);
|
|
2574
2618
|
}
|
|
2575
2619
|
function logWatcherAndKeyboardShortcutInfo(config, _server) {
|
|
2576
2620
|
const prefix = "Watching files...";
|
|
@@ -2619,7 +2663,7 @@ init_color();
|
|
|
2619
2663
|
var package_default = {
|
|
2620
2664
|
name: "qunitx-cli",
|
|
2621
2665
|
type: "module",
|
|
2622
|
-
version: "0.19.
|
|
2666
|
+
version: "0.19.1",
|
|
2623
2667
|
description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
|
|
2624
2668
|
author: "Izel Nakri",
|
|
2625
2669
|
license: "MIT",
|