qunitx-cli 0.21.3 → 0.22.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/README.md +80 -15
- package/dist/cli.js +78 -25
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -14,8 +14,9 @@ output to the terminal.
|
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
17
|
-
- Runs `.js
|
|
18
|
-
- TypeScript
|
|
17
|
+
- Runs `.js`, `.ts`, `.jsx`, and `.tsx` test files in headless Chrome, Firefox, or WebKit (Playwright + esbuild)
|
|
18
|
+
- TypeScript and JSX work with zero configuration — esbuild handles transpilation, including the React 17+ automatic JSX runtime
|
|
19
|
+
- Bring your own esbuild plugins through `package.json` for `.vue`, `.svelte`, and other custom loaders
|
|
19
20
|
- Inline source maps for accurate stack traces pointing to original source files
|
|
20
21
|
- Streams TAP-formatted output to the terminal in real time
|
|
21
22
|
- Concurrent mode (default) splits test files across all CPU cores for fast parallel runs
|
|
@@ -165,29 +166,93 @@ All CLI flags can also be set in `package.json` under the `qunitx` key, so you d
|
|
|
165
166
|
"qunitx": {
|
|
166
167
|
"inputs": ["test/**/*-test.js", "test/**/*-test.ts"],
|
|
167
168
|
"htmlPaths": ["test/tests.html"],
|
|
168
|
-
"extensions": ["js", "ts"],
|
|
169
|
+
"extensions": ["js", "ts", "jsx", "tsx"],
|
|
169
170
|
"output": "tmp",
|
|
170
171
|
"timeout": 20000,
|
|
171
172
|
"failFast": false,
|
|
172
173
|
"port": 1234,
|
|
173
|
-
"browser": "chromium"
|
|
174
|
+
"browser": "chromium",
|
|
175
|
+
"plugins": []
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
178
|
```
|
|
177
179
|
|
|
178
|
-
| Key | Default
|
|
179
|
-
| ------------ |
|
|
180
|
-
| `inputs` | `[]`
|
|
181
|
-
| `htmlPaths` | `[]`
|
|
182
|
-
| `extensions` | `["js", "ts"]`
|
|
183
|
-
| `output` | `"tmp"`
|
|
184
|
-
| `timeout` | `20000`
|
|
185
|
-
| `failFast` | `false`
|
|
186
|
-
| `port` | `1234`
|
|
187
|
-
| `browser` | `"chromium"`
|
|
180
|
+
| Key | Default | Description |
|
|
181
|
+
| ------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
182
|
+
| `inputs` | `[]` | Glob patterns, file paths, or directories to use as test entry points. Merged with any paths given on the CLI. |
|
|
183
|
+
| `htmlPaths` | `[]` | Optional HTML templates to run tests inside. Any listed `.html` file that contains `{{qunitxScript}}` or other handlebars-style tokens is treated as a test runner template. |
|
|
184
|
+
| `extensions` | `["js", "ts", "jsx", "tsx"]` | File extensions tracked for test discovery (directory scans) and watch-mode rebuild triggers. Add `"mjs"`, `"cjs"`, or any other extension your project uses. |
|
|
185
|
+
| `output` | `"tmp"` | Directory where compiled test bundles are written. |
|
|
186
|
+
| `timeout` | `20000` | Maximum milliseconds to wait for the full test suite before timing out. |
|
|
187
|
+
| `failFast` | `false` | Stop the run after the first failing test. |
|
|
188
|
+
| `port` | `1234` | Preferred HTTP server port. qunitx auto-selects a free port if this one is taken. |
|
|
189
|
+
| `browser` | `"chromium"` | Browser engine to use: `"chromium"`, `"firefox"`, or `"webkit"`. Overridden by `--browser` on the CLI. |
|
|
190
|
+
| `plugins` | `[]` | esbuild plugin specifiers loaded from your `node_modules` and applied to the test bundle. See [esbuild plugins](#esbuild-plugins). |
|
|
188
191
|
|
|
189
192
|
CLI flags always override `package.json` values when both are present.
|
|
190
193
|
|
|
194
|
+
## JSX / TSX
|
|
195
|
+
|
|
196
|
+
`.jsx` and `.tsx` files are picked up automatically — no configuration needed. The bundle uses esbuild's automatic JSX runtime so React 17+ "no `import React`" code just works:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
// test/button-test.tsx
|
|
200
|
+
import { module, test } from 'qunitx';
|
|
201
|
+
import { flushSync } from 'react-dom';
|
|
202
|
+
import { createRoot } from 'react-dom/client';
|
|
203
|
+
import { Button } from '../src/button.tsx';
|
|
204
|
+
|
|
205
|
+
module('Button', (hooks) => {
|
|
206
|
+
let container;
|
|
207
|
+
hooks.beforeEach(() => {
|
|
208
|
+
container = document.createElement('div');
|
|
209
|
+
document.body.appendChild(container);
|
|
210
|
+
});
|
|
211
|
+
hooks.afterEach(() => container.remove());
|
|
212
|
+
|
|
213
|
+
test('renders the label', (assert) => {
|
|
214
|
+
flushSync(() => createRoot(container).render(<Button label="Save" />));
|
|
215
|
+
assert.equal(container.querySelector('button').textContent, 'Save');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Vue, Preact, Solid, and other JSX dialects work via a one-line override at the top of each file:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
/** @jsxImportSource vue */
|
|
224
|
+
import { createApp } from 'vue';
|
|
225
|
+
// ...JSX uses vue/jsx-runtime instead of react/jsx-runtime
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
You can also set `compilerOptions.jsxImportSource` in your `tsconfig.json` to apply the override across a directory.
|
|
229
|
+
|
|
230
|
+
## esbuild plugins
|
|
231
|
+
|
|
232
|
+
For file formats esbuild does not handle natively (e.g. `.vue` SFCs, `.svelte`), declare plugin specifiers in `package.json#qunitx.plugins`. qunitx dynamic-imports each one from your project's `node_modules` and passes it to the build:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"qunitx": {
|
|
237
|
+
"extensions": ["js", "ts", "jsx", "tsx", "vue"],
|
|
238
|
+
"plugins": [
|
|
239
|
+
"esbuild-plugin-vue-next",
|
|
240
|
+
["esbuild-svelte", { "compilerOptions": { "css": "injected" } }]
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Each entry is one of:
|
|
247
|
+
|
|
248
|
+
| Form | Behavior |
|
|
249
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| `"<package-name>"` | Imports the package. If the default export is a function, it's called with no arguments to produce the plugin; otherwise the export is used as the plugin. |
|
|
251
|
+
| `["<package-name>", <options>]` | Same, but the factory is called with `<options>` as its only argument. Use this form to pass plugin-specific configuration. |
|
|
252
|
+
| `"./relative/plugin.js"` | Loads a plugin you wrote yourself. Resolved against the project root (where your `package.json` lives). |
|
|
253
|
+
|
|
254
|
+
Don't forget to add the plugin's file extension(s) to `qunitx.extensions` so directory scans and watch-mode rebuilds pick them up.
|
|
255
|
+
|
|
191
256
|
### Environment variables
|
|
192
257
|
|
|
193
258
|
| Variable | Description |
|
|
@@ -218,7 +283,7 @@ Options:
|
|
|
218
283
|
--debug Print the server URL; pipe browser console to stdout
|
|
219
284
|
--timeout=<ms> Max ms to wait for the suite to finish [default: 20000]
|
|
220
285
|
--output=<dir> Directory for compiled test assets [default: ./tmp]
|
|
221
|
-
--extensions=<...> Comma-separated file extensions to track [default: js,ts]
|
|
286
|
+
--extensions=<...> Comma-separated file extensions to track [default: js,ts,jsx,tsx]
|
|
222
287
|
--before=<file> Script to run (and optionally await) before tests start
|
|
223
288
|
--after=<file> Script to run (and optionally await) after tests finish
|
|
224
289
|
--open, -o Open output in the test browser as soon as the bundle is ready
|
package/dist/cli.js
CHANGED
|
@@ -377,6 +377,21 @@ var init_color = __esm({
|
|
|
377
377
|
}
|
|
378
378
|
});
|
|
379
379
|
|
|
380
|
+
// lib/setup/default-project-config-values.ts
|
|
381
|
+
var defaultProjectConfigValues;
|
|
382
|
+
var init_default_project_config_values = __esm({
|
|
383
|
+
"lib/setup/default-project-config-values.ts"() {
|
|
384
|
+
defaultProjectConfigValues = {
|
|
385
|
+
output: "tmp",
|
|
386
|
+
timeout: 2e4,
|
|
387
|
+
failFast: false,
|
|
388
|
+
port: 1234,
|
|
389
|
+
extensions: ["js", "ts", "jsx", "tsx"],
|
|
390
|
+
browser: "chromium"
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
380
395
|
// lib/utils/read-template.ts
|
|
381
396
|
import fs3 from "node:fs/promises";
|
|
382
397
|
import { dirname, join as join2 } from "node:path";
|
|
@@ -1987,10 +2002,10 @@ var init_browser = __esm({
|
|
|
1987
2002
|
// lib/utils/open-output-in-browser.ts
|
|
1988
2003
|
import { spawn as spawn2 } from "node:child_process";
|
|
1989
2004
|
import path6 from "node:path";
|
|
1990
|
-
import { pathToFileURL } from "node:url";
|
|
2005
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
1991
2006
|
async function openOutputInBrowser(config) {
|
|
1992
2007
|
try {
|
|
1993
|
-
const outputFile = config.watch ? `http://localhost:${config.port}` :
|
|
2008
|
+
const outputFile = config.watch ? `http://localhost:${config.port}` : pathToFileURL2(path6.join(path6.resolve(config.projectRoot, config.output), "index.html")).href;
|
|
1994
2009
|
if (typeof config.open === "string") {
|
|
1995
2010
|
spawnDetached(config.open, [outputFile]);
|
|
1996
2011
|
return;
|
|
@@ -2036,10 +2051,10 @@ var init_time_counter = __esm({
|
|
|
2036
2051
|
});
|
|
2037
2052
|
|
|
2038
2053
|
// lib/utils/run-user-module.ts
|
|
2039
|
-
import { pathToFileURL as
|
|
2054
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
2040
2055
|
async function runUserModule(modulePath, params, scriptPosition) {
|
|
2041
2056
|
try {
|
|
2042
|
-
const func = await import(
|
|
2057
|
+
const func = await import(pathToFileURL3(modulePath).href);
|
|
2043
2058
|
if (func) {
|
|
2044
2059
|
func.default ? await func.default(params) : typeof func === "function" ? await func(params) : null;
|
|
2045
2060
|
}
|
|
@@ -2140,6 +2155,11 @@ async function buildTestBundle(config, cachedContent) {
|
|
|
2140
2155
|
legalComments: "none",
|
|
2141
2156
|
target: esbuildTarget(config.browser),
|
|
2142
2157
|
sourcemap,
|
|
2158
|
+
// jsx: 'automatic' is a no-op for .ts/.js files (extension-gated by esbuild) and emits
|
|
2159
|
+
// `import { jsx } from 'react/jsx-runtime'` for .tsx/.jsx files. Per-file overrides via
|
|
2160
|
+
// tsconfig's `jsxImportSource` or a `@jsxImportSource <pkg>` pragma cover Vue/Preact/Solid.
|
|
2161
|
+
jsx: "automatic",
|
|
2162
|
+
plugins: config.plugins,
|
|
2143
2163
|
// Signal the runtime that all test modules are registered. The runtime's maybeStart()
|
|
2144
2164
|
// waits for both this event and the WebSocket 'open' event before calling QUnit.start().
|
|
2145
2165
|
// Dispatching from the bundle (rather than from a script onload attr) is reliable across
|
|
@@ -2345,7 +2365,10 @@ async function buildAllGroupBundles(groupConfigs, groupCachedContents) {
|
|
|
2345
2365
|
in: `group-entry-${slotIndex}`,
|
|
2346
2366
|
out: `group-${slotIndex}`
|
|
2347
2367
|
})),
|
|
2348
|
-
|
|
2368
|
+
// groupEntryPlugin must run first — it owns the virtual entry-point modules every other
|
|
2369
|
+
// plugin sees. User plugins follow and apply to the resolved test files just like in
|
|
2370
|
+
// single-group mode (`buildTestBundle`).
|
|
2371
|
+
plugins: [groupEntryPlugin, ...groupConfigs[0].plugins ?? []],
|
|
2349
2372
|
nodePaths: ANCESTOR_NODE_MODULES,
|
|
2350
2373
|
bundle: true,
|
|
2351
2374
|
logLevel: "silent",
|
|
@@ -2357,6 +2380,7 @@ async function buildAllGroupBundles(groupConfigs, groupCachedContents) {
|
|
|
2357
2380
|
target: esbuildTarget(browser),
|
|
2358
2381
|
sourcemap,
|
|
2359
2382
|
write: false,
|
|
2383
|
+
jsx: "automatic",
|
|
2360
2384
|
footer: { js: 'window.dispatchEvent(new CustomEvent("qunitx:tests-ready"));' }
|
|
2361
2385
|
};
|
|
2362
2386
|
const hasSmallOutput = (result) => (result.outputFiles ?? []).some(
|
|
@@ -2425,6 +2449,8 @@ function buildFilteredTests(filteredTests, outputPath, config) {
|
|
|
2425
2449
|
legalComments: "none",
|
|
2426
2450
|
target: esbuildTarget(config.browser),
|
|
2427
2451
|
sourcemap,
|
|
2452
|
+
jsx: "automatic",
|
|
2453
|
+
plugins: config.plugins,
|
|
2428
2454
|
footer: { js: 'window.dispatchEvent(new CustomEvent("qunitx:tests-ready"));' }
|
|
2429
2455
|
},
|
|
2430
2456
|
needsDisk
|
|
@@ -2620,7 +2646,7 @@ import fs10 from "node:fs";
|
|
|
2620
2646
|
import { readdir, stat, lstat } from "node:fs/promises";
|
|
2621
2647
|
import path8 from "node:path";
|
|
2622
2648
|
function setupFileWatchers(testFileLookupPaths, config, onEventFunc, onFinishFunc) {
|
|
2623
|
-
const extensions = config.extensions ||
|
|
2649
|
+
const extensions = config.extensions || defaultProjectConfigValues.extensions;
|
|
2624
2650
|
const readyPromises = [];
|
|
2625
2651
|
const parentWatchers = [];
|
|
2626
2652
|
const rescanTimers = [];
|
|
@@ -2884,6 +2910,7 @@ var CHANGE_DEDUPE_MS, SYMLINK_POLL_INTERVAL_MS, OVERLAYFS_RENAME_RETRY_MS, RESCA
|
|
|
2884
2910
|
var init_file_watcher = __esm({
|
|
2885
2911
|
"lib/setup/file-watcher.ts"() {
|
|
2886
2912
|
init_color();
|
|
2913
|
+
init_default_project_config_values();
|
|
2887
2914
|
CHANGE_DEDUPE_MS = 10;
|
|
2888
2915
|
SYMLINK_POLL_INTERVAL_MS = 500;
|
|
2889
2916
|
OVERLAYFS_RENAME_RETRY_MS = 50;
|
|
@@ -3004,7 +3031,8 @@ __export(run_exports, {
|
|
|
3004
3031
|
run: () => run
|
|
3005
3032
|
});
|
|
3006
3033
|
import fs12 from "node:fs/promises";
|
|
3007
|
-
import { normalize } from "node:path";
|
|
3034
|
+
import { join as join3, normalize } from "node:path";
|
|
3035
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
3008
3036
|
import { availableParallelism } from "node:os";
|
|
3009
3037
|
async function run(config) {
|
|
3010
3038
|
const browserPromise = config.watch ? null : launchBrowser(config);
|
|
@@ -3296,7 +3324,7 @@ async function addCachedContentMainHTML(projectRoot, cachedContent) {
|
|
|
3296
3324
|
} else {
|
|
3297
3325
|
const html = await readTemplate("setup/tests.hbs");
|
|
3298
3326
|
cachedContent.mainHTML = { filePath: `${projectRoot}/test/tests.html`, html };
|
|
3299
|
-
cachedContent.assets.add(
|
|
3327
|
+
cachedContent.assets.add(join3(resolveQunitxRoot(projectRoot), "vendor/qunit.css"));
|
|
3300
3328
|
}
|
|
3301
3329
|
return cachedContent;
|
|
3302
3330
|
}
|
|
@@ -3369,6 +3397,12 @@ function normalizeInternalAssetPathFromHTML(projectRoot, assetPath, htmlPath) {
|
|
|
3369
3397
|
const currentDirectory = htmlPath ? htmlPath.split("/").slice(0, -1).join("/") : projectRoot;
|
|
3370
3398
|
return assetPath.startsWith("./") ? normalize(`${currentDirectory}/${assetPath.slice(2)}`) : normalize(`${currentDirectory}/${assetPath}`);
|
|
3371
3399
|
}
|
|
3400
|
+
function resolveQunitxRoot(projectRoot) {
|
|
3401
|
+
const mainEntry = createRequire2(`${projectRoot}/package.json`).resolve("qunitx");
|
|
3402
|
+
const match = /^(.*[\\/]qunitx)[\\/]/.exec(mainEntry);
|
|
3403
|
+
if (!match) throw new Error(`Could not derive qunitx root from ${mainEntry}`);
|
|
3404
|
+
return match[1];
|
|
3405
|
+
}
|
|
3372
3406
|
var WATCH_NAV_TIMEOUT_MS, PAGE_CLOSE_GRACE_MS, STDOUT_FLUSH_GRACE_MS, KEEP_ALIVE_INTERVAL_MS, EXIT_CODE_SIGTERM;
|
|
3373
3407
|
var init_run = __esm({
|
|
3374
3408
|
"lib/commands/run.ts"() {
|
|
@@ -3408,7 +3442,7 @@ init_color();
|
|
|
3408
3442
|
var package_default = {
|
|
3409
3443
|
name: "qunitx-cli",
|
|
3410
3444
|
type: "module",
|
|
3411
|
-
version: "0.
|
|
3445
|
+
version: "0.22.0",
|
|
3412
3446
|
description: "Browser runner for QUnitx: run your qunitx tests in google-chrome",
|
|
3413
3447
|
author: "Izel Nakri",
|
|
3414
3448
|
license: "MIT",
|
|
@@ -3465,8 +3499,11 @@ var package_default = {
|
|
|
3465
3499
|
devDependencies: {
|
|
3466
3500
|
"js-yaml": "^4.1.1",
|
|
3467
3501
|
prettier: "^3.8.3",
|
|
3468
|
-
qunitx: "^1.2.
|
|
3469
|
-
|
|
3502
|
+
qunitx: "^1.2.9",
|
|
3503
|
+
react: "^19.2.5",
|
|
3504
|
+
"react-dom": "^19.2.5",
|
|
3505
|
+
typescript: "^6.0.3",
|
|
3506
|
+
vue: "^3.5.33"
|
|
3470
3507
|
},
|
|
3471
3508
|
volta: {
|
|
3472
3509
|
node: "24.14.0"
|
|
@@ -3502,7 +3539,7 @@ ${color("--timeout")} : change default timeout per test case
|
|
|
3502
3539
|
${color("--output")} : folder to distribute built qunitx html and js that a webservers can run[default: tmp]
|
|
3503
3540
|
${color("--failFast")} : run the target file or folders with immediate abort if a single test fails
|
|
3504
3541
|
${color("--port")} : HTTP server port (auto-selects a free port if the given port is taken)[default: 1234]
|
|
3505
|
-
${color("--extensions")} : comma-separated file extensions to track for discovery and watch-mode rebuilds[default: js,ts]
|
|
3542
|
+
${color("--extensions")} : comma-separated file extensions to track for discovery and watch-mode rebuilds[default: js,ts,jsx,tsx]
|
|
3506
3543
|
${color("--browser")} : browser engine to run tests in: chromium, firefox, webkit[default: chromium]
|
|
3507
3544
|
${color("--before")} : run a script before the tests(i.e start a new web server before tests)
|
|
3508
3545
|
${color("--after")} : run a script after the tests(i.e save test results to a file)
|
|
@@ -3561,17 +3598,8 @@ async function findProjectRoot() {
|
|
|
3561
3598
|
}
|
|
3562
3599
|
}
|
|
3563
3600
|
|
|
3564
|
-
// lib/setup/default-project-config-values.ts
|
|
3565
|
-
var defaultProjectConfigValues = {
|
|
3566
|
-
output: "tmp",
|
|
3567
|
-
timeout: 2e4,
|
|
3568
|
-
failFast: false,
|
|
3569
|
-
port: 1234,
|
|
3570
|
-
extensions: ["js", "ts"],
|
|
3571
|
-
browser: "chromium"
|
|
3572
|
-
};
|
|
3573
|
-
|
|
3574
3601
|
// lib/commands/init.ts
|
|
3602
|
+
init_default_project_config_values();
|
|
3575
3603
|
init_read_template();
|
|
3576
3604
|
async function initializeProject() {
|
|
3577
3605
|
const projectRoot = await findProjectRoot();
|
|
@@ -3658,13 +3686,17 @@ function pathToModuleName(filePath) {
|
|
|
3658
3686
|
}
|
|
3659
3687
|
|
|
3660
3688
|
// lib/setup/config.ts
|
|
3689
|
+
init_default_project_config_values();
|
|
3661
3690
|
import fs7 from "node:fs/promises";
|
|
3691
|
+
import { createRequire } from "node:module";
|
|
3692
|
+
import { pathToFileURL } from "node:url";
|
|
3662
3693
|
|
|
3663
3694
|
// lib/setup/fs-tree.ts
|
|
3695
|
+
init_default_project_config_values();
|
|
3664
3696
|
import fs6, { glob as fsGlob } from "node:fs/promises";
|
|
3665
3697
|
import path3 from "node:path";
|
|
3666
3698
|
async function buildFSTree(fileAbsolutePaths, config = {}) {
|
|
3667
|
-
const targetExtensions = config.extensions ||
|
|
3699
|
+
const targetExtensions = config.extensions || defaultProjectConfigValues.extensions;
|
|
3668
3700
|
const fsTree = {};
|
|
3669
3701
|
await Promise.all(
|
|
3670
3702
|
fileAbsolutePaths.map(async (fileAbsolutePath) => {
|
|
@@ -3849,11 +3881,13 @@ async function setupConfig() {
|
|
|
3849
3881
|
const projectRoot = await findProjectRoot();
|
|
3850
3882
|
const cliConfigFlags = parseCliFlags(projectRoot);
|
|
3851
3883
|
const projectPackageJSON = await readConfigFromPackageJSON(projectRoot);
|
|
3884
|
+
const { plugins: rawPlugins, ...userQunitx } = projectPackageJSON.qunitx ?? {};
|
|
3885
|
+
const pluginsPromise = resolvePlugins(rawPlugins, projectRoot);
|
|
3852
3886
|
const inputs2 = cliConfigFlags.inputs.concat(readInputsFromPackageJSON(projectPackageJSON));
|
|
3853
3887
|
const config = {
|
|
3854
3888
|
...defaultProjectConfigValues,
|
|
3855
3889
|
htmlPaths: [],
|
|
3856
|
-
...
|
|
3890
|
+
...userQunitx,
|
|
3857
3891
|
...cliConfigFlags,
|
|
3858
3892
|
projectRoot,
|
|
3859
3893
|
inputs: inputs2,
|
|
@@ -3874,7 +3908,10 @@ async function setupConfig() {
|
|
|
3874
3908
|
_onTestsJsServed: null
|
|
3875
3909
|
};
|
|
3876
3910
|
config.htmlPaths = normalizeHTMLPaths(config.projectRoot, config.htmlPaths);
|
|
3877
|
-
config.fsTree = await
|
|
3911
|
+
[config.fsTree, config.plugins] = await Promise.all([
|
|
3912
|
+
buildFSTree(config.testFileLookupPaths, config),
|
|
3913
|
+
pluginsPromise
|
|
3914
|
+
]);
|
|
3878
3915
|
return config;
|
|
3879
3916
|
}
|
|
3880
3917
|
async function readConfigFromPackageJSON(projectRoot) {
|
|
@@ -3888,6 +3925,22 @@ function readInputsFromPackageJSON(packageJSON) {
|
|
|
3888
3925
|
const qunitx = packageJSON.qunitx;
|
|
3889
3926
|
return qunitx && qunitx.inputs ? qunitx.inputs : [];
|
|
3890
3927
|
}
|
|
3928
|
+
function resolvePlugins(raw, projectRoot) {
|
|
3929
|
+
if (raw == null) return Promise.resolve([]);
|
|
3930
|
+
if (!Array.isArray(raw)) {
|
|
3931
|
+
console.error(`# qunitx: package.json#qunitx.plugins must be an array`);
|
|
3932
|
+
process.exit(1);
|
|
3933
|
+
}
|
|
3934
|
+
const projectRequire = createRequire(`${projectRoot}/package.json`);
|
|
3935
|
+
return Promise.all(
|
|
3936
|
+
raw.map(async (entry) => {
|
|
3937
|
+
const [spec, options] = Array.isArray(entry) ? entry : [entry];
|
|
3938
|
+
const mod = await import(pathToFileURL(projectRequire.resolve(spec)).href);
|
|
3939
|
+
const exported = mod.default ?? mod;
|
|
3940
|
+
return typeof exported === "function" ? exported(options) : exported;
|
|
3941
|
+
})
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3891
3944
|
|
|
3892
3945
|
// cli.ts
|
|
3893
3946
|
process4.title = "qunitx";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qunitx-cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.22.0",
|
|
5
5
|
"description": "Browser runner for QUnitx: run your qunitx tests in google-chrome",
|
|
6
6
|
"author": "Izel Nakri",
|
|
7
7
|
"license": "MIT",
|
|
@@ -58,8 +58,11 @@
|
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"js-yaml": "^4.1.1",
|
|
60
60
|
"prettier": "^3.8.3",
|
|
61
|
-
"qunitx": "^1.2.
|
|
62
|
-
"
|
|
61
|
+
"qunitx": "^1.2.9",
|
|
62
|
+
"react": "^19.2.5",
|
|
63
|
+
"react-dom": "^19.2.5",
|
|
64
|
+
"typescript": "^6.0.3",
|
|
65
|
+
"vue": "^3.5.33"
|
|
63
66
|
},
|
|
64
67
|
"volta": {
|
|
65
68
|
"node": "24.14.0"
|