qunitx 0.9.3 → 0.12.2

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/Makefile ADDED
@@ -0,0 +1,28 @@
1
+ .PHONY: check test lint build release
2
+
3
+ check: lint test
4
+
5
+ lint:
6
+ npm run lint
7
+
8
+ test:
9
+ npm test
10
+
11
+ build:
12
+ npm run build
13
+
14
+ # Lint, bump version, update changelog, commit, tag, push, publish to npm.
15
+ # CI then creates the GitHub release.
16
+ # Usage: make release (defaults to patch)
17
+ # make release LEVEL=minor|major
18
+ LEVEL ?= patch
19
+ release:
20
+ @npm whoami 2>/dev/null || npm login
21
+ npm run lint
22
+ npm version $(LEVEL) --no-git-tag-version
23
+ npm run changelog:update
24
+ git add package.json package-lock.json CHANGELOG.md
25
+ git commit -m "Release $$(node -p 'require("./package.json").version')"
26
+ git tag "v$$(node -p 'require("./package.json").version')"
27
+ git push && git push --tags
28
+ npm publish --access public
package/README.md CHANGED
@@ -1,153 +1,226 @@
1
- ![docker-based-ci](https://github.com/izelnakri/qunitx/workflows/docker-based-ci/badge.svg)
2
- [![npm version](https://badge.fury.io/js/qunitx.svg)](https://badge.fury.io/js/qunitx)
1
+ [![CI](https://github.com/izelnakri/qunitx/actions/workflows/ci.yml/badge.svg)](https://github.com/izelnakri/qunitx/actions/workflows/ci.yml)
2
+ [![npm](https://img.shields.io/npm/v/qunitx)](https://www.npmjs.com/package/qunitx)
3
+ [![npm downloads](https://img.shields.io/npm/dm/qunitx)](https://www.npmjs.com/package/qunitx)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![Ask me anything](https://img.shields.io/badge/ask%20me-anything-1abc9c.svg)](https://github.com/izelnakri/qunitx/issues)
6
+ [![Sponsor](https://img.shields.io/badge/sponsor-%E2%99%A5-pink)](https://github.com/sponsors/izelnakri)
3
7
 
4
8
  # QUnitX
5
9
 
6
- ***Truly universal testing for JavaScript with the oldest, most mature & flexible
7
- testing API in the JavaScript ecosystem. Run the same test file in node.js, deno or in the browser***
10
+ **The oldest, most battle-tested JavaScript test API now universal.**
8
11
 
9
- Your test JS/TS file(s) can now run interchangably in different runtimes with
10
- the default test runner of node.js or deno, or with a browser runner of your
11
- choice!
12
+ Run the **same test file** in Node.js, Deno, and the browser without changes.
13
+ Zero dependencies. No config needed for Node. TypeScript works out of the box.
12
14
 
13
- [![asciicast](https://asciinema.org/a/597066.svg)](https://asciinema.org/a/597066?autoplay=1)
15
+ ---
14
16
 
15
- In the browser you can use the same browser test/filter UI of
16
- [QUnit](https://github.com/qunitjs/qunit) and share the web links with your
17
- colleagues thanks to the test filters through query params feature of QUnit:
17
+ ## Why QUnit?
18
18
 
19
- [QUnit Test Suite Example](https://objectmodel.js.org/test/?moduleId=6e15ed5f&moduleId=950ec9c5)
19
+ QUnit was created in 2008 by the jQuery team. While newer frameworks come and go,
20
+ QUnit has quietly accumulated 16+ years of real-world edge-case handling that younger
21
+ tools are still catching up to. Its assertion API is the most mature in the JavaScript
22
+ ecosystem:
20
23
 
21
- **UI visual automated tests also possible with QUnit!**
24
+ - **`assert.deepEqual`** handles circular references, prototype chains, Sets, Maps,
25
+ typed arrays, Dates, RegExps, and getters correctly
26
+ - **`assert.throws` / `assert.rejects`** — match errors by constructor, regex, or custom validator
27
+ - **`assert.step` / `assert.verifySteps`** — declarative execution-order verification;
28
+ catches missing async callbacks that other frameworks silently swallow
29
+ - **`assert.expect(n)`** — fails the test if exactly _n_ assertions didn't run;
30
+ invaluable for async code where missing assertions would otherwise pass silently
31
+ - **Hooks** — `before`, `beforeEach`, `afterEach`, `after` with correct FIFO/LIFO ordering,
32
+ properly scoped across nested modules
33
+ - **Shareable browser URLs** — the QUnit browser UI filters tests via query params, so you can
34
+ share `https://yourapp.test/?moduleId=abc123` with a colleague and they see exactly the same view
22
35
 
23
- ![QunitX terminal output](https://raw.githubusercontent.com/izelnakri/qunitx/main/docs/qunitx-help-stdout.png)
36
+ QUnitX wraps this API to work with **Node.js's built-in `node:test` runner** and
37
+ **Deno's native test runner** — no Jest, Vitest, or other framework needed.
24
38
 
25
- ### Installation: Node & Deno
39
+ ---
26
40
 
27
- This is a 0-dependency test library that runs code in your target runtime(node,
28
- deno or browser) test runner. Since a default test runner is a new feature of node.js, please use node.js v20.3+.
41
+ ## Demo
29
42
 
30
- In order to use qunitx to convert qunit tests files please change:
43
+ > Left window: `node --test` and `deno test` running the same file.
44
+ > Right window: QUnit browser UI with filterable, shareable test results.
31
45
 
32
- ```js
33
- import { module, test } from 'qunit';
46
+ <!-- Demo GIF: see docs/demo.tape (VHS script) for terminal portion.
47
+ For the combined terminal + browser recording, see "Recording the demo" below. -->
48
+ ![QUnitX demo](https://raw.githubusercontent.com/izelnakri/qunitx/main/docs/demo.gif)
34
49
 
35
- // to:
36
- import { module, test } from 'qunitx';
50
+ Live browser UI example (click to see filterable QUnit test suite):
51
+
52
+ [objectmodel.js.org/test/?moduleId=6e15ed5f](https://objectmodel.js.org/test/?moduleId=6e15ed5f&moduleId=950ec9c5)
53
+
54
+ ![QUnitX CLI help](https://raw.githubusercontent.com/izelnakri/qunitx/main/docs/qunitx-help-stdout.png)
55
+
56
+ ---
57
+
58
+ ## Installation
59
+
60
+ ```sh
61
+ npm install qunitx
37
62
  ```
38
63
 
39
- Example:
64
+ Requires **Node.js >= 22** (LTS) or **Deno >= 2**.
65
+
66
+ ---
67
+
68
+ ## Quick start
40
69
 
41
70
  ```js
42
- // in some-test.js: (typescript also works)
71
+ // math-test.js (works in Node, Deno, and browser unchanged)
43
72
  import { module, test } from 'qunitx';
44
- import $ from 'jquery';
45
73
 
46
- module('Basic sanity check', function (hooks) {
47
- test('it works', function (assert) {
48
- assert.equal(true, true);
74
+ module('Math utilities', (hooks) => {
75
+ hooks.before((assert) => {
76
+ assert.step('setup complete');
49
77
  });
50
78
 
51
- module('More advanced cases', function (hooks) {
52
- test('deepEqual works', function (assert) {
53
- assert.deepEqual({ username: 'izelnakri' }, { username: 'izelnakri' });
54
- });
55
- test('can import ES & npm modules', function (assert) {
56
- assert.ok(Object.keys($));
79
+ test('addition', (assert) => {
80
+ assert.equal(2 + 2, 4);
81
+ assert.notEqual(2 + 2, 5);
82
+ });
83
+
84
+ test('deepEqual', (assert) => {
85
+ assert.deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] });
86
+ });
87
+
88
+ module('Async', () => {
89
+ test('resolves correctly', async (assert) => {
90
+ const result = await Promise.resolve(42);
91
+ assert.strictEqual(result, 42);
57
92
  });
58
93
  });
59
94
  });
60
95
  ```
61
96
 
62
- ```zsh
63
- # you can run the test in node with ES modules package.json{ "type": "module" }
64
- $ node --test some-test.js
97
+ ### Node.js
98
+
99
+ ```sh
100
+ # No extra dependencies — uses the Node built-in test runner
101
+ node --test math-test.js
65
102
 
66
- # TypeScript also works, make sure on node.js mode, tsconfig.json exists with compilerOptions.module & compilerOptions.moduleResolution set to "NodeNext":
67
- $ node --loader=ts-node/esm/transpile-only --test some-test.ts
103
+ # Watch mode (re-runs on save)
104
+ node --test --watch math-test.js
68
105
 
69
- # You can use the new watch mode of node.js to watch for files or folder patterns
70
- $ node --test --watch some-test.js some-folder/*.js
106
+ # Glob pattern
107
+ node --test --watch 'test/**/*.js'
71
108
 
72
- # You can also run this test on deno. Unfortunately today deno requires one extra step to create a deno.json file:
73
- $ echo '{"imports": { "qunitx": "https://esm.sh/qunitx/shims/deno/index.js" } }' > deno.json
109
+ # TypeScript (tsconfig.json with moduleResolution: NodeNext required)
110
+ node --import=tsx/esm --test math-test.ts
74
111
 
75
- # then run the tests in default deno test runner:
76
- $ deno test some-test.js
112
+ # Code coverage
113
+ npx c8 node --test math-test.js
77
114
  ```
78
115
 
79
- ### Installation: Browser
116
+ ### Deno
80
117
 
81
- QUnitX mainly proxies to [QUnit
82
- API](https://api.qunitjs.com/QUnit/module/#hooks-on-nested-modules) in browser.
83
- You can use [QUnitX CLI](https://github.com/izelnakri/qunitx-cli) to get your
84
- browser tests to stdout/CI or use the watch mode during the development.
118
+ ```sh
119
+ # One-time: create a deno.json import map
120
+ echo '{"imports": {"qunitx": "https://esm.sh/qunitx/shims/deno/index.js"}}' > deno.json
85
121
 
86
- ```zsh
87
- # Install QUnitX browser runner/cli:
88
- $ npm install -g qunitx-cli
89
- $ qunitx
90
- $ qunitx some-test.js
122
+ # Run
123
+ deno test math-test.js
91
124
 
92
- # with browser output enabled:
93
- $ qunitx some-test.js --debug
125
+ # With explicit permissions
126
+ deno test --allow-read --allow-env math-test.js
127
+ ```
94
128
 
129
+ ### Browser
130
+
131
+ Use [qunitx-cli](https://github.com/izelnakri/qunitx-cli) to get browser test output
132
+ in your terminal / CI, or to open the live QUnit UI during development:
133
+
134
+ ```sh
135
+ npm install -g qunitx-cli
136
+
137
+ # Headless (CI-friendly — outputs TAP to stdout)
138
+ qunitx math-test.js
139
+
140
+ # Open QUnit browser UI alongside terminal output
141
+ qunitx math-test.js --debug
95
142
  ```
96
143
 
97
- ### Concurrency options
144
+ The browser UI lets you:
145
+ - Filter by module or test name (filter state is preserved in the URL)
146
+ - Share a link that reproduces the exact filtered view with a colleague
147
+ - Re-run individual tests by clicking them
148
+ - See full assertion diffs inline
149
+
150
+ ---
98
151
 
99
- QUnitX API accepts an optional options object as 2nd argument to:
100
- - `QUnit.module(testName, optionsOrHandler?, handler?)`
101
- - `QUnit.test(testName, optionsOrHandler?, handler?)`
152
+ ## Migrating from QUnit
102
153
 
103
- So you can run tests in parallel(default) or in series. You can even run them
104
- through the [node.js test runner run()
105
- api](https://nodejs.org/api/test.html#runoptions):
154
+ One import line is all that changes:
106
155
 
107
156
  ```js
108
- // in some-test.js: (typescript also works)
157
+ // Before:
158
+ import { module, test } from 'qunit';
159
+
160
+ // After:
109
161
  import { module, test } from 'qunitx';
110
- import $ from 'jquery';
162
+ ```
111
163
 
112
- module('Basic sanity check', function (hooks) {
113
- test('it works', { concurrency: false }, function (assert) {
114
- assert.equal(true, true);
115
- });
164
+ ---
116
165
 
117
- module('More advanced cases', { concurrency: false, permissions: { read: true }, sanitizeExit: false }, function (hooks) {
118
- test('deepEqual works', function (assert) {
119
- assert.deepEqual({ username: 'izelnakri' }, { username: 'izelnakri' });
120
- });
121
- test('can import ES & npm modules', function (assert) {
122
- assert.ok(Object.keys($));
123
- });
166
+ ## Concurrency options
167
+
168
+ `module()` and `test()` accept an optional options object forwarded directly to the underlying
169
+ Node / Deno test runner:
170
+
171
+ ```js
172
+ import { module, test } from 'qunitx';
173
+
174
+ // Run tests in this module serially
175
+ module('Serial suite', { concurrency: false }, (hooks) => {
176
+ test('first', (assert) => { assert.ok(true); });
177
+ test('second', (assert) => { assert.ok(true); });
178
+ });
179
+
180
+ // Deno-specific: permissions, sanitizeExit, etc.
181
+ module('Deno file access', { permissions: { read: true }, sanitizeExit: false }, (hooks) => {
182
+ test('reads a file', async (assert) => {
183
+ const text = await Deno.readTextFile('./README.md');
184
+ assert.ok(text.length > 0);
124
185
  });
125
186
  });
126
187
  ```
127
188
 
128
- ### Code coverage
189
+ ---
190
+
191
+ ## How it works
192
+
193
+ | Runtime | Adapter |
194
+ |---------|---------|
195
+ | Node.js | Wraps `node:test` `describe` / `it` with QUnit lifecycle |
196
+ | Deno | Wraps Deno BDD helpers with the same QUnit lifecycle |
197
+ | Browser | Thin re-export of QUnit's native browser API |
198
+
199
+ The browser path is literally QUnit itself, so you get full QUnit compatibility:
200
+ plugins, custom reporters, the event API (`QUnit.on`, `QUnit.done`, etc.), and the
201
+ familiar browser UI with zero extra layers.
129
202
 
130
- Since QUnitX proxies to default node.js test runner in when executed with node,
131
- you can use any code coverage tool you like. When running the tests in
132
- `qunit`(the browser mode) code coverage support is limited.
203
+ ---
133
204
 
134
- ```zsh
135
- $ c8 node --test test/attachments test/user
205
+ ## Code coverage
206
+
207
+ ```sh
208
+ # Node (any c8-compatible reporter)
209
+ npx c8 node --test test/
210
+
211
+ # View HTML report
212
+ npx c8 --reporter=html node --test test/ && open coverage/index.html
136
213
  ```
137
214
 
138
- You can browse [c8 documentation](https://github.com/bcoe/c8) for all
139
- configuration options.
140
-
141
- Implementing code coverage for the browser mode is currently not possible
142
- because we use esbuild --bundle feature to create a JS bundles for testing in
143
- the browser, this could be instrumented with `puppeteer-to-istanbul` however
144
- instrumentation includes transpiled npm imports of `qunitx` and other potential
145
- npm imports developer includes in the code, this cannot be filtered since
146
- potential filtering can only occur after the `esbuild` bundling. When chrome
147
- browser and puppeteer fully supports ES asset maps we can remove esbuild from
148
- the browser mode, run everything in deno and make instrumentation for code
149
- coverage possible with the default v8 instrumentation.
150
-
151
- Esbuild plugin interface is an ongoing development, we might be able to figure
152
- out a way to generate this instrumentation with esbuild in the future, which
153
- could allow code coverage for --browser mode.
215
+ Browser-mode coverage is limited because qunitx-cli bundles test files with esbuild.
216
+ Native ES import maps support in Puppeteer/Chrome would eliminate the bundling step
217
+ and unlock v8 instrumentation for browser coverage.
218
+
219
+ ---
220
+
221
+ ## Links
222
+
223
+ - [QUnit API reference](https://api.qunitjs.com)
224
+ - [qunitx-cli](https://github.com/izelnakri/qunitx-cli) browser runner / CI reporter
225
+ - [Node.js test runner docs](https://nodejs.org/api/test.html)
226
+ - [Deno testing docs](https://docs.deno.com/runtime/fundamentals/testing/)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "qunitx",
3
3
  "type": "module",
4
- "version": "0.9.3",
4
+ "version": "0.12.2",
5
5
  "description": "A universal test framework for testing any js file on node.js, browser or deno with QUnit API",
6
6
  "author": "Izel Nakri",
7
7
  "license": "MIT",
@@ -38,12 +38,13 @@
38
38
  "frontend",
39
39
  "backend",
40
40
  "qunit",
41
+ "qunit-plugin",
41
42
  "node",
42
43
  "deno",
43
44
  "browser"
44
45
  ],
45
46
  "engines": {
46
- "node": ">=20.3.0"
47
+ "node": ">=22.0.0"
47
48
  },
48
49
  "imports": {
49
50
  "qunitx": {
@@ -59,52 +60,37 @@
59
60
  },
60
61
  "repository": {
61
62
  "type": "git",
62
- "url": "https://github.com/izelnakri/qunitx.git"
63
+ "url": "git+https://github.com/izelnakri/qunitx.git"
63
64
  },
64
65
  "scripts": {
66
+ "lint": "prettier --check \"test/**/*.js\" \"*.js\" \"package.json\"",
67
+ "lint:fix": "prettier --write \"test/**/*.js\" \"*.js\" \"package.json\"",
65
68
  "build": "node build.js",
66
69
  "run:all": "npm run run:node && npm run run:deno",
67
70
  "run:node": "node --test test/helpers/passing-tests.js && node --test test/helpers/failing-tests.js",
68
71
  "run:deno": "deno test test/helpers/passing-tests.js && deno test test/helpers/failing-tests.js",
69
- "changelog:unreleased": "node_modules/.bin/auto-changelog --stdout --commit-limit false --package --unreleased-only --hide-credit --sort-commits date-desc",
70
- "changelog:preview": "node_modules/.bin/auto-changelog --stdout --commit-limit false --package -u --sort-commits date-desc",
71
- "changelog:update": "node_modules/.bin/auto-changelog --commit-limit false --package --sort-commits date-desc",
72
+ "changelog:unreleased": "git-cliff --unreleased --strip all",
73
+ "changelog:preview": "git-cliff",
74
+ "changelog:update": "git-cliff --output CHANGELOG.md",
72
75
  "prepack": "npm run build",
73
- "release:alpha": "node_modules/.bin/release-it --preRelease=alpha --no-git.requireUpstream",
74
- "release:beta": "node_modules/.bin/release-it --preRelease=beta --no-git.requireUpstream",
75
- "release": "node_modules/.bin/release-it",
76
76
  "test": "npm run test:browser && npm run test:node && npm run test:deno",
77
77
  "test:dev": "npm run test | tee test-output.log",
78
- "test:browser": "node_modules/.bin/qunitx test/index.js --debug",
78
+ "test:browser": "qunitx test/index.js --debug",
79
79
  "test:deno": "deno test --allow-read --allow-env --allow-run test/index.js",
80
80
  "test:node": "node --test test/index.js"
81
81
  },
82
82
  "devDependencies": {
83
- "auto-changelog": "^2.4.0",
84
- "prettier": "^3.0.0",
85
- "qunit": "^2.19.4",
86
- "qunitx": "^0.9.1",
87
- "qunitx-cli": "^0.1.1",
88
- "release-it": "^16.1.3",
89
- "ts-node": ">=10.7.0"
83
+ "prettier": "^3.8.1",
84
+ "qunit": "^2.25.0",
85
+ "qunitx": "^0.9.3",
86
+ "qunitx-cli": "^0.5.0"
90
87
  },
91
88
  "volta": {
92
- "node": "20.5.0"
89
+ "node": "24.14.0"
93
90
  },
94
91
  "prettier": {
95
92
  "printWidth": 100,
96
93
  "singleQuote": true,
97
94
  "arrowParens": "always"
98
- },
99
- "release-it": {
100
- "git": {
101
- "changelog": "npm run changelog:unreleased"
102
- },
103
- "github": {
104
- "release": true
105
- },
106
- "hooks": {
107
- "after:bump": "npm run changelog:update"
108
- }
109
95
  }
110
96
  }
@@ -1,4 +1,4 @@
1
- import { AssertionError as DenoAssertionError } from "https://deno.land/std@0.192.0/testing/asserts.ts";
1
+ import { AssertionError as DenoAssertionError } from "jsr:@std/assert";
2
2
  import '../../vendor/qunit.js';
3
3
  import Assert from '../shared/assert.js';
4
4
  import ModuleContext from '../shared/module-context.js';
@@ -1,4 +1,4 @@
1
- import { describe, beforeAll, afterAll } from "https://deno.land/std@0.192.0/testing/bdd.ts";
1
+ import { describe, beforeAll, afterAll } from "jsr:@std/testing/bdd";
2
2
  import ModuleContext from '../shared/module-context.js';
3
3
 
4
4
  // NOTE: node.js beforeEach & afterEach is buggy because the TestContext it has is NOT correct reference when called, it gets the last context
@@ -10,7 +10,7 @@ export default function module(moduleName, runtimeOptions, moduleContent) {
10
10
  let targetModuleContent = moduleContent ? moduleContent : runtimeOptions;
11
11
  let moduleContext = new ModuleContext(moduleName);
12
12
 
13
- return describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, async function () {
13
+ return describe(moduleName, { concurrency: true, ...targetRuntimeOptions }, function () {
14
14
  let beforeHooks = [];
15
15
  let afterHooks = [];
16
16
 
@@ -1,4 +1,4 @@
1
- import { it } from "https://deno.land/std@0.192.0/testing/bdd.ts";
1
+ import { it } from "jsr:@std/testing/bdd";
2
2
  import TestContext from '../shared/test-context.js';
3
3
  import ModuleContext from '../shared/module-context.js';
4
4
 
package/vendor/qunit.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * QUnit 2.19.4
2
+ * QUnit 2.25.0
3
3
  * https://qunitjs.com/
4
4
  *
5
5
  * Copyright OpenJS Foundation and other contributors
@@ -45,10 +45,10 @@
45
45
  @media (min-height: 500px) {
46
46
  #qunit {
47
47
  position: fixed;
48
- left: 0px;
49
- right: 0px;
50
- top: 0px;
51
- bottom: 0px;
48
+ left: 0;
49
+ right: 0;
50
+ top: 0;
51
+ bottom: 0;
52
52
  padding: 8px;
53
53
  display: -webkit-box;
54
54
  display: flex;
@@ -329,11 +329,11 @@
329
329
  margin: 0;
330
330
  }
331
331
 
332
- #qunit-tests li strong {
332
+ #qunit-tests li .qunit-test-name {
333
333
  cursor: pointer;
334
334
  }
335
335
 
336
- #qunit-tests li.skipped strong {
336
+ #qunit-tests li.skipped .qunit-test-name {
337
337
  cursor: default;
338
338
  }
339
339