qunitx 0.12.0 → 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 CHANGED
@@ -13,9 +13,11 @@ build:
13
13
 
14
14
  # Lint, bump version, update changelog, commit, tag, push, publish to npm.
15
15
  # CI then creates the GitHub release.
16
- # Usage: make release LEVEL=patch|minor|major
16
+ # Usage: make release (defaults to patch)
17
+ # make release LEVEL=minor|major
18
+ LEVEL ?= patch
17
19
  release:
18
- @test -n "$(LEVEL)" || (echo "Usage: make release LEVEL=patch|minor|major" && exit 1)
20
+ @npm whoami 2>/dev/null || npm login
19
21
  npm run lint
20
22
  npm version $(LEVEL) --no-git-tag-version
21
23
  npm run changelog:update
package/README.md CHANGED
@@ -1,156 +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)
3
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)
4
7
 
5
8
  # QUnitX
6
9
 
7
- Mature, fast, 0 dependency & flexible test API for JavaScript/TypeScript.
10
+ **The oldest, most battle-tested JavaScript test API now universal.**
8
11
 
9
- ***Universal testing for JavaScript with the oldest, most mature & flexible
10
- testing API in the JavaScript ecosystem. Run the same test file in node.js, deno or in the browser***
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.
11
14
 
12
- Your test JS/TS file(s) can now run interchangably in different runtimes with
13
- the default test runner of node.js or deno, or with a browser runner of your
14
- choice!
15
+ ---
15
16
 
16
- [![asciicast](https://asciinema.org/a/597066.svg)](https://asciinema.org/a/597066?autoplay=1)
17
+ ## Why QUnit?
17
18
 
18
- In the browser you can use the same browser test/filter UI of
19
- [QUnit](https://github.com/qunitjs/qunit) and share the web links with your
20
- colleagues thanks to the test filters through query params feature of QUnit:
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:
21
23
 
22
- [QUnit Test Suite Example](https://objectmodel.js.org/test/?moduleId=6e15ed5f&moduleId=950ec9c5)
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
23
35
 
24
- **UI visual automated tests also possible with QUnit!**
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.
25
38
 
26
- ![QunitX terminal output](https://raw.githubusercontent.com/izelnakri/qunitx/main/docs/qunitx-help-stdout.png)
39
+ ---
27
40
 
28
- ### Installation: Node & Deno
41
+ ## Demo
29
42
 
30
- This is a 0-dependency test library that runs code in your target runtime(node,
31
- deno or browser) test runner. Since a default test runner is a new feature of node.js, please use node.js v20.3+.
43
+ > Left window: `node --test` and `deno test` running the same file.
44
+ > Right window: QUnit browser UI with filterable, shareable test results.
32
45
 
33
- In order to use qunitx to convert qunit tests files please change:
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
- ```js
36
- import { module, test } from 'qunit';
50
+ Live browser UI example (click to see filterable QUnit test suite):
37
51
 
38
- // to:
39
- import { module, test } from 'qunitx';
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
40
62
  ```
41
63
 
42
- Example:
64
+ Requires **Node.js >= 22** (LTS) or **Deno >= 2**.
65
+
66
+ ---
67
+
68
+ ## Quick start
43
69
 
44
70
  ```js
45
- // in some-test.js: (typescript also works)
71
+ // math-test.js (works in Node, Deno, and browser unchanged)
46
72
  import { module, test } from 'qunitx';
47
- import $ from 'jquery';
48
73
 
49
- module('Basic sanity check', function (hooks) {
50
- test('it works', function (assert) {
51
- assert.equal(true, true);
74
+ module('Math utilities', (hooks) => {
75
+ hooks.before((assert) => {
76
+ assert.step('setup complete');
52
77
  });
53
78
 
54
- module('More advanced cases', function (hooks) {
55
- test('deepEqual works', function (assert) {
56
- assert.deepEqual({ username: 'izelnakri' }, { username: 'izelnakri' });
57
- });
58
- test('can import ES & npm modules', function (assert) {
59
- 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);
60
92
  });
61
93
  });
62
94
  });
63
95
  ```
64
96
 
65
- ```zsh
66
- # you can run the test in node with ES modules package.json{ "type": "module" }
67
- $ 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
102
+
103
+ # Watch mode (re-runs on save)
104
+ node --test --watch math-test.js
105
+
106
+ # Glob pattern
107
+ node --test --watch 'test/**/*.js'
108
+
109
+ # TypeScript (tsconfig.json with moduleResolution: NodeNext required)
110
+ node --import=tsx/esm --test math-test.ts
111
+
112
+ # Code coverage
113
+ npx c8 node --test math-test.js
114
+ ```
115
+
116
+ ### Deno
68
117
 
69
- # TypeScript also works, make sure on node.js mode, tsconfig.json exists with compilerOptions.module & compilerOptions.moduleResolution set to "NodeNext":
70
- $ node --loader=ts-node/esm/transpile-only --test some-test.ts
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
71
121
 
72
- # You can use the new watch mode of node.js to watch for files or folder patterns
73
- $ node --test --watch some-test.js some-folder/*.js
122
+ # Run
123
+ deno test math-test.js
74
124
 
75
- # You can also run this test on deno. Unfortunately today deno requires one extra step to create a deno.json file:
76
- $ echo '{"imports": { "qunitx": "https://esm.sh/qunitx/shims/deno/index.js" } }' > deno.json
125
+ # With explicit permissions
126
+ deno test --allow-read --allow-env math-test.js
127
+ ```
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:
77
133
 
78
- # then run the tests in default deno test runner:
79
- $ deno test some-test.js
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
80
142
  ```
81
143
 
82
- ### Installation: Browser
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
83
149
 
84
- QUnitX mainly proxies to [QUnit
85
- API](https://api.qunitjs.com/QUnit/module/#hooks-on-nested-modules) in browser.
86
- You can use [QUnitX CLI](https://github.com/izelnakri/qunitx-cli) to get your
87
- browser tests to stdout/CI or use the watch mode during the development.
150
+ ---
88
151
 
89
- ```zsh
90
- # Install QUnitX browser runner/cli:
91
- $ npm install -g qunitx-cli
92
- $ qunitx
93
- $ qunitx some-test.js
152
+ ## Migrating from QUnit
94
153
 
95
- # with browser output enabled:
96
- $ qunitx some-test.js --debug
154
+ One import line is all that changes:
97
155
 
156
+ ```js
157
+ // Before:
158
+ import { module, test } from 'qunit';
159
+
160
+ // After:
161
+ import { module, test } from 'qunitx';
98
162
  ```
99
163
 
100
- ### Concurrency options
164
+ ---
101
165
 
102
- QUnitX API accepts an optional options object as 2nd argument to:
103
- - `QUnit.module(testName, optionsOrHandler?, handler?)`
104
- - `QUnit.test(testName, optionsOrHandler?, handler?)`
166
+ ## Concurrency options
105
167
 
106
- So you can run tests in parallel(default) or in series. You can even run them
107
- through the [node.js test runner run()
108
- api](https://nodejs.org/api/test.html#runoptions):
168
+ `module()` and `test()` accept an optional options object forwarded directly to the underlying
169
+ Node / Deno test runner:
109
170
 
110
171
  ```js
111
- // in some-test.js: (typescript also works)
112
172
  import { module, test } from 'qunitx';
113
- import $ from 'jquery';
114
173
 
115
- module('Basic sanity check', function (hooks) {
116
- test('it works', { concurrency: false }, function (assert) {
117
- assert.equal(true, true);
118
- });
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
+ });
119
179
 
120
- module('More advanced cases', { concurrency: false, permissions: { read: true }, sanitizeExit: false }, function (hooks) {
121
- test('deepEqual works', function (assert) {
122
- assert.deepEqual({ username: 'izelnakri' }, { username: 'izelnakri' });
123
- });
124
- test('can import ES & npm modules', function (assert) {
125
- assert.ok(Object.keys($));
126
- });
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);
127
185
  });
128
186
  });
129
187
  ```
130
188
 
131
- ### 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.
132
202
 
133
- Since QUnitX proxies to default node.js test runner in when executed with node,
134
- you can use any code coverage tool you like. When running the tests in
135
- `qunit`(the browser mode) code coverage support is limited.
203
+ ---
136
204
 
137
- ```zsh
138
- $ 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
139
213
  ```
140
214
 
141
- You can browse [c8 documentation](https://github.com/bcoe/c8) for all
142
- configuration options.
143
-
144
- Implementing code coverage for the browser mode is currently not possible
145
- because we use esbuild --bundle feature to create a JS bundles for testing in
146
- the browser, this could be instrumented with `puppeteer-to-istanbul` however
147
- instrumentation includes transpiled npm imports of `qunitx` and other potential
148
- npm imports developer includes in the code, this cannot be filtered since
149
- potential filtering can only occur after the `esbuild` bundling. When chrome
150
- browser and puppeteer fully supports ES asset maps we can remove esbuild from
151
- the browser mode, run everything in deno and make instrumentation for code
152
- coverage possible with the default v8 instrumentation.
153
-
154
- Esbuild plugin interface is an ongoing development, we might be able to figure
155
- out a way to generate this instrumentation with esbuild in the future, which
156
- 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.12.0",
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",
@@ -44,7 +44,7 @@
44
44
  "browser"
45
45
  ],
46
46
  "engines": {
47
- "node": ">=24.0.0"
47
+ "node": ">=22.0.0"
48
48
  },
49
49
  "imports": {
50
50
  "qunitx": {
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "repository": {
62
62
  "type": "git",
63
- "url": "https://github.com/izelnakri/qunitx.git"
63
+ "url": "git+https://github.com/izelnakri/qunitx.git"
64
64
  },
65
65
  "scripts": {
66
66
  "lint": "prettier --check \"test/**/*.js\" \"*.js\" \"package.json\"",
@@ -75,7 +75,7 @@
75
75
  "prepack": "npm run build",
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
  },
@@ -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