vitest-environment-webgl-node 3.0.0 → 3.0.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/README.md +112 -27
- package/dist/index.d.ts +21 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +89 -5
- package/dist/index.js.map +1 -1
- package/package.json +14 -4
- package/src/index.ts +88 -4
package/README.md
CHANGED
|
@@ -1,37 +1,90 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://npmjs.com/package/vitest-environment-webgl-node"><img src="https://img.shields.io/npm/v/vitest-environment-webgl-node.svg" alt="npm package"></a>
|
|
3
|
+
<a href="https://www.npmtrends.com/vitest-environment-webgl-node"><img src="https://img.shields.io/npm/dw/vitest-environment-webgl-node.svg" alt="npm downloads"></a>
|
|
4
|
+
<a href="https://codecov.io/gh/bhouston/vitest-gpu"><img src="https://codecov.io/gh/bhouston/vitest-gpu/branch/main/graph/badge.svg" alt="coverage"></a>
|
|
5
|
+
<a href="https://discord.gg/fwupDN493R"><img src="https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord" alt="discord chat"></a>
|
|
6
|
+
<a href="https://vitest-gpu.ben3d.ca/"><img src="https://img.shields.io/badge/docs-read-blue?style=flat" alt="documentation"></a>
|
|
7
|
+
</p>
|
|
8
|
+
<br/>
|
|
9
|
+
|
|
1
10
|
# vitest-environment-webgl-node
|
|
2
11
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
> Real WebGL 1 and 2 tests in Vitest, with no browser.
|
|
13
|
+
|
|
14
|
+
- 🖥️ Real WebGL 1 and 2
|
|
15
|
+
- 🚫 No Browser or Mocks
|
|
16
|
+
- 🧩 Runs three.js Unchanged
|
|
17
|
+
- 🖼️ Pairs With vitest-screenshot
|
|
18
|
+
- ⚡ Up to 2.4x Faster Than Browsers
|
|
19
|
+
|
|
20
|
+
Headless GPU testing for CI: `canvas.getContext('webgl2')` returns a GPU-backed context, so three.js, regl,
|
|
21
|
+
PixiJS and plain WebGL code run unchanged. Native GPU testing is up to 2.4x faster than running the same tests
|
|
22
|
+
in a browser, see the [blog post](https://ben3d.ca/blog/native-gpu-testing-for-vitest-and-jest).
|
|
23
|
+
Powered by [`@onirenaud/node-webgl`](https://github.com/RenaudRohlinger/node-webgl) (Chrome's ANGLE, statically linked).
|
|
24
|
+
|
|
25
|
+
Part of [vitest-gpu](https://github.com/bhouston/vitest-gpu), which also provides WebGPU and image snapshot packages.
|
|
26
|
+
|
|
27
|
+
[Read the Docs to Learn More](https://vitest-gpu.ben3d.ca/docs/webgl/) about the WebGL environment: what it installs, its options, TypeScript types for the globals and CI setup.
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pnpm add -D vitest vitest-environment-webgl-node
|
|
31
|
+
```
|
|
7
32
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
33
|
+
```ts
|
|
34
|
+
// vitest.config.ts
|
|
35
|
+
import { defineConfig } from 'vitest/config';
|
|
36
|
+
|
|
37
|
+
export default defineConfig({ test: { environment: 'webgl-node' } });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// gl.test.ts
|
|
42
|
+
import { expect, it } from 'vitest';
|
|
43
|
+
|
|
44
|
+
it('clears to red on a real GPU', () => {
|
|
45
|
+
const canvas = document.createElement('canvas');
|
|
46
|
+
canvas.width = canvas.height = 4;
|
|
47
|
+
const gl = canvas.getContext('webgl2')!;
|
|
48
|
+
gl.clearColor(1, 0, 0, 1);
|
|
49
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
50
|
+
const pixel = new Uint8Array(4);
|
|
51
|
+
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
|
52
|
+
expect([...pixel]).toEqual([255, 0, 0, 255]);
|
|
53
|
+
});
|
|
54
|
+
```
|
|
13
55
|
|
|
14
56
|
The environment installs `window`, `document`, `HTMLCanvasElement`, `Image`, `requestAnimationFrame`,
|
|
15
57
|
the `WebGL*` classes and a file-reading `fetch()` before each test file, and removes them afterwards.
|
|
16
|
-
`document.createElement('canvas').getContext('webgl2')` returns a GPU-backed context, so three.js,
|
|
17
|
-
regl, pixi and plain WebGL code run unchanged.
|
|
18
58
|
|
|
19
59
|
Using Jest instead? See the equivalent
|
|
20
60
|
[`jest-environment-webgl-node`](https://github.com/bhouston/jest-gpu/tree/main/packages/jest-environment-webgl-node)
|
|
21
|
-
in [jest-gpu](https://github.com/bhouston/jest-gpu).
|
|
61
|
+
in [jest-gpu](https://github.com/bhouston/jest-gpu), documented at [jest-gpu.ben3d.ca](https://jest-gpu.ben3d.ca/).
|
|
22
62
|
|
|
23
63
|
## Stability
|
|
24
64
|
|
|
25
65
|
As of 1.0.0, the public API (the environment export and its options) is considered stable.
|
|
26
66
|
Breaking changes will be released as a new major version.
|
|
27
67
|
|
|
28
|
-
##
|
|
68
|
+
## Compared to alternatives
|
|
29
69
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
70
|
+
| Approach | Real rendering | Notes |
|
|
71
|
+
| --------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| jsdom plus a canvas mock (`jest-webgl-canvas-mock`, etc.) | No | Fast and light, but `getContext()` returns stubs: you can assert calls, not pixels or shader results. |
|
|
73
|
+
| Raw [`gl`](https://github.com/stackgl/headless-gl) | Yes | WebGL 1 only (per node-webgl's docs), runs on the system OpenGL driver, and you wire up the DOM globals yourself. |
|
|
74
|
+
| Vitest browser mode or Playwright | Yes | A real browser: heavier to install and slower to start, and the right choice when you need real browser APIs. |
|
|
75
|
+
| vitest-environment-webgl-node | Yes | ANGLE in the Node process, WebGL 1 and 2. Not a browser: only canvas, `Image`, `fetch` and `requestAnimationFrame` are provided. |
|
|
76
|
+
|
|
77
|
+
## Supported versions and platforms
|
|
78
|
+
|
|
79
|
+
- Vitest 4 and 5 (CI tests both). Vitest 3 is not supported.
|
|
80
|
+
- Node 22 or later. CI runs Node 22 and 26 on `macos-latest`, `ubuntu-latest` and `windows-latest`.
|
|
81
|
+
- macOS uses Metal. Linux uses Mesa (see below). Windows uses Direct3D 11 per the
|
|
82
|
+
[node-webgl docs](https://github.com/RenaudRohlinger/node-webgl#platforms).
|
|
33
83
|
|
|
34
|
-
|
|
84
|
+
## Useful for
|
|
85
|
+
|
|
86
|
+
three.js `WebGLRenderer`, Babylon.js, regl, PixiJS, deck.gl and plain WebGL code. The demos exercise three.js and
|
|
87
|
+
Babylon.js; node-webgl's docs name regl, PixiJS and deck.gl. These are compatible use cases, not endorsements.
|
|
35
88
|
|
|
36
89
|
## Usage
|
|
37
90
|
|
|
@@ -62,7 +115,6 @@ it('renders', () => {
|
|
|
62
115
|
|
|
63
116
|
Options are those of node-webgl's `init()` (`backend`, `api`) and `installDOM()`
|
|
64
117
|
(`baseDir`, `fetch`, `devicePixelRatio`, `innerWidth`, `innerHeight`, `frameInterval`).
|
|
65
|
-
Pair with [`vitest-screenshot`](../vitest-screenshot) for pixel-diff assertions.
|
|
66
118
|
|
|
67
119
|
For a Node-only TypeScript project, opt into types for the installed globals by adding
|
|
68
120
|
`import 'vitest-environment-webgl-node/globals';` to a `.d.ts` file your tsconfig includes. Do not include
|
|
@@ -73,18 +125,51 @@ node-webgl canvas at the library boundary when necessary.
|
|
|
73
125
|
On Linux CI install Mesa (`apt-get install libegl1 libgles2 libgl1-mesa-dri`) and set
|
|
74
126
|
`LIBGL_ALWAYS_SOFTWARE=1`. macOS and Windows use prebuilt binaries.
|
|
75
127
|
|
|
76
|
-
##
|
|
128
|
+
## Image snapshots
|
|
129
|
+
|
|
130
|
+
Pair with [`vitest-screenshot`](https://github.com/bhouston/vitest-gpu/tree/main/packages/vitest-screenshot) to diff the canvas against a committed baseline:
|
|
77
131
|
|
|
78
|
-
|
|
132
|
+
```ts
|
|
133
|
+
import * as THREE from 'three';
|
|
134
|
+
import { expect, it } from 'vitest';
|
|
135
|
+
import { extendMatchers } from 'vitest-screenshot';
|
|
136
|
+
|
|
137
|
+
extendMatchers();
|
|
138
|
+
|
|
139
|
+
it('renders a cube', async () => {
|
|
140
|
+
const canvas = document.createElement('canvas');
|
|
141
|
+
const renderer = new THREE.WebGLRenderer({ canvas });
|
|
142
|
+
renderer.setSize(256, 256, false);
|
|
143
|
+
const scene = new THREE.Scene();
|
|
144
|
+
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 100);
|
|
145
|
+
camera.position.z = 3;
|
|
146
|
+
scene.add(new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshNormalMaterial()));
|
|
147
|
+
|
|
148
|
+
renderer.render(scene, camera);
|
|
149
|
+
await expect(canvas).toMatchScreenshot('cube.png');
|
|
150
|
+
renderer.dispose();
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## See also
|
|
155
|
+
|
|
156
|
+
- [`vitest-environment-webgpu-node`](https://github.com/bhouston/vitest-gpu/tree/main/packages/vitest-environment-webgpu-node) for headless WebGPU.
|
|
157
|
+
- [`vitest-screenshot`](https://github.com/bhouston/vitest-gpu/tree/main/packages/vitest-screenshot) for `toMatchScreenshot()`.
|
|
158
|
+
- [`demo/`](https://github.com/bhouston/vitest-gpu/tree/main/demo/test/webgl) for WebGL contexts, uploads, readbacks, three.js and Babylon.js tests, and the
|
|
159
|
+
[repository README](https://github.com/bhouston/vitest-gpu#readme).
|
|
160
|
+
|
|
161
|
+
## Resource cleanup
|
|
162
|
+
|
|
163
|
+
Environment teardown destroys contexts first created through `Canvas.getContext()` during the test file, including DOM canvases, `OffscreenCanvas` and imported canvas factories. It also cancels pending callbacks scheduled through its animation-frame shim. Contexts created directly with the WebGL context constructors, or before setup, remain the caller's responsibility: call `destroy()` on them. Dispose renderers and canvases during a long-running file to release resources sooner.
|
|
164
|
+
|
|
165
|
+
## Contribution
|
|
166
|
+
|
|
167
|
+
See [Contributing Guide](https://github.com/bhouston/vitest-gpu/blob/main/CONTRIBUTING.md).
|
|
79
168
|
|
|
80
169
|
## License
|
|
81
170
|
|
|
82
171
|
MIT
|
|
83
172
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
[
|
|
87
|
-
[tests-badge]: https://github.com/bhouston/vitest-gpu/actions/workflows/ci.yml/badge.svg
|
|
88
|
-
[tests-url]: https://github.com/bhouston/vitest-gpu/actions/workflows/ci.yml
|
|
89
|
-
[discord-badge]: https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white
|
|
90
|
-
[discord-url]: https://discord.gg/fwupDN493R
|
|
173
|
+
## Author
|
|
174
|
+
|
|
175
|
+
[Ben Houston](https://ben3d.ca), Sponsored by [Land of Assets](https://landofassets.com).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { type InitOptions, type InstallDOMOptions } from '@onirenaud/node-webgl';
|
|
2
2
|
/** Set under `test.environmentOptions.webglNode` in your Vitest config. */
|
|
3
3
|
export type WebglNodeOptions = InitOptions & InstallDOMOptions;
|
|
4
|
+
/**
|
|
5
|
+
* The Vitest environment registered as `webgl-node`. Before each test file it installs node-webgl's `window`,
|
|
6
|
+
* `document`, `HTMLCanvasElement`, `Image`, `requestAnimationFrame` and `WebGL*` classes plus a file-reading
|
|
7
|
+
* `fetch()`, and it restores the original globals afterwards.
|
|
8
|
+
*/
|
|
4
9
|
declare const _default: {
|
|
10
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgl-node'`. */
|
|
5
11
|
name: string;
|
|
12
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
6
13
|
viteEnvironment: "ssr";
|
|
14
|
+
/**
|
|
15
|
+
* Installs the GL display and the DOM globals on the test file's global object.
|
|
16
|
+
*
|
|
17
|
+
* @param global - The global object of the test file's context.
|
|
18
|
+
* @param __namedParameters - The environment options from the Vitest config; `webglNode` holds {@link WebglNodeOptions}.
|
|
19
|
+
* @returns A `teardown` hook that destroys contexts and restores the globals this call changed.
|
|
20
|
+
*/
|
|
7
21
|
setup(global: typeof globalThis, { webglNode }: {
|
|
8
22
|
webglNode?: WebglNodeOptions;
|
|
9
23
|
}): {
|
|
10
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Destroys the WebGL contexts created during the test file, cancels pending animation-frame callbacks and
|
|
26
|
+
* restores the globals that `setup` replaced.
|
|
27
|
+
*
|
|
28
|
+
* @param g - The global object of the test file's context.
|
|
29
|
+
*/
|
|
30
|
+
teardown: (g: Record<string, unknown>) => void;
|
|
11
31
|
};
|
|
12
32
|
};
|
|
13
33
|
export default _default;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAM/B,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,iBAAiB,CAAC;AA+E/D;;;;GAIG;;IAED,yFAAyF;;IAEzF,sEAAsE;;IAEtE;;;;;;OAMG;kBACW,OAAO,UAAU,iBAAsB;QAAE,SAAS,CAAC,EAAE,gBAAgB,CAAA;KAAE;QA0FjF;;;;;WAKG;sBACW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;AA5G3C,wBAyHwB"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDisplayInfo, init, installDOM } from '@onirenaud/node-webgl';
|
|
1
|
+
import { Canvas, getDisplayInfo, init, installDOM, } from '@onirenaud/node-webgl';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { extname, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
@@ -73,9 +73,23 @@ const installFetch = (global, baseDir) => {
|
|
|
73
73
|
};
|
|
74
74
|
Object.defineProperty(global, 'fetch', { value: localFetch, writable: true, configurable: true });
|
|
75
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* The Vitest environment registered as `webgl-node`. Before each test file it installs node-webgl's `window`,
|
|
78
|
+
* `document`, `HTMLCanvasElement`, `Image`, `requestAnimationFrame` and `WebGL*` classes plus a file-reading
|
|
79
|
+
* `fetch()`, and it restores the original globals afterwards.
|
|
80
|
+
*/
|
|
76
81
|
export default {
|
|
82
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgl-node'`. */
|
|
77
83
|
name: 'webgl-node',
|
|
84
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
78
85
|
viteEnvironment: 'ssr',
|
|
86
|
+
/**
|
|
87
|
+
* Installs the GL display and the DOM globals on the test file's global object.
|
|
88
|
+
*
|
|
89
|
+
* @param global - The global object of the test file's context.
|
|
90
|
+
* @param __namedParameters - The environment options from the Vitest config; `webglNode` holds {@link WebglNodeOptions}.
|
|
91
|
+
* @returns A `teardown` hook that destroys contexts and restores the globals this call changed.
|
|
92
|
+
*/
|
|
79
93
|
setup(global, { webglNode = {} }) {
|
|
80
94
|
const { backend, api, ...dom } = webglNode;
|
|
81
95
|
if (backend || api) {
|
|
@@ -90,11 +104,65 @@ export default {
|
|
|
90
104
|
}
|
|
91
105
|
init({ backend, api });
|
|
92
106
|
}
|
|
107
|
+
const contexts = new Set();
|
|
108
|
+
const frames = new Set();
|
|
109
|
+
let disposed = false;
|
|
110
|
+
const dispose = () => {
|
|
111
|
+
disposed = true;
|
|
112
|
+
for (const frame of frames)
|
|
113
|
+
clearTimeout(frame);
|
|
114
|
+
frames.clear();
|
|
115
|
+
for (const context of contexts)
|
|
116
|
+
context.destroy();
|
|
117
|
+
contexts.clear();
|
|
118
|
+
};
|
|
119
|
+
const hasFrame = global.requestAnimationFrame !== undefined;
|
|
93
120
|
const before = snapshot(global);
|
|
94
121
|
const nested = [];
|
|
95
122
|
try {
|
|
96
123
|
// node-webgl remembers fetch installation at module scope, which cannot represent separate Vitest environments.
|
|
97
124
|
installDOM({ ...dom, fetch: false });
|
|
125
|
+
// Intercept the shared canvas API so imported factories and OffscreenCanvas are covered too.
|
|
126
|
+
const canvasBefore = snapshot(Canvas.prototype);
|
|
127
|
+
nested.push({ target: Canvas.prototype, before: canvasBefore });
|
|
128
|
+
// oxlint-disable-next-line typescript/unbound-method -- Canvas.prototype.getContext is patched/compared by identity, never called unbound
|
|
129
|
+
const getContext = Canvas.prototype.getContext;
|
|
130
|
+
Canvas.prototype.getContext = function (...args) {
|
|
131
|
+
if (disposed)
|
|
132
|
+
throw new Error('WebGL environment has been torn down');
|
|
133
|
+
// node-webgl exposes the cached context at runtime but strips it from its declarations.
|
|
134
|
+
const previous = this._ctx;
|
|
135
|
+
const context = getContext.apply(this, args);
|
|
136
|
+
if (context && context !== previous)
|
|
137
|
+
contexts.add(context);
|
|
138
|
+
return context;
|
|
139
|
+
};
|
|
140
|
+
nested.at(-1).changed = changedProperties(canvasBefore, Canvas.prototype);
|
|
141
|
+
// Own only the rAF shim installed by this setup; preserve host implementations.
|
|
142
|
+
if (!hasFrame) {
|
|
143
|
+
Object.defineProperty(global, 'requestAnimationFrame', {
|
|
144
|
+
configurable: true,
|
|
145
|
+
writable: true,
|
|
146
|
+
value: (callback) => {
|
|
147
|
+
if (disposed)
|
|
148
|
+
return 0;
|
|
149
|
+
const frame = setTimeout(() => {
|
|
150
|
+
frames.delete(frame);
|
|
151
|
+
callback(performance.now());
|
|
152
|
+
}, dom.frameInterval ?? 16).unref();
|
|
153
|
+
frames.add(frame);
|
|
154
|
+
return frame;
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
Object.defineProperty(global, 'cancelAnimationFrame', {
|
|
158
|
+
configurable: true,
|
|
159
|
+
writable: true,
|
|
160
|
+
value: (frame) => {
|
|
161
|
+
frames.delete(frame);
|
|
162
|
+
clearTimeout(frame);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
98
166
|
if (dom.fetch !== false)
|
|
99
167
|
installFetch(global, dom.baseDir ?? process.cwd());
|
|
100
168
|
// node-webgl's window/document have no event methods; engines like Babylon.js register resize/blur listeners.
|
|
@@ -110,6 +178,7 @@ export default {
|
|
|
110
178
|
}
|
|
111
179
|
}
|
|
112
180
|
catch (error) {
|
|
181
|
+
dispose();
|
|
113
182
|
for (const item of nested.toReversed())
|
|
114
183
|
restore(item.target, item.before, item.changed ?? changedProperties(item.before, item.target));
|
|
115
184
|
restore(global, before, changedProperties(before, global));
|
|
@@ -117,10 +186,25 @@ export default {
|
|
|
117
186
|
}
|
|
118
187
|
const changed = changedProperties(before, global);
|
|
119
188
|
return {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Destroys the WebGL contexts created during the test file, cancels pending animation-frame callbacks and
|
|
191
|
+
* restores the globals that `setup` replaced.
|
|
192
|
+
*
|
|
193
|
+
* @param g - The global object of the test file's context.
|
|
194
|
+
*/
|
|
195
|
+
teardown: (g) => {
|
|
196
|
+
if (disposed)
|
|
197
|
+
return;
|
|
198
|
+
try {
|
|
199
|
+
dispose();
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
for (const item of nested.toReversed())
|
|
203
|
+
restore(item.target, item.before, item.changed);
|
|
204
|
+
restore(g, before, changed);
|
|
205
|
+
nested.length = 0;
|
|
206
|
+
before.clear();
|
|
207
|
+
}
|
|
124
208
|
},
|
|
125
209
|
};
|
|
126
210
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-environment-webgl-node",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "Vitest environment
|
|
3
|
+
"version": "3.0.2",
|
|
4
|
+
"description": "Vitest environment for real headless WebGL 1/2 tests in Node (ANGLE via @onirenaud/node-webgl): no browser, Playwright or canvas mocks",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"babylonjs",
|
|
7
|
+
"canvas",
|
|
8
|
+
"ci",
|
|
9
|
+
"gpu",
|
|
10
|
+
"gpu-testing",
|
|
6
11
|
"headless",
|
|
12
|
+
"headless-gl",
|
|
13
|
+
"node",
|
|
14
|
+
"pixijs",
|
|
15
|
+
"regl",
|
|
7
16
|
"testing",
|
|
8
17
|
"three.js",
|
|
9
18
|
"vitest",
|
|
10
19
|
"vitest-environment",
|
|
11
20
|
"webgl",
|
|
21
|
+
"webgl-testing",
|
|
12
22
|
"webgl2"
|
|
13
23
|
],
|
|
14
24
|
"homepage": "https://github.com/bhouston/vitest-gpu#readme",
|
|
@@ -56,7 +66,7 @@
|
|
|
56
66
|
"node": ">=22"
|
|
57
67
|
},
|
|
58
68
|
"scripts": {
|
|
59
|
-
"build": "tsc",
|
|
60
|
-
"tsc": "tsc"
|
|
69
|
+
"build": "tsc -p tsconfig.build.json",
|
|
70
|
+
"tsc": "tsc --noEmit"
|
|
61
71
|
}
|
|
62
72
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Canvas,
|
|
3
|
+
getDisplayInfo,
|
|
4
|
+
init,
|
|
5
|
+
installDOM,
|
|
6
|
+
type InitOptions,
|
|
7
|
+
type InstallDOMOptions,
|
|
8
|
+
} from '@onirenaud/node-webgl';
|
|
2
9
|
import { readFile } from 'node:fs/promises';
|
|
3
10
|
import { extname, resolve } from 'node:path';
|
|
4
11
|
import { fileURLToPath } from 'node:url';
|
|
@@ -84,9 +91,23 @@ const installFetch = (global: typeof globalThis, baseDir: string): void => {
|
|
|
84
91
|
Object.defineProperty(global, 'fetch', { value: localFetch, writable: true, configurable: true });
|
|
85
92
|
};
|
|
86
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The Vitest environment registered as `webgl-node`. Before each test file it installs node-webgl's `window`,
|
|
96
|
+
* `document`, `HTMLCanvasElement`, `Image`, `requestAnimationFrame` and `WebGL*` classes plus a file-reading
|
|
97
|
+
* `fetch()`, and it restores the original globals afterwards.
|
|
98
|
+
*/
|
|
87
99
|
export default {
|
|
100
|
+
/** The name Vitest uses to select this environment: `test.environment: 'webgl-node'`. */
|
|
88
101
|
name: 'webgl-node',
|
|
102
|
+
/** Runs test files through Vite's `ssr` environment, as Node code. */
|
|
89
103
|
viteEnvironment: 'ssr',
|
|
104
|
+
/**
|
|
105
|
+
* Installs the GL display and the DOM globals on the test file's global object.
|
|
106
|
+
*
|
|
107
|
+
* @param global - The global object of the test file's context.
|
|
108
|
+
* @param __namedParameters - The environment options from the Vitest config; `webglNode` holds {@link WebglNodeOptions}.
|
|
109
|
+
* @returns A `teardown` hook that destroys contexts and restores the globals this call changed.
|
|
110
|
+
*/
|
|
90
111
|
setup(global: typeof globalThis, { webglNode = {} }: { webglNode?: WebglNodeOptions }) {
|
|
91
112
|
const { backend, api, ...dom } = webglNode;
|
|
92
113
|
if (backend || api) {
|
|
@@ -103,11 +124,60 @@ export default {
|
|
|
103
124
|
}
|
|
104
125
|
init({ backend, api });
|
|
105
126
|
}
|
|
127
|
+
const contexts = new Set<NonNullable<ReturnType<Canvas['getContext']>>>();
|
|
128
|
+
const frames = new Set<ReturnType<typeof setTimeout>>();
|
|
129
|
+
let disposed = false;
|
|
130
|
+
const dispose = () => {
|
|
131
|
+
disposed = true;
|
|
132
|
+
for (const frame of frames) clearTimeout(frame);
|
|
133
|
+
frames.clear();
|
|
134
|
+
for (const context of contexts) context.destroy();
|
|
135
|
+
contexts.clear();
|
|
136
|
+
};
|
|
137
|
+
const hasFrame = (global as unknown as Record<string, unknown>).requestAnimationFrame !== undefined;
|
|
106
138
|
const before = snapshot(global);
|
|
107
139
|
const nested: { target: object; before: PropertySnapshot; changed?: Set<PropertyKey> }[] = [];
|
|
108
140
|
try {
|
|
109
141
|
// node-webgl remembers fetch installation at module scope, which cannot represent separate Vitest environments.
|
|
110
142
|
installDOM({ ...dom, fetch: false });
|
|
143
|
+
// Intercept the shared canvas API so imported factories and OffscreenCanvas are covered too.
|
|
144
|
+
const canvasBefore = snapshot(Canvas.prototype);
|
|
145
|
+
nested.push({ target: Canvas.prototype, before: canvasBefore });
|
|
146
|
+
// oxlint-disable-next-line typescript/unbound-method -- Canvas.prototype.getContext is patched/compared by identity, never called unbound
|
|
147
|
+
const getContext = Canvas.prototype.getContext;
|
|
148
|
+
Canvas.prototype.getContext = function (this: Canvas, ...args: Parameters<Canvas['getContext']>) {
|
|
149
|
+
if (disposed) throw new Error('WebGL environment has been torn down');
|
|
150
|
+
// node-webgl exposes the cached context at runtime but strips it from its declarations.
|
|
151
|
+
const previous = (this as Canvas & { _ctx?: ReturnType<Canvas['getContext']> })._ctx;
|
|
152
|
+
const context = getContext.apply(this, args);
|
|
153
|
+
if (context && context !== previous) contexts.add(context);
|
|
154
|
+
return context;
|
|
155
|
+
} as Canvas['getContext'];
|
|
156
|
+
nested.at(-1)!.changed = changedProperties(canvasBefore, Canvas.prototype);
|
|
157
|
+
// Own only the rAF shim installed by this setup; preserve host implementations.
|
|
158
|
+
if (!hasFrame) {
|
|
159
|
+
Object.defineProperty(global, 'requestAnimationFrame', {
|
|
160
|
+
configurable: true,
|
|
161
|
+
writable: true,
|
|
162
|
+
value: (callback: (time: number) => void) => {
|
|
163
|
+
if (disposed) return 0;
|
|
164
|
+
const frame = setTimeout(() => {
|
|
165
|
+
frames.delete(frame);
|
|
166
|
+
callback(performance.now());
|
|
167
|
+
}, dom.frameInterval ?? 16).unref();
|
|
168
|
+
frames.add(frame);
|
|
169
|
+
return frame;
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
Object.defineProperty(global, 'cancelAnimationFrame', {
|
|
173
|
+
configurable: true,
|
|
174
|
+
writable: true,
|
|
175
|
+
value: (frame: ReturnType<typeof setTimeout>) => {
|
|
176
|
+
frames.delete(frame);
|
|
177
|
+
clearTimeout(frame);
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
111
181
|
if (dom.fetch !== false) installFetch(global, dom.baseDir ?? process.cwd());
|
|
112
182
|
// node-webgl's window/document have no event methods; engines like Babylon.js register resize/blur listeners.
|
|
113
183
|
const { window, document } = global as unknown as Record<string, Record<string, unknown>>;
|
|
@@ -120,6 +190,7 @@ export default {
|
|
|
120
190
|
nested.at(-1)!.changed = changedProperties(targetBefore, target);
|
|
121
191
|
}
|
|
122
192
|
} catch (error) {
|
|
193
|
+
dispose();
|
|
123
194
|
for (const item of nested.toReversed())
|
|
124
195
|
restore(item.target, item.before, item.changed ?? changedProperties(item.before, item.target));
|
|
125
196
|
restore(global, before, changedProperties(before, global));
|
|
@@ -127,9 +198,22 @@ export default {
|
|
|
127
198
|
}
|
|
128
199
|
const changed = changedProperties(before, global);
|
|
129
200
|
return {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Destroys the WebGL contexts created during the test file, cancels pending animation-frame callbacks and
|
|
203
|
+
* restores the globals that `setup` replaced.
|
|
204
|
+
*
|
|
205
|
+
* @param g - The global object of the test file's context.
|
|
206
|
+
*/
|
|
207
|
+
teardown: (g: Record<string, unknown>) => {
|
|
208
|
+
if (disposed) return;
|
|
209
|
+
try {
|
|
210
|
+
dispose();
|
|
211
|
+
} finally {
|
|
212
|
+
for (const item of nested.toReversed()) restore(item.target, item.before, item.changed!);
|
|
213
|
+
restore(g, before, changed);
|
|
214
|
+
nested.length = 0;
|
|
215
|
+
before.clear();
|
|
216
|
+
}
|
|
133
217
|
},
|
|
134
218
|
};
|
|
135
219
|
},
|