From 1d6aaffce61ffa2cd13130f10f3b0b48d06ae33a Mon Sep 17 00:00:00 2001 From: CsUtil <45512166+cs-util@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:24:08 +0000 Subject: [PATCH 1/8] Added test:e2e:index in package.json to run just index.spec.js with the existing Playwright config --- package.json | 3 ++- playwright-ui-tests/index.spec.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index eb9b593..80d7905 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "check:all": "npm run check:dup && npm run check:cycles && npm run check:boundaries", "validate:all": "npm run test && npm run mutation", "serve:static": "serve -l 4173 .", - "test:e2e": "playwright test --config playwright-ui-tests/playwright.config.js", + "test:e2e": "playwright test --config playwright-ui-tests/playwright.config.js", + "test:e2e:index": "playwright test --config playwright-ui-tests/playwright.config.js playwright-ui-tests/index.spec.js", "test:e2e:artifacts": "PLAYWRIGHT_CAPTURE=1 playwright test --config playwright-ui-tests/playwright.config.js", "test:e2e:headed": "playwright test --config playwright-ui-tests/playwright.config.js --headed", "test:e2e:ui": "playwright test --config playwright-ui-tests/playwright.config.js --ui" diff --git a/playwright-ui-tests/index.spec.js b/playwright-ui-tests/index.spec.js index c793b65..0c49bfc 100644 --- a/playwright-ui-tests/index.spec.js +++ b/playwright-ui-tests/index.spec.js @@ -42,6 +42,7 @@ test.describe('index.html smoke test', () => { // Prefer networkidle so the check works for any static page; fall back to load if idle never fires. try { + await page.waitForTimeout(1000); await page.waitForLoadState('networkidle', { timeout: 10000 }); } catch (error) { await page.waitForLoadState('load'); From fc554d171764f9bc7f5d10daea40a62a6612053e Mon Sep 17 00:00:00 2001 From: CsUtil <45512166+cs-util@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:24:23 +0000 Subject: [PATCH 2/8] Updated test:e2e:index in package.json to pass --project=chromium, so it now runs the smoke test only on Chromium --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80d7905..94a8174 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "validate:all": "npm run test && npm run mutation", "serve:static": "serve -l 4173 .", "test:e2e": "playwright test --config playwright-ui-tests/playwright.config.js", - "test:e2e:index": "playwright test --config playwright-ui-tests/playwright.config.js playwright-ui-tests/index.spec.js", + "test:e2e:index": "playwright test --config playwright-ui-tests/playwright.config.js --project=chromium playwright-ui-tests/index.spec.js", "test:e2e:artifacts": "PLAYWRIGHT_CAPTURE=1 playwright test --config playwright-ui-tests/playwright.config.js", "test:e2e:headed": "playwright test --config playwright-ui-tests/playwright.config.js --headed", "test:e2e:ui": "playwright test --config playwright-ui-tests/playwright.config.js --ui" From 4d91325e7bdd58ac0ce53997ec0c2b26c4d03e74 Mon Sep 17 00:00:00 2001 From: CsUtil <45512166+cs-util@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:26:05 +0000 Subject: [PATCH 3/8] Updated the main test script in package.json to chain npm run test:e2e:index, so calling npm test now covers the Chromium smoke check as well --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94a8174..db1b317 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "TemplateJs is a minimal single-page web app template designed for quick deployment. It follows an AI-assisted iterative development process:", "main": "index.js", "scripts": { - "test": "npm run format && npm run lint && npm run check:all && npm run test:unit", + "test": "npm run format && npm run lint && npm run check:all && npm run test:unit && npm run test:e2e:index", "format": "prettier --write --ignore-unknown --no-error-on-unmatched-pattern \"src\" \"pages\" \"config\" \"docs\" index.html README.md package.json", "test:unit": "jest --coverage --config config/jest.config.js", "lint": "eslint . --config config/eslint.config.js", From 4f96fa22b34b05b04e9e3ac3c634c4e096858db0 Mon Sep 17 00:00:00 2001 From: CsUtil <45512166+cs-util@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:28:52 +0000 Subject: [PATCH 4/8] auto formatting --- docs/codeCoverageIgnoreGuidelines.md | 117 ++++++++++-------- docs/threeJs un test environments.md | 170 +++++++++++++-------------- package.json | 6 +- pages/about.html | 4 +- 4 files changed, 154 insertions(+), 143 deletions(-) diff --git a/docs/codeCoverageIgnoreGuidelines.md b/docs/codeCoverageIgnoreGuidelines.md index bd05c49..dccd754 100644 --- a/docs/codeCoverageIgnoreGuidelines.md +++ b/docs/codeCoverageIgnoreGuidelines.md @@ -5,7 +5,7 @@ This document defines how to use coverage-ignore comments in this repository. It ## TL;DR - `/* istanbul ignore next */` excludes the **next AST node** from coverage. -- Use ignores **sparingly** and **only** for code that is *truly* untestable or irrelevant to product behavior. +- Use ignores **sparingly** and **only** for code that is _truly_ untestable or irrelevant to product behavior. - Every ignore **must include a reason** right next to it. - Prefer tests, refactors, or config-level excludes over in-source ignores. @@ -30,24 +30,33 @@ Use an ignore only when exercising the code in automated tests is impractical or 1. **Unreachable defensive code** Exhaustive switch fallthroughs, invariant guards, or “should never happen” paths that exist purely as safety nets. + ```ts - type Kind = "A" | "B" - function assertNever(x: never): never { throw new Error("unreachable") } + type Kind = 'A' | 'B'; + function assertNever(x: never): never { + throw new Error('unreachable'); + } switch (kind) { - case "A": handleA(); break - case "B": handleB(); break + case 'A': + handleA(); + break; + case 'B': + handleB(); + break; /* istanbul ignore next -- defensive, unreachable by construction */ - default: assertNever(kind as never) + default: + assertNever(kind as never); } + ``` 2. **Platform-/environment-specific branches** Behavior that cannot be exercised in CI or across all supported OSes without unrealistic setups. ```ts - if (process.platform === "win32") { + if (process.platform === 'win32') { /* istanbul ignore next -- requires native Windows console; not in CI image */ - enableWindowsConsoleMode() + enableWindowsConsoleMode(); } ``` @@ -61,11 +70,11 @@ Use an ignore only when exercising the code in automated tests is impractical or ## When it is **not** acceptable -* To boost coverage percentages or hide missing tests. -* On **business logic** or any behavior affecting users. -* Broadly before `if`/`switch`/function declarations that mask multiple branches or large regions. -* As a substitute for a **small refactor** that would make testing feasible (e.g., splitting out side effects, injecting dependencies). -* For convenience when a test is mildly inconvenient to write (e.g., mocking a timer or a rejected promise). +- To boost coverage percentages or hide missing tests. +- On **business logic** or any behavior affecting users. +- Broadly before `if`/`switch`/function declarations that mask multiple branches or large regions. +- As a substitute for a **small refactor** that would make testing feasible (e.g., splitting out side effects, injecting dependencies). +- For convenience when a test is mildly inconvenient to write (e.g., mocking a timer or a rejected promise). --- @@ -97,10 +106,10 @@ Use an ignore only when exercising the code in automated tests is impractical or ## Preferred alternatives to ignores -* **Write a focused test**: Use dependency injection, seam extraction, or a small adapter to isolate side effects. -* **Refactor for testability**: Split logic from I/O; return values instead of printing; pass a clock/random source. -* **Use config excludes for generated code**: Keep production logic fully measured. -* **Switch directive, not scope**: Prefer `ignore if/else` over `ignore next` when only one branch is untestable. +- **Write a focused test**: Use dependency injection, seam extraction, or a small adapter to isolate side effects. +- **Refactor for testability**: Split logic from I/O; return values instead of printing; pass a clock/random source. +- **Use config excludes for generated code**: Keep production logic fully measured. +- **Switch directive, not scope**: Prefer `ignore if/else` over `ignore next` when only one branch is untestable. --- @@ -135,14 +144,14 @@ Jest example (if using V8 coverage): // jest.config.js module.exports = { collectCoverage: true, - coverageProvider: "v8", + coverageProvider: 'v8', coveragePathIgnorePatterns: [ - "/node_modules/", - "/dist/", - "/build/", - "\\.gen\\." - ] -} + '/node_modules/', + '/dist/', + '/build/', + '\\.gen\\.', + ], +}; ``` > Align comment style with the active provider: `istanbul` for Babel/nyc instrumentation; `c8` for V8. @@ -155,28 +164,32 @@ module.exports = { ```js // scripts/check-coverage-ignores.mjs -import { readFileSync } from "node:fs"; -import { globby } from "globby"; +import { readFileSync } from 'node:fs'; +import { globby } from 'globby'; -const files = await globby(["src/**/*.{ts,tsx,js,jsx}"], { gitignore: true }); +const files = await globby(['src/**/*.{ts,tsx,js,jsx}'], { gitignore: true }); const offenders = []; const re = /(istanbul|c8)\s+ignore\s+(next|if|else|file)/; for (const f of files) { - const lines = readFileSync(f, "utf8").split("\n"); + const lines = readFileSync(f, 'utf8').split('\n'); for (let i = 0; i < lines.length; i++) { if (re.test(lines[i])) { const hasReason = - /--\s*[A-Za-z0-9]/.test(lines[i]) || (i > 0 && /--\s*[A-Za-z0-9]/.test(lines[i - 1])); - if (!hasReason) offenders.push(`${f}:${i + 1}: missing reason after ignore comment`); + /--\s*[A-Za-z0-9]/.test(lines[i]) || + (i > 0 && /--\s*[A-Za-z0-9]/.test(lines[i - 1])); + if (!hasReason) + offenders.push(`${f}:${i + 1}: missing reason after ignore comment`); } } } if (offenders.length) { - console.error("Coverage ignore comments require an inline reason (use `-- reason`)."); - console.error(offenders.join("\n")); + console.error( + 'Coverage ignore comments require an inline reason (use `-- reason`).' + ); + console.error(offenders.join('\n')); process.exit(1); } ``` @@ -200,7 +213,13 @@ Optional ESLint guard (warn on any usage): "no-restricted-comments": [ "warn", { - "terms": ["istanbul ignore next", "istanbul ignore if", "istanbul ignore else", "istanbul ignore file", "c8 ignore next"], + "terms": [ + "istanbul ignore next", + "istanbul ignore if", + "istanbul ignore else", + "istanbul ignore file", + "c8 ignore next" + ], "location": "anywhere", "message": "Coverage ignore detected: add `-- reason` and ensure policy compliance." } @@ -217,11 +236,10 @@ Optional ESLint guard (warn on any usage): ```ts if (cacheEnabled) { - warmCache() -} -/* istanbul ignore else -- cold path is a telemetry-only fallback */ -else { - coldStartWithTelemetry() + warmCache(); +} else { + /* istanbul ignore else -- cold path is a telemetry-only fallback */ + coldStartWithTelemetry(); } ``` @@ -231,7 +249,7 @@ else { // Calls a native API that only exists on macOS ≥ 13: if (isDarwin13Plus()) { /* istanbul ignore next -- native API unavailable in CI runners */ - enableFancyTerminal() + enableFancyTerminal(); } ``` @@ -252,18 +270,15 @@ nyc.exclude += ["src/generated/**"] // in package.json nyc config ``` 2. **Classify** - - * ✅ Legitimate (add/verify reason, minimize scope) - * 🟡 Replaceable (write a test or refactor) - * 🔴 Remove/ban (business logic, overly broad) + - ✅ Legitimate (add/verify reason, minimize scope) + - 🟡 Replaceable (write a test or refactor) + - 🔴 Remove/ban (business logic, overly broad) 3. **Refactor & test** - - * Extract logic from side effects; inject collaborators; mock clocks/randomness. + - Extract logic from side effects; inject collaborators; mock clocks/randomness. 4. **Guard** - - * Add CI script and ESLint rule to prevent regressions. + - Add CI script and ESLint rule to prevent regressions. --- @@ -282,7 +297,7 @@ A: Use one approach consistently. If switching to V8 coverage, update directives ## Checklist for new code -* [ ] Coverage added for changed behavior. -* [ ] No new `istanbul`/`c8` ignores **unless** justified and minimal. -* [ ] Each ignore has `-- reason` and (optionally) a ticket reference. -* [ ] Generated/vendor code excluded via config, not inline comments. \ No newline at end of file +- [ ] Coverage added for changed behavior. +- [ ] No new `istanbul`/`c8` ignores **unless** justified and minimal. +- [ ] Each ignore has `-- reason` and (optionally) a ticket reference. +- [ ] Generated/vendor code excluded via config, not inline comments. diff --git a/docs/threeJs un test environments.md b/docs/threeJs un test environments.md index aa4a0a0..e3936a4 100644 --- a/docs/threeJs un test environments.md +++ b/docs/threeJs un test environments.md @@ -10,26 +10,26 @@ General playbook: start with pure math in Node; add jsdom only when the DOM is i **A. Pure unit tests (math & data structures) — Node env** -* What to test: `Color`, `Vector*`, `Matrix4`, material parameter objects, helpers with no DOM. -* Environment: `testEnvironment: "node"` (fast, no DOM). -* ESM note: Three.js is ESM; configure Jest for ESM (see §3). Jest’s ESM support is still labeled “experimental”, so follow their steps (e.g., `--experimental-vm-modules`) ([jestjs.io][1]). +- What to test: `Color`, `Vector*`, `Matrix4`, material parameter objects, helpers with no DOM. +- Environment: `testEnvironment: "node"` (fast, no DOM). +- ESM note: Three.js is ESM; configure Jest for ESM (see §3). Jest’s ESM support is still labeled “experimental”, so follow their steps (e.g., `--experimental-vm-modules`) ([jestjs.io][1]). **B. DOM integration (no WebGL) — jsdom env** -* What to test: code that touches `document`, sizes a ``, event wiring, etc. -* Environment: `jest-environment-jsdom` (install it—Jest stopped bundling it in v28+) ([jestjs.io][2]). -* Canvas note: jsdom treats `` like a `
` unless you add the `canvas` package or a mock; otherwise `getContext` throws “Not implemented” ([GitHub][3]). +- What to test: code that touches `document`, sizes a ``, event wiring, etc. +- Environment: `jest-environment-jsdom` (install it—Jest stopped bundling it in v28+) ([jestjs.io][2]). +- Canvas note: jsdom treats `` like a `
` unless you add the `canvas` package or a mock; otherwise `getContext` throws “Not implemented” ([GitHub][3]). **C. WebGL integration in Node — “real” GL via headless-gl** -* Goal: run `WebGLRenderer` for pixel-level assertions without a browser. -* Tooling: inject a WebGL context from **headless-gl** (`gl` package). It provides a WebGL context in Node, aiming at WebGL 1.0.3 (WebGL2 support is marked experimental) ([GitHub][4]). +- Goal: run `WebGLRenderer` for pixel-level assertions without a browser. +- Tooling: inject a WebGL context from **headless-gl** (`gl` package). It provides a WebGL context in Node, aiming at WebGL 1.0.3 (WebGL2 support is marked experimental) ([GitHub][4]). **D. Visual/E2E — real browser (Playwright)** -* Goal: catch rendering regressions with screenshots. -* Use Playwright’s built-in screenshot snapshots: `await expect(page).toHaveScreenshot()` ([Playwright][5]). -* For headless WebGL performance/stability, pass GPU flags like `--use-gl=egl`/`--use-gl=desktop` (Chrome/Chromium); teams report big wins with those on CI ([Michel Krämer’s portfolio and blog][6]). +- Goal: catch rendering regressions with screenshots. +- Use Playwright’s built-in screenshot snapshots: `await expect(page).toHaveScreenshot()` ([Playwright][5]). +- For headless WebGL performance/stability, pass GPU flags like `--use-gl=egl`/`--use-gl=desktop` (Chrome/Chromium); teams report big wins with those on CI ([Michel Krämer’s portfolio and blog][6]). --- @@ -38,15 +38,13 @@ General playbook: start with pure math in Node; add jsdom only when the DOM is i 1. **Keep math logic separate** from renderer code. Most of Three’s classes are pure and test nicely in Node. 2. **When you need `WebGLRenderer` in Jest**, prefer: - - * **Node + headless-gl** for fast, deterministic pixel tests (WebGL1 feature set), or - * **Playwright** for WebGL2 features, GPU-specific paths, and full browser semantics. + - **Node + headless-gl** for fast, deterministic pixel tests (WebGL1 feature set), or + - **Playwright** for WebGL2 features, GPU-specific paths, and full browser semantics. 3. **For DOM-only tests**, use jsdom and (optionally) `canvas`/`jest-canvas-mock` only when you touch `` APIs; don’t pay the cost otherwise. jsdom’s canvas support requires the `canvas` peer dependency; without it, `` behaves like a `
` and `getContext` is unimplemented ([GitHub][3]). 4. **Do serialized/state snapshots instead of object graph snapshots**: - - * Use `object3D.toJSON()` and `ObjectLoader` for stable, text-based snapshots of scenes/meshes/materials ([threejs.org][7]). + - Use `object3D.toJSON()` and `ObjectLoader` for stable, text-based snapshots of scenes/meshes/materials ([threejs.org][7]). 5. **When you need pixels**, render to a **`WebGLRenderTarget`** and read with `renderer.readRenderTargetPixels(...)`. It wraps `gl.readPixels` and works well in Node+headless-gl. Be mindful of types (reads `UnsignedByteType`) and limitations (not MRT/MSAA targets) ([threejs.org][8]). @@ -74,10 +72,10 @@ General playbook: start with pure math in Node; add jsdom only when the DOM is i ```js /** @type {import('jest').Config} */ export default { - testEnvironment: "node", - transform: {}, // don't transpile to CJS; keep ESM - extensionsToTreatAsEsm: [".ts", ".tsx", ".jsx"], - moduleNameMapper: {}, // if you alias paths + testEnvironment: 'node', + transform: {}, // don't transpile to CJS; keep ESM + extensionsToTreatAsEsm: ['.ts', '.tsx', '.jsx'], + moduleNameMapper: {}, // if you alias paths }; ``` @@ -95,16 +93,16 @@ npm i -D jest-environment-jsdom canvas ```js export default { - testEnvironment: "jsdom", - setupFilesAfterEnv: ["/test/setup-canvas.ts"] -} + testEnvironment: 'jsdom', + setupFilesAfterEnv: ['/test/setup-canvas.ts'], +}; ``` **`test/setup-canvas.ts`** ```ts // If you need a 2D canvas API in jsdom: -import "canvas"; // jsdom will pick this up automatically (peer dep) +import 'canvas'; // jsdom will pick this up automatically (peer dep) // Or, alternative: jest-canvas-mock if you only need basic APIs: // import "jest-canvas-mock"; ``` @@ -123,8 +121,8 @@ npm i -D gl ```js export default { - testEnvironment: "jsdom", // we want a DOM + our own WebGL context - setupFilesAfterEnv: ["/test/setup-webgl.ts"], + testEnvironment: 'jsdom', // we want a DOM + our own WebGL context + setupFilesAfterEnv: ['/test/setup-webgl.ts'], transform: {}, }; ``` @@ -133,11 +131,11 @@ export default { ```ts // Inject a WebGL (WebGL1) context for .getContext('webgl') -import createGL from "gl"; +import createGL from 'gl'; const orig = HTMLCanvasElement.prototype.getContext; -HTMLCanvasElement.prototype.getContext = function(type: string, attrs?: any) { - if (type === "webgl" || type === "experimental-webgl") { +HTMLCanvasElement.prototype.getContext = function (type: string, attrs?: any) { + if (type === 'webgl' || type === 'experimental-webgl') { const w = this.width || 1; const h = this.height || 1; const gl = createGL(w, h, { preserveDrawingBuffer: true, ...attrs }); @@ -147,21 +145,24 @@ HTMLCanvasElement.prototype.getContext = function(type: string, attrs?: any) { }; ``` -* `gl` creates a WebGL context in Node and targets WebGL 1.0.3; WebGL2 is experimental. For WebGL2 features, prefer Playwright (browser) tests ([GitHub][4]). -* Community pattern: mock `getContext` to return headless-gl in Jest/jsdom ([three.js forum][9]). +- `gl` creates a WebGL context in Node and targets WebGL 1.0.3; WebGL2 is experimental. For WebGL2 features, prefer Playwright (browser) tests ([GitHub][4]). +- Community pattern: mock `getContext` to return headless-gl in Jest/jsdom ([three.js forum][9]). **Example pixel test (Node):** ```ts -import * as THREE from "three"; +import * as THREE from 'three'; -test("renders red pixel", () => { - const canvas = document.createElement("canvas"); +test('renders red pixel', () => { + const canvas = document.createElement('canvas'); canvas.width = canvas.height = 4; - const renderer = new THREE.WebGLRenderer({ canvas, preserveDrawingBuffer: true }); + const renderer = new THREE.WebGLRenderer({ + canvas, + preserveDrawingBuffer: true, + }); const scene = new THREE.Scene(); - scene.background = new THREE.Color("red"); + scene.background = new THREE.Color('red'); const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 10); camera.position.z = 1; @@ -175,8 +176,8 @@ test("renders red pixel", () => { const buf = new Uint8Array(4); renderer.readRenderTargetPixels(rt, 0, 0, 1, 1, buf); // wraps gl.readPixels expect(buf[0]).toBe(255); // R - expect(buf[1]).toBe(0); // G - expect(buf[2]).toBe(0); // B + expect(buf[1]).toBe(0); // G + expect(buf[2]).toBe(0); // B rt.dispose(); renderer.dispose(); // frees GPU resources @@ -203,11 +204,9 @@ export default defineConfig({ use: { headless: true, // Enable GPU in headless Chromium for WebGL stability/perf in CI: - launchOptions: { args: ['--use-gl=egl'] } // or '--use-gl=desktop' + launchOptions: { args: ['--use-gl=egl'] }, // or '--use-gl=desktop' }, - projects: [ - { name: 'Chromium', use: { ...devices['Desktop Chrome'] } }, - ] + projects: [{ name: 'Chromium', use: { ...devices['Desktop Chrome'] } }], }); ``` @@ -232,29 +231,28 @@ test('canvas rendering is stable', async ({ page }) => { ## 4) Patterns for reliable, maintainable Three.js tests -* **Prefer small offscreen buffers.** Render to a tiny `WebGLRenderTarget` (e.g., 64×64) for speed and stability, then read a pixel/patch with `readRenderTargetPixels` ([threejs.org][8]). - -* **Snapshot structure, not instances.** Three objects contain cycles and methods; JSON-snapshot with `object.toJSON()` and reconstruct via `ObjectLoader` when needed ([threejs.org][7]). +- **Prefer small offscreen buffers.** Render to a tiny `WebGLRenderTarget` (e.g., 64×64) for speed and stability, then read a pixel/patch with `readRenderTargetPixels` ([threejs.org][8]). -* **Be deterministic.** Seed `Math.random` (e.g., with `seedrandom`) for particle initializers or jittered sampling; keep camera and lights fixed. +- **Snapshot structure, not instances.** Three objects contain cycles and methods; JSON-snapshot with `object.toJSON()` and reconstruct via `ObjectLoader` when needed ([threejs.org][7]). -* **Stabilize renderer output.** Fix renderer size, color space and tone mapping in tests to avoid diff noise. (E.g., set size, background, and avoid dynamic exposure.) +- **Be deterministic.** Seed `Math.random` (e.g., with `seedrandom`) for particle initializers or jittered sampling; keep camera and lights fixed. -* **Clean up.** Call `dispose()` on `WebGLRenderer`, `WebGLRenderTarget`, `Geometry/BufferGeometry`, `Material` and `Texture`s you create. Do not render after disposing the renderer; it removes important listeners and should end its lifecycle ([threejs.org][8]). +- **Stabilize renderer output.** Fix renderer size, color space and tone mapping in tests to avoid diff noise. (E.g., set size, background, and avoid dynamic exposure.) -* **jsdom gotchas.** +- **Clean up.** Call `dispose()` on `WebGLRenderer`, `WebGLRenderTarget`, `Geometry/BufferGeometry`, `Material` and `Texture`s you create. Do not render after disposing the renderer; it removes important listeners and should end its lifecycle ([threejs.org][8]). - * Without `canvas` (or a mock), `HTMLCanvasElement.prototype.getContext` is unimplemented and returns errors; add `canvas` or a mock package ([GitHub][3]). - * A canvas can only have one context type; requesting `2d` first will prevent a later WebGL context on the same canvas ([MDN Web Docs][10]). +- **jsdom gotchas.** + - Without `canvas` (or a mock), `HTMLCanvasElement.prototype.getContext` is unimplemented and returns errors; add `canvas` or a mock package ([GitHub][3]). + - A canvas can only have one context type; requesting `2d` first will prevent a later WebGL context on the same canvas ([MDN Web Docs][10]). -* **Know headless-gl’s limits.** WebGL2 features may not work; if you depend on them, run those tests in a real browser via Playwright ([GitHub][4]). +- **Know headless-gl’s limits.** WebGL2 features may not work; if you depend on them, run those tests in a real browser via Playwright ([GitHub][4]). --- ## 5) Optional: packaged mocks & visual matchers -* **`jest-webgl-canvas-mock`** (combines `jest-canvas-mock` + a WebGL mock) — good for libraries like Pixi or basic WebGL expectations when you don’t need real GPU behavior ([npm][11]). -* **`jest-image-snapshot`** — pixelmatch-based image comparator for Jest; useful when you can produce PNGs (e.g., via Playwright screenshots or `toDataURL` with `canvas`) ([GitHub][12]). +- **`jest-webgl-canvas-mock`** (combines `jest-canvas-mock` + a WebGL mock) — good for libraries like Pixi or basic WebGL expectations when you don’t need real GPU behavior ([npm][11]). +- **`jest-image-snapshot`** — pixelmatch-based image comparator for Jest; useful when you can produce PNGs (e.g., via Playwright screenshots or `toDataURL` with `canvas`) ([GitHub][12]). --- @@ -267,19 +265,19 @@ Run fast unit tests in Node, integration in jsdom+headless-gl: export default { projects: [ { - displayName: "unit", - testEnvironment: "node", - testMatch: ["/test/unit/**/*.test.(ts|js)"], + displayName: 'unit', + testEnvironment: 'node', + testMatch: ['/test/unit/**/*.test.(ts|js)'], transform: {}, }, { - displayName: "int-webgl", - testEnvironment: "jsdom", - setupFilesAfterEnv: ["/test/setup-webgl.ts"], - testMatch: ["/test/integration/**/*.test.(ts|js)"], + displayName: 'int-webgl', + testEnvironment: 'jsdom', + setupFilesAfterEnv: ['/test/setup-webgl.ts'], + testMatch: ['/test/integration/**/*.test.(ts|js)'], transform: {}, - } - ] + }, + ], }; ``` @@ -287,21 +285,21 @@ export default { ## 7) CI notes -* **Playwright + GPU:** on Linux runners, flags like `--use-gl=egl` are often necessary; Firefox headless historically disables WebGL, so prefer Chromium for WebGL E2E in headless mode ([Michel Krämer’s portfolio and blog][6]). -* **headless-gl system deps:** Linux may need Mesa/GL dev libs; see README for required packages and hints for CI images ([GitHub][4]). +- **Playwright + GPU:** on Linux runners, flags like `--use-gl=egl` are often necessary; Firefox headless historically disables WebGL, so prefer Chromium for WebGL E2E in headless mode ([Michel Krämer’s portfolio and blog][6]). +- **headless-gl system deps:** Linux may need Mesa/GL dev libs; see README for required packages and hints for CI images ([GitHub][4]). --- ## 8) Quick reference links -* Jest ESM guide (how to enable, flags, mocking in ESM) ([jestjs.io][1]) -* jsdom README — canvas support via `canvas` package ([GitHub][3]) -* headless-gl README (WebGL in Node; WebGL1 target, WebGL2 experimental; system deps) ([GitHub][4]) -* Playwright screenshot/snapshot assertions ([Playwright][5]) -* `WebGLRenderer.readRenderTargetPixels` and `dispose()` docs ([threejs.org][8]) -* `Object3D.toJSON` / `ObjectLoader` for serializing scene graphs ([threejs.org][7]) -* jsdom “getContext” not implemented error background ([Stack Overflow][13]) -* GPU flags for headless Chromium (`--use-gl=egl`/`--use-gl=desktop`) ([Michel Krämer’s portfolio and blog][6]) +- Jest ESM guide (how to enable, flags, mocking in ESM) ([jestjs.io][1]) +- jsdom README — canvas support via `canvas` package ([GitHub][3]) +- headless-gl README (WebGL in Node; WebGL1 target, WebGL2 experimental; system deps) ([GitHub][4]) +- Playwright screenshot/snapshot assertions ([Playwright][5]) +- `WebGLRenderer.readRenderTargetPixels` and `dispose()` docs ([threejs.org][8]) +- `Object3D.toJSON` / `ObjectLoader` for serializing scene graphs ([threejs.org][7]) +- jsdom “getContext” not implemented error background ([Stack Overflow][13]) +- GPU flags for headless Chromium (`--use-gl=egl`/`--use-gl=desktop`) ([Michel Krämer’s portfolio and blog][6]) --- @@ -318,16 +316,16 @@ Install `canvas` or a dedicated mock package; by default jsdom doesn’t impleme --- -[1]: https://jestjs.io/docs/ecmascript-modules "ECMAScript Modules · Jest" -[2]: https://jestjs.io/docs/next/tutorial-jquery?utm_source=chatgpt.com "DOM Manipulation" -[3]: https://github.com/jsdom/jsdom "GitHub - jsdom/jsdom: A JavaScript implementation of various web standards, for use with Node.js" -[4]: https://github.com/stackgl/headless-gl "GitHub - stackgl/headless-gl: Windowless WebGL for node.js" -[5]: https://playwright.dev/docs/test-snapshots?utm_source=chatgpt.com "Visual comparisons" -[6]: https://michelkraemer.com/enable-gpu-for-slow-playwright-tests-in-headless-mode/?utm_source=chatgpt.com "Enable GPU to speed up slow Playwright tests in headless ..." -[7]: https://threejs.org/docs/api/en/loaders/ObjectLoader.html?utm_source=chatgpt.com "ObjectLoader – three.js docs" -[8]: https://threejs.org/docs/?utm_source=chatgpt.com "WebGLRenderer#readRenderTargetPixels" -[9]: https://discourse.threejs.org/t/suggestions-for-unit-testing-with-headless-gl-and-webgl-2/66891?utm_source=chatgpt.com "Suggestions for unit testing with headless-gl and WebGL 2" -[10]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext?utm_source=chatgpt.com "HTMLCanvasElement: getContext() method - Web APIs - MDN" -[11]: https://www.npmjs.com/package/jest-webgl-canvas-mock?utm_source=chatgpt.com "jest-webgl-canvas-mock" -[12]: https://github.com/americanexpress/jest-image-snapshot?utm_source=chatgpt.com "americanexpress/jest-image-snapshot" -[13]: https://stackoverflow.com/questions/48828759/unit-test-raises-error-because-of-getcontext-is-not-implemented?utm_source=chatgpt.com "unit test raises error because of .getContext() is not ..." +[1]: https://jestjs.io/docs/ecmascript-modules 'ECMAScript Modules · Jest' +[2]: https://jestjs.io/docs/next/tutorial-jquery?utm_source=chatgpt.com 'DOM Manipulation' +[3]: https://github.com/jsdom/jsdom 'GitHub - jsdom/jsdom: A JavaScript implementation of various web standards, for use with Node.js' +[4]: https://github.com/stackgl/headless-gl 'GitHub - stackgl/headless-gl: Windowless WebGL for node.js' +[5]: https://playwright.dev/docs/test-snapshots?utm_source=chatgpt.com 'Visual comparisons' +[6]: https://michelkraemer.com/enable-gpu-for-slow-playwright-tests-in-headless-mode/?utm_source=chatgpt.com 'Enable GPU to speed up slow Playwright tests in headless ...' +[7]: https://threejs.org/docs/api/en/loaders/ObjectLoader.html?utm_source=chatgpt.com 'ObjectLoader – three.js docs' +[8]: https://threejs.org/docs/?utm_source=chatgpt.com 'WebGLRenderer#readRenderTargetPixels' +[9]: https://discourse.threejs.org/t/suggestions-for-unit-testing-with-headless-gl-and-webgl-2/66891?utm_source=chatgpt.com 'Suggestions for unit testing with headless-gl and WebGL 2' +[10]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext?utm_source=chatgpt.com 'HTMLCanvasElement: getContext() method - Web APIs - MDN' +[11]: https://www.npmjs.com/package/jest-webgl-canvas-mock?utm_source=chatgpt.com 'jest-webgl-canvas-mock' +[12]: https://github.com/americanexpress/jest-image-snapshot?utm_source=chatgpt.com 'americanexpress/jest-image-snapshot' +[13]: https://stackoverflow.com/questions/48828759/unit-test-raises-error-because-of-getcontext-is-not-implemented?utm_source=chatgpt.com 'unit test raises error because of .getContext() is not ...' diff --git a/package.json b/package.json index db1b317..fea5996 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "TemplateJs is a minimal single-page web app template designed for quick deployment. It follows an AI-assisted iterative development process:", "main": "index.js", "scripts": { - "test": "npm run format && npm run lint && npm run check:all && npm run test:unit && npm run test:e2e:index", + "test": "npm run format && npm run lint && npm run check:all && npm run test:unit && npm run test:e2e:index", "format": "prettier --write --ignore-unknown --no-error-on-unmatched-pattern \"src\" \"pages\" \"config\" \"docs\" index.html README.md package.json", "test:unit": "jest --coverage --config config/jest.config.js", "lint": "eslint . --config config/eslint.config.js", @@ -16,8 +16,8 @@ "check:all": "npm run check:dup && npm run check:cycles && npm run check:boundaries", "validate:all": "npm run test && npm run mutation", "serve:static": "serve -l 4173 .", - "test:e2e": "playwright test --config playwright-ui-tests/playwright.config.js", - "test:e2e:index": "playwright test --config playwright-ui-tests/playwright.config.js --project=chromium playwright-ui-tests/index.spec.js", + "test:e2e": "playwright test --config playwright-ui-tests/playwright.config.js", + "test:e2e:index": "playwright test --config playwright-ui-tests/playwright.config.js --project=chromium playwright-ui-tests/index.spec.js", "test:e2e:artifacts": "PLAYWRIGHT_CAPTURE=1 playwright test --config playwright-ui-tests/playwright.config.js", "test:e2e:headed": "playwright test --config playwright-ui-tests/playwright.config.js --headed", "test:e2e:ui": "playwright test --config playwright-ui-tests/playwright.config.js --ui" diff --git a/pages/about.html b/pages/about.html index d9246e7..8e71a1f 100644 --- a/pages/about.html +++ b/pages/about.html @@ -77,9 +77,7 @@