qunitx-cli 0.11.0 → 0.15.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.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/cli.js +60 -11
  3. package/package.json +5 -3
package/README.md CHANGED
@@ -188,6 +188,13 @@ All CLI flags can also be set in `package.json` under the `qunitx` key, so you d
188
188
 
189
189
  CLI flags always override `package.json` values when both are present.
190
190
 
191
+ ### Environment variables
192
+
193
+ | Variable | Description |
194
+ |------------------|---------------------------------------------------------------------------------------------------------------|
195
+ | `CHROME_BIN` | Path to the Chrome/Chromium executable. Required on systems where Chrome is not on `PATH` (e.g. many CI environments). Set automatically when using `browser-actions/setup-chrome` in GitHub Actions. |
196
+ | `QUNITX_BROWSER` | Browser engine to use (`chromium`, `firefox`, `webkit`). Equivalent to `--browser` on the CLI. Useful in CI matrix jobs. |
197
+
191
198
  If you do not provide any HTML template, qunitx falls back to its built-in `test/tests.html` boilerplate internally, so `qunitx init` is optional.
192
199
 
193
200
  You can also pass a custom HTML file on the CLI:
package/dist/cli.js CHANGED
@@ -682,17 +682,38 @@ function setupWebServer(config, cachedContent) {
682
682
  socket.on("message", function message(data) {
683
683
  const { event, details, abort } = JSON.parse(data);
684
684
  if (event === "wsOpen") {
685
+ config._phase = "loading";
685
686
  config._onWsOpen?.();
686
687
  } else if (event === "connection") {
688
+ config._phase = "running";
687
689
  if (!config._groupMode) console.log("TAP version 13");
690
+ if (config.debug && config._groupMode) {
691
+ const allFiles = Object.keys(config.fsTree);
692
+ const relFiles = allFiles.map((f) => f.replace(`${config.projectRoot}/`, ""));
693
+ const shown = relFiles.slice(0, 2);
694
+ const rest = relFiles.length - shown.length;
695
+ const fileList = rest > 0 ? `${shown.join(" ")} +${rest} more` : shown.join(" ");
696
+ console.log("#", blue(`\u2500\u2500 ${fileList} \u2500\u2500`));
697
+ }
688
698
  config._resetTestTimeout?.();
689
699
  } else if (event === "testEnd" && !abort) {
690
700
  if (details.status === "failed") {
691
701
  config.lastFailedTestFiles = config.lastRanTestFiles;
692
702
  }
703
+ if (config.debug && details.runtime > config.timeout * 0.8) {
704
+ console.log(
705
+ `# SLOW (${details.runtime.toFixed(0)}ms / ${config.timeout}ms timeout): ${details.fullName.join(" | ")}`
706
+ );
707
+ }
693
708
  config._resetTestTimeout?.();
694
709
  TAPDisplayTestResult(config.COUNTER, details);
695
710
  } else if (event === "done") {
711
+ config._phase = "done";
712
+ if (config.debug && config._groupMode) {
713
+ console.log(
714
+ `# group done: ${details.passed} passed, ${details.failed} failed (${details.runtime}ms)`
715
+ );
716
+ }
696
717
  if (typeof config._testRunDone === "function") {
697
718
  config._testRunDone();
698
719
  config._testRunDone = null;
@@ -966,6 +987,7 @@ var init_web_server = __esm({
966
987
  init_find_internal_assets_from_html();
967
988
  init_html_content_marker();
968
989
  init_display_test_result();
990
+ init_color();
969
991
  init_path_exists();
970
992
  init_http();
971
993
  fsPromise = fs7.promises;
@@ -1122,7 +1144,7 @@ async function runUserModule(modulePath, params, scriptPosition) {
1122
1144
  console.log("#", red(`QUnitX ${scriptPosition} script failed:`));
1123
1145
  console.trace(error);
1124
1146
  console.error(error);
1125
- return process.exit(1);
1147
+ process.stdout.write("", () => process.exit(1));
1126
1148
  }
1127
1149
  }
1128
1150
  var init_run_user_module = __esm({
@@ -1470,6 +1492,7 @@ function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFun
1470
1492
  function handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFinishFunc) {
1471
1493
  const isFileEvent = extensions.some((ext) => filePath.endsWith(`.${ext}`));
1472
1494
  if (!isFileEvent && event !== "unlinkDir") return;
1495
+ if (event === "change" && config._building && config._justAddedFiles?.has(filePath)) return;
1473
1496
  mutateFSTree(config.fsTree, event, filePath);
1474
1497
  console.log(
1475
1498
  "#",
@@ -1482,6 +1505,7 @@ function handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFi
1482
1505
  );
1483
1506
  if (!config._building) {
1484
1507
  config._building = true;
1508
+ config._justAddedFiles = event === "add" ? /* @__PURE__ */ new Set([filePath]) : /* @__PURE__ */ new Set();
1485
1509
  const result = onEventFunc(event, filePath);
1486
1510
  if (!(result instanceof Promise)) {
1487
1511
  config._building = false;
@@ -1500,6 +1524,7 @@ function handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFi
1500
1524
  }
1501
1525
  });
1502
1526
  } else {
1527
+ if (event === "add") config._justAddedFiles?.add(filePath);
1503
1528
  config._pendingBuildTrigger = () => handleWatchEvent(config, extensions, event, filePath, onEventFunc, onFinishFunc);
1504
1529
  }
1505
1530
  }
@@ -1594,7 +1619,7 @@ function setupKeyboardEvents(config, cachedContent, connections) {
1594
1619
  });
1595
1620
  }
1596
1621
  function abortBrowserQUnit(_config, connections) {
1597
- connections.server.publish("abort", "abort");
1622
+ connections.server.publish("abort");
1598
1623
  }
1599
1624
  var init_keyboard_events = __esm({
1600
1625
  "lib/setup/keyboard-events.ts"() {
@@ -1671,11 +1696,21 @@ async function run(config) {
1671
1696
  if (["change", "unlink", "unlinkDir"].includes(event)) {
1672
1697
  if (event === "change" && !(file in config.fsTree)) return;
1673
1698
  cachedContent.allTestCode = null;
1699
+ if (config.debug) {
1700
+ console.log(
1701
+ `# Rerun triggered: ${event} \u2192 ${file.replace(`${config.projectRoot}/`, "")}`
1702
+ );
1703
+ }
1674
1704
  return await runTestsInBrowser(config, cachedContent, connections);
1675
1705
  }
1706
+ if (config.debug) {
1707
+ console.log(
1708
+ `# Rerun triggered: ${event} \u2192 ${file.replace(`${config.projectRoot}/`, "")}`
1709
+ );
1710
+ }
1676
1711
  await runTestsInBrowser(config, cachedContent, connections, [file]);
1677
1712
  },
1678
- (_path, _event) => connections.server.publish("refresh", "refresh")
1713
+ (_path, _event) => connections.server.publish("refresh")
1679
1714
  );
1680
1715
  await watcherReady;
1681
1716
  }
@@ -1691,10 +1726,14 @@ async function run(config) {
1691
1726
  fsTree: Object.fromEntries(groupFiles.map((f) => [f, config.fsTree[f]])),
1692
1727
  // Single group keeps the root output dir for backward-compatible file paths.
1693
1728
  output: groupCount === 1 ? config.output : `${config.output}/group-${i}`,
1694
- _groupMode: true
1729
+ _groupMode: true,
1730
+ _phase: "bundling"
1695
1731
  }));
1696
1732
  const groupCachedContents = groups.map(() => ({ ...cachedContent }));
1697
1733
  console.log("TAP version 13");
1734
+ console.log(
1735
+ `# Running ${allFiles.length} test file${allFiles.length === 1 ? "" : "s"} across ${groupCount} group${groupCount === 1 ? "" : "s"}`
1736
+ );
1698
1737
  const [browser] = await Promise.all([
1699
1738
  launchBrowser(config),
1700
1739
  Promise.all(
@@ -1716,14 +1755,22 @@ async function run(config) {
1716
1755
  const groupResults = await Promise.allSettled(
1717
1756
  groupConfigs.map((groupConfig, i) => {
1718
1757
  const groupTimeout = new Promise((_, reject) => {
1719
- const t = setTimeout(
1720
- () => reject(new Error(`Group ${i} timed out after ${GROUP_TIMEOUT_MS}ms`)),
1721
- GROUP_TIMEOUT_MS
1722
- );
1758
+ const t = setTimeout(() => {
1759
+ const files = Object.keys(groupConfig.fsTree).map(
1760
+ (f) => f.replace(`${groupConfig.projectRoot}/`, "")
1761
+ );
1762
+ reject(
1763
+ new Error(
1764
+ `Group ${i} timed out after ${GROUP_TIMEOUT_MS / 1e3}s in phase '${groupConfig._phase ?? "unknown"}'
1765
+ Files: ${files.join(", ")}`
1766
+ )
1767
+ );
1768
+ }, GROUP_TIMEOUT_MS);
1723
1769
  t.unref();
1724
1770
  });
1725
1771
  return Promise.race([
1726
1772
  (async () => {
1773
+ groupConfig._phase = "connecting";
1727
1774
  const connections = await setupBrowser(groupConfig, groupCachedContents[i], browser);
1728
1775
  groupConfig.expressApp = connections.server;
1729
1776
  if (config.before) {
@@ -1881,7 +1928,7 @@ init_color();
1881
1928
  var package_default = {
1882
1929
  name: "qunitx-cli",
1883
1930
  type: "module",
1884
- version: "0.11.0",
1931
+ version: "0.15.0",
1885
1932
  description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
1886
1933
  author: "Izel Nakri",
1887
1934
  license: "MIT",
@@ -1911,8 +1958,10 @@ var package_default = {
1911
1958
  "changelog:preview": "git-cliff",
1912
1959
  "changelog:update": "git-cliff --output CHANGELOG.md",
1913
1960
  postinstall: "PLAYWRIGHT_SKIP_DOWNLOAD=true deno install --allow-scripts=npm:playwright-core || true",
1914
- test: `node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test --test-concurrency=$(node -p 'require("os").availableParallelism()') test/**/*-test.ts`,
1915
- "test:browser": `node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test --test-concurrency=$(node -p 'require("os").availableParallelism()') test/flags/*-test.ts test/inputs/*-test.ts`,
1961
+ test: "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test test/**/*-test.ts",
1962
+ "test:debug": "node --experimental-strip-types test/setup.ts && QUNITX_DEBUG=1 node --experimental-strip-types --test test/**/*-test.ts",
1963
+ dev: "node --experimental-strip-types test/setup.ts && node --experimental-strip-types --test --watch test/**/*-test.ts",
1964
+ "test:browser": "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test test/flags/*-test.ts test/inputs/*-test.ts",
1916
1965
  "test:release": "bash scripts/test-release.sh",
1917
1966
  "test:sanity-first": "./cli.ts test/helpers/failing-tests.js test/helpers/failing-tests.ts",
1918
1967
  "test:sanity-second": "./cli.ts test/helpers/passing-tests.js test/helpers/passing-tests.ts"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qunitx-cli",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.15.0",
5
5
  "description": "Browser runner for QUnitx: run your qunitx tests in google-chrome",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",
@@ -31,8 +31,10 @@
31
31
  "changelog:preview": "git-cliff",
32
32
  "changelog:update": "git-cliff --output CHANGELOG.md",
33
33
  "postinstall": "PLAYWRIGHT_SKIP_DOWNLOAD=true deno install --allow-scripts=npm:playwright-core || true",
34
- "test": "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test --test-concurrency=$(node -p 'require(\"os\").availableParallelism()') test/**/*-test.ts",
35
- "test:browser": "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test --test-concurrency=$(node -p 'require(\"os\").availableParallelism()') test/flags/*-test.ts test/inputs/*-test.ts",
34
+ "test": "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test test/**/*-test.ts",
35
+ "test:debug": "node --experimental-strip-types test/setup.ts && QUNITX_DEBUG=1 node --experimental-strip-types --test test/**/*-test.ts",
36
+ "dev": "node --experimental-strip-types test/setup.ts && node --experimental-strip-types --test --watch test/**/*-test.ts",
37
+ "test:browser": "node --experimental-strip-types test/setup.ts && FORCE_COLOR=0 node --experimental-strip-types --test test/flags/*-test.ts test/inputs/*-test.ts",
36
38
  "test:release": "bash scripts/test-release.sh",
37
39
  "test:sanity-first": "./cli.ts test/helpers/failing-tests.js test/helpers/failing-tests.ts",
38
40
  "test:sanity-second": "./cli.ts test/helpers/passing-tests.js test/helpers/passing-tests.ts"