diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27b9accd..b1943c58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -42,7 +42,7 @@ jobs: strategy: matrix: - node-version: ['22.13.1', 24] + node-version: ['22.18.0', 24] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 diff --git a/.github/workflows/release-ember-client.yml b/.github/workflows/release-ember-client.yml index 5a980411..e78743e7 100644 --- a/.github/workflows/release-ember-client.yml +++ b/.github/workflows/release-ember-client.yml @@ -39,7 +39,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -124,7 +124,7 @@ jobs: - name: Set Node.js back to release version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Install dependencies diff --git a/.github/workflows/release-ruby-client.yml b/.github/workflows/release-ruby-client.yml index 55e6632c..13957bb2 100644 --- a/.github/workflows/release-ruby-client.yml +++ b/.github/workflows/release-ruby-client.yml @@ -34,7 +34,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Set up Ruby uses: ruby/setup-ruby@97ecb7b512899eb71ab1bf2310a624c6f1589ac6 # v1 diff --git a/.github/workflows/release-static-site-client.yml b/.github/workflows/release-static-site-client.yml index d4febdf5..d26426a4 100644 --- a/.github/workflows/release-static-site-client.yml +++ b/.github/workflows/release-static-site-client.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -115,7 +115,7 @@ jobs: - name: Set Node.js back to release version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Install dependencies diff --git a/.github/workflows/release-storybook-client.yml b/.github/workflows/release-storybook-client.yml index 9cf55f7f..9f5b7885 100644 --- a/.github/workflows/release-storybook-client.yml +++ b/.github/workflows/release-storybook-client.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -115,7 +115,7 @@ jobs: - name: Set Node.js back to release version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Install dependencies diff --git a/.github/workflows/release-swift-client.yml b/.github/workflows/release-swift-client.yml index 44c32d26..6389de07 100644 --- a/.github/workflows/release-swift-client.yml +++ b/.github/workflows/release-swift-client.yml @@ -33,7 +33,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -150,7 +150,7 @@ jobs: - name: Restore Node.js for publishing uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Restore npm for publishing diff --git a/.github/workflows/release-vitest-client.yml b/.github/workflows/release-vitest-client.yml index 78f890a4..923ff113 100644 --- a/.github/workflows/release-vitest-client.yml +++ b/.github/workflows/release-vitest-client.yml @@ -34,7 +34,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -115,7 +115,7 @@ jobs: - name: Set Node.js back to release version uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Install dependencies diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7a96128..f5e75e83 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Upgrade npm for Trusted Publishers @@ -159,7 +159,7 @@ jobs: - name: Restore Node.js for publishing uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.14.0' + node-version: '22.18.0' registry-url: 'https://registry.npmjs.org' - name: Restore npm and pnpm for publishing diff --git a/.github/workflows/reporter.yml b/.github/workflows/reporter.yml index ea0ae830..08a9c013 100644 --- a/.github/workflows/reporter.yml +++ b/.github/workflows/reporter.yml @@ -15,10 +15,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | diff --git a/.github/workflows/sdk-e2e.yml b/.github/workflows/sdk-e2e.yml index 037f9694..ec920a62 100644 --- a/.github/workflows/sdk-e2e.yml +++ b/.github/workflows/sdk-e2e.yml @@ -16,10 +16,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -78,10 +78,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -141,10 +141,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -221,10 +221,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -295,10 +295,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -366,10 +366,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -424,10 +424,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | diff --git a/.github/workflows/sdk-unit.yml b/.github/workflows/sdk-unit.yml index 5f15859f..2d68d3af 100644 --- a/.github/workflows/sdk-unit.yml +++ b/.github/workflows/sdk-unit.yml @@ -62,10 +62,10 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | @@ -103,7 +103,7 @@ jobs: strategy: matrix: - node-version: ['22.13.1', 24] + node-version: ['22.18.0', 24] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -153,7 +153,7 @@ jobs: strategy: matrix: - node-version: ['22.13.1', 24] + node-version: ['22.18.0', 24] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -195,7 +195,7 @@ jobs: # Vitest SDK - runs on multiple Node versions vitest: - name: Vitest SDK + name: Vitest SDK (Node ${{ matrix.node-version }}, Vitest ${{ matrix.vitest-version }}) runs-on: ubuntu-latest timeout-minutes: 8 needs: changes @@ -203,7 +203,8 @@ jobs: strategy: matrix: - node-version: ['22.13.1', 24] + node-version: ['22.18.0', 24] + vitest-version: [4, 5] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -229,6 +230,11 @@ jobs: working-directory: ./clients/vitest run: pnpm install --frozen-lockfile + - name: Install Vitest 4 for compatibility tests + if: matrix.vitest-version == 4 + working-directory: ./clients/vitest + run: pnpm add -D vitest@4.1.11 @vitest/browser@4.1.11 @vitest/browser-playwright@4.1.11 + - name: Get installed Playwright version working-directory: ./clients/vitest id: playwright-version @@ -250,6 +256,12 @@ jobs: working-directory: ./clients/vitest run: pnpm run lint + - name: Run Vitest client browser tests + working-directory: ./clients/vitest + run: pnpm run test:browser + env: + CI: true + - name: Run Vitest client unit tests working-directory: ./clients/vitest run: pnpm run test:unit @@ -290,10 +302,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Install pnpm run: | diff --git a/.github/workflows/tui.yml b/.github/workflows/tui.yml index 19985417..d53b4902 100644 --- a/.github/workflows/tui.yml +++ b/.github/workflows/tui.yml @@ -15,10 +15,10 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Use Node.js 22.13.1 + - name: Use Node.js 22.18.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: '22.13.1' + node-version: '22.18.0' - name: Setup tui-driver uses: vizzly-testing/tui-driver@f21ea93489f2160eccf4d58844e456a9b38a0491 # main diff --git a/README.md b/README.md index 66242053..9a1b98c1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ build or diff data for scripts, automation, and coding agents. ## Quick Start -You need Node.js 22+. +You need Node.js 22.18 or newer within Node 22, or Node.js 24+. ```bash pnpm install -g @vizzly-testing/cli @@ -43,6 +43,19 @@ existing Vizzly config. Use `vizzly init --agent-skill` to install or refresh only the local skill, or `vizzly init --skip-agent-skill` when you want config without the agent prompt. +### TypeScript Configuration + +Vizzly loads `vizzly.config.ts` with Node's native type stripping. Type +annotations, interfaces, and `satisfies` work without installing TypeScript. +Use `import type` for type-only imports and include file extensions when +importing local helpers, such as `./helpers.ts`. + +Enums, namespaces containing runtime code, and constructor parameter properties +aren't supported. Replace them with ordinary JavaScript values and assignments. +Node doesn't apply `tsconfig.json` path aliases when loading your config. +These restrictions apply to the config and its imports; they don't change how +your application or tests compile. + ### Start Local TDD Start the TDD server, run your tests, and open the dashboard at the URL the diff --git a/clients/ember/package.json b/clients/ember/package.json index f2b02e21..ffd46188 100644 --- a/clients/ember/package.json +++ b/clients/ember/package.json @@ -61,7 +61,7 @@ "check:fix": "biome check --write src tests bin" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "publishConfig": { "access": "public", diff --git a/clients/static-site/CHANGELOG.md b/clients/static-site/CHANGELOG.md index fab908d2..7adec232 100644 --- a/clients/static-site/CHANGELOG.md +++ b/clients/static-site/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Require Node.js 22.18 or newer within Node 22, or Node.js 24+. +- Remove the unused Cosmiconfig dependency. Configuration still comes from + the CLI and the existing interactions file loader. + ## [0.3.0] - 2026-06-01 ## What's Changed diff --git a/clients/static-site/README.md b/clients/static-site/README.md index e54ef0ec..7093bd6b 100644 --- a/clients/static-site/README.md +++ b/clients/static-site/README.md @@ -2,6 +2,10 @@ Seamlessly integrate your static sites (Gatsby, Astro, Jekyll, Next.js static export, etc.) into Vizzly's visual development workflow. Iterate locally with `vizzly tdd`, automatically create team builds from CI/CD, and collaborate on visual changes with position-based comments and review rules. +## Requirements + +Use Node.js 22.18 or newer within Node 22, or Node.js 24+. + ## Installation ```bash diff --git a/clients/static-site/package.json b/clients/static-site/package.json index 5f819a36..566ee2f0 100644 --- a/clients/static-site/package.json +++ b/clients/static-site/package.json @@ -57,7 +57,7 @@ "check:fix": "biome check --write src" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "publishConfig": { "access": "public", @@ -67,7 +67,6 @@ "@vizzly-testing/cli": ">=0.24.0" }, "dependencies": { - "cosmiconfig": "^9.0.0", "fast-xml-parser": "^5.8.0", "playwright-core": "^1.50.0", "serve-handler": "^6.1.5", diff --git a/clients/storybook/package.json b/clients/storybook/package.json index 064cbdfb..6efa9e75 100644 --- a/clients/storybook/package.json +++ b/clients/storybook/package.json @@ -53,7 +53,7 @@ "check:fix": "biome check --write src tests" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "publishConfig": { "access": "public", diff --git a/clients/swift/PREVIEWS.md b/clients/swift/PREVIEWS.md index bd27159b..78f68f0f 100644 --- a/clients/swift/PREVIEWS.md +++ b/clients/swift/PREVIEWS.md @@ -8,7 +8,7 @@ Preview capture is optional. It does not change the `Vizzly` or ## Requirements - Xcode 26.6 -- Node.js 22+ +- Node.js 22.18 or newer within Node 22, or Node.js 24+ - An arm64 Mac - An iOS 17+ Simulator - A scene-based iOS app diff --git a/clients/swift/package.json b/clients/swift/package.json index c168e7a3..a522dd6a 100644 --- a/clients/swift/package.json +++ b/clients/swift/package.json @@ -45,7 +45,7 @@ "prepublishOnly": "pnpm run check" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "peerDependencies": { "@vizzly-testing/cli": ">=0.36.0" diff --git a/clients/vitest/CHANGELOG.md b/clients/vitest/CHANGELOG.md index d1c8096a..1bd04103 100644 --- a/clients/vitest/CHANGELOG.md +++ b/clients/vitest/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- Require Node.js 22.18 or newer within Node 22, or Node.js 24+. + +### Fixed + +- Pass browser settings through Vitest environment variables so screenshot + uploads, build IDs, and fail-on-diff settings work with Vitest 5. +- Honor fail-on-diff during local runs that also carry a build ID. + ## [0.2.0] - 2026-06-01 ## What's Changed diff --git a/clients/vitest/README.md b/clients/vitest/README.md index e57e3e4d..869c11ed 100644 --- a/clients/vitest/README.md +++ b/clients/vitest/README.md @@ -22,6 +22,10 @@ standard `toMatchScreenshot` API - no code changes required! - 🚀 **CI/CD Ready** - Parallel execution and baseline management - 🔬 **Better Diffing** - Powered by honeydiff (Rust-based, faster than pixelmatch) +## Requirements + +Use Node.js 22.18 or newer within Node 22, or Node.js 24+. + ## Installation ```bash diff --git a/clients/vitest/package.json b/clients/vitest/package.json index 96117047..76050f9c 100644 --- a/clients/vitest/package.json +++ b/clients/vitest/package.json @@ -40,6 +40,7 @@ "scripts": { "test": "pnpm run test:unit", "test:unit": "vitest run --config vitest.config.js", + "test:browser": "node --test --test-reporter=spec tests/browser-integration.test.js", "test:e2e": "vitest run --config vitest.e2e.config.js", "test:e2e:tdd": "../../bin/vizzly.js tdd run 'pnpm run test:e2e' --no-open", "test:e2e:cloud": "../../bin/vizzly.js run 'pnpm run test:e2e'", @@ -52,7 +53,7 @@ "prepublishOnly": "pnpm run lint && pnpm run test:unit" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "publishConfig": { "access": "public", @@ -64,10 +65,10 @@ }, "devDependencies": { "@biomejs/biome": "^2.5.10", - "@vitest/browser": "^4.1.11", - "@vitest/browser-playwright": "^4.1.11", + "@vitest/browser": "^5.0.0", + "@vitest/browser-playwright": "^5.0.0", "playwright": "^1.49.1", "serve-handler": "^6.1.6", - "vitest": "^4.1.11" + "vitest": "^5.0.0" } } diff --git a/clients/vitest/src/index.js b/clients/vitest/src/index.js index 3dd77ff7..5e68c7d2 100644 --- a/clients/vitest/src/index.js +++ b/clients/vitest/src/index.js @@ -91,19 +91,18 @@ export function vizzlyPlugin() { return { test: { - setupFiles: [resolve(import.meta.dirname || __dirname, 'setup.js')], + setupFiles: [resolve(import.meta.dirname, 'setup.js')], + env: { + VIZZLY_SERVER_URL: serverUrl, + VIZZLY_BUILD_ID: buildId, + VIZZLY_FAIL_ON_DIFF: failOnDiff, + }, browser: { // Disable Vitest's native screenshot testing // Our custom matcher completely replaces it screenshotFailures: false, }, }, - // Pass Vizzly environment variables to browser context via define - define: { - __VIZZLY_SERVER_URL__: JSON.stringify(serverUrl), - __VIZZLY_BUILD_ID__: JSON.stringify(buildId), - __VIZZLY_FAIL_ON_DIFF__: JSON.stringify(failOnDiff), - }, }; }, }; diff --git a/clients/vitest/src/setup.js b/clients/vitest/src/setup.js index bf658a16..ef59e8d2 100644 --- a/clients/vitest/src/setup.js +++ b/clients/vitest/src/setup.js @@ -70,10 +70,7 @@ export function shouldFailOnDiff(override = null) { return override === true; } - let value = - typeof __VIZZLY_FAIL_ON_DIFF__ !== 'undefined' - ? __VIZZLY_FAIL_ON_DIFF__ - : ''; + let value = import.meta.env.VIZZLY_FAIL_ON_DIFF || ''; return value === true || value === 'true' || value === '1'; } @@ -84,10 +81,8 @@ async function toMatchScreenshot(element, name, options = {}) { name = args.name; options = args.options; - let serverUrl = - typeof __VIZZLY_SERVER_URL__ !== 'undefined' ? __VIZZLY_SERVER_URL__ : ''; - let buildId = - typeof __VIZZLY_BUILD_ID__ !== 'undefined' ? __VIZZLY_BUILD_ID__ : ''; + let serverUrl = import.meta.env.VIZZLY_SERVER_URL || ''; + let buildId = import.meta.env.VIZZLY_BUILD_ID || ''; // If no server URL, Vizzly is not available if (!serverUrl) { @@ -155,8 +150,8 @@ async function toMatchScreenshot(element, name, options = {}) { let result = await response.json(); - // In cloud mode, always pass - if (buildId) { + // Local runs also have build IDs; the server response identifies TDD mode. + if (buildId && result.tddMode !== true) { return { pass: true, message: () => '', diff --git a/clients/vitest/tests/browser-integration.test.js b/clients/vitest/tests/browser-integration.test.js new file mode 100644 index 00000000..d277c1d8 --- /dev/null +++ b/clients/vitest/tests/browser-integration.test.js @@ -0,0 +1,150 @@ +import assert from 'node:assert/strict'; +import { execFile } from 'node:child_process'; +import { + cp, + mkdtemp, + readdir, + readFile, + rm, + symlink, + writeFile, +} from 'node:fs/promises'; +import { createServer } from 'node:http'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { test } from 'node:test'; +import { promisify } from 'node:util'; +import { getDimensionsSync } from '@vizzly-testing/honeydiff'; +import { createApiClient } from '../../../src/api/client.js'; +import { CONFIG_DEFAULTS } from '../../../src/config/core.js'; +import { createApiHandler } from '../../../src/server/handlers/api-handler.js'; +import { createTddHandler } from '../../../src/server/handlers/tdd-handler.js'; +import { createHttpServer } from '../../../src/server/http-server.js'; + +let run = promisify(execFile); +let packageDir = resolve(import.meta.dirname, '..'); +let vitestCli = join(packageDir, 'node_modules/vitest/vitest.mjs'); + +for (let scenario of ['disabled', 'tdd', 'tdd-run', 'cloud']) { + test(`browser matcher works in ${scenario} mode`, async t => { + let workingDir = await mkdtemp(join(tmpdir(), 'vizzly-vitest-browser-')); + t.after(() => rm(workingDir, { recursive: true, force: true })); + await writeFile(join(workingDir, 'package.json'), '{"type":"module"}'); + await cp(join(packageDir, 'src'), join(workingDir, 'src'), { + recursive: true, + }); + await cp( + join(packageDir, 'tests/browser'), + join(workingDir, 'tests/browser'), + { + recursive: true, + } + ); + await symlink( + join(packageDir, 'node_modules'), + join(workingDir, 'node_modules') + ); + + let uploads = []; + let handler; + let server; + let serverUrl = ''; + let buildId = ''; + if (scenario === 'cloud') { + buildId = 'browser-build'; + } else if (scenario === 'tdd-run') { + buildId = 'local-build'; + } + + if (scenario === 'cloud') { + // Only the external cloud API is replaced; captures use the real local server. + let cloud = createServer(async (req, res) => { + let chunks = []; + for await (let chunk of req) chunks.push(chunk); + uploads.push({ + path: req.url, + body: JSON.parse(Buffer.concat(chunks)), + }); + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ upload_required: true })); + }); + await new Promise(resolve => cloud.listen(0, '127.0.0.1', resolve)); + t.after(() => new Promise(resolve => cloud.close(resolve))); + let client = createApiClient({ + baseUrl: `http://127.0.0.1:${cloud.address().port}`, + token: 'vzt_browser_test', + }); + handler = createApiHandler(client); + } else if (scenario.startsWith('tdd')) { + handler = createTddHandler(CONFIG_DEFAULTS, workingDir); + await handler.initialize(); + } + + if (handler) { + server = createHttpServer(0, handler, { workingDir }); + await server.start(); + t.after(() => server.stop()); + serverUrl = `http://127.0.0.1:${server.getServer().address().port}`; + } + + let env = { + ...process.env, + VIZZLY_HOME: join(workingDir, 'home'), + VIZZLY_SERVER_URL: serverUrl, + VIZZLY_BUILD_ID: buildId, + VIZZLY_FAIL_ON_DIFF: 'true', + TEST_SCENARIO: scenario, + }; + delete env.VIZZLY_TOKEN; + try { + await run( + process.execPath, + [vitestCli, 'run', '--config', 'tests/browser/fixture.config.js'], + { + cwd: workingDir, + env, + } + ); + } catch (error) { + throw new Error(`${error.stdout}\n${error.stderr}`, { cause: error }); + } + + if (scenario === 'disabled') { + let files = await readdir(join(workingDir, 'tests/browser')); + assert.ok(!files.includes('__screenshots__')); + } else if (scenario.startsWith('tdd')) { + let report = JSON.parse( + await readFile(join(workingDir, '.vizzly/report-data.json'), 'utf8') + ); + assert.equal(report.comparisons.length, 3); + let diff = report.comparisons.find(item => item.name === 'changing-page'); + assert.equal(diff.status, 'failed'); + assert.ok(diff.diffPercentage > 0); + } else { + let stats = await handler.flush(); + assert.equal(stats.uploaded, 2); + assert.equal(stats.failed, 0); + assert.ok( + uploads.every( + upload => upload.path === '/api/sdk/builds/browser-build/screenshots' + ) + ); + let captures = uploads.filter(upload => upload.body.image_data); + assert.equal(captures.length, 2); + let page = captures.find(upload => upload.body.name === 'page').body; + assert.equal(page.properties.theme, 'light'); + assert.equal(page.threshold, 5); + assert.equal(page.minClusterSize, 10); + assert.equal(page.captureMode, 'full_page'); + let button = captures.find(upload => upload.body.name === 'button').body; + assert.equal(button.captureMode, 'element'); + assert.deepEqual( + getDimensionsSync(Buffer.from(button.image_data, 'base64')), + { + width: 200, + height: 100, + } + ); + } + }); +} diff --git a/clients/vitest/tests/browser/fixture.config.js b/clients/vitest/tests/browser/fixture.config.js new file mode 100644 index 00000000..3bed8036 --- /dev/null +++ b/clients/vitest/tests/browser/fixture.config.js @@ -0,0 +1,19 @@ +import { resolve } from 'node:path'; +import { playwright } from '@vitest/browser-playwright'; +import { defineConfig } from 'vitest/config'; +import { vizzlyPlugin } from '../../src/index.js'; + +export default defineConfig({ + root: resolve(import.meta.dirname, '../..'), + plugins: [vizzlyPlugin()], + test: { + include: ['tests/browser/matcher.test.js'], + env: { TEST_SCENARIO: process.env.TEST_SCENARIO }, + browser: { + enabled: true, + headless: true, + viewport: { width: 400, height: 300 }, + instances: [{ browser: 'chromium', provider: playwright() }], + }, + }, +}); diff --git a/clients/vitest/tests/browser/matcher.test.js b/clients/vitest/tests/browser/matcher.test.js new file mode 100644 index 00000000..4f82f849 --- /dev/null +++ b/clients/vitest/tests/browser/matcher.test.js @@ -0,0 +1,48 @@ +import { expect, test } from 'vitest'; +import { page } from 'vitest/browser'; + +let scenario = import.meta.env.TEST_SCENARIO; +let properties = { url: 'https://example.test/visual-test' }; + +if (scenario === 'disabled') { + test('passes without capturing when no Vizzly server is available', async () => { + await expect(null).toMatchScreenshot('disabled'); + }); +} else { + test('captures the page with metadata and comparison options', async () => { + document.body.innerHTML = '

Hello Vizzly

'; + await expect(page).toMatchScreenshot('page', { + properties: { ...properties, theme: 'light' }, + fullPage: true, + threshold: 5, + minClusterSize: 10, + }); + }); + + test('captures an element', async () => { + document.body.innerHTML = + ''; + await expect(page.getByRole('button', { name: 'Save' })).toMatchScreenshot( + 'button', + { properties, fullPage: true } + ); + }); + + if (scenario.startsWith('tdd')) { + test('fails on a diff by default and respects the screenshot override', async () => { + document.body.innerHTML = + '
'; + await expect(page).toMatchScreenshot('changing-page', { properties }); + + document.body.firstElementChild.style.background = 'white'; + await expect( + expect(page).toMatchScreenshot('changing-page', { properties }) + ).rejects.toThrow('Visual difference detected: changing-page'); + + await expect(page).toMatchScreenshot('changing-page', { + properties, + failOnDiff: false, + }); + }); + } +} diff --git a/clients/vitest/tests/e2e/example.test.js b/clients/vitest/tests/e2e/example.test.js index 35921d7f..06a02bf9 100644 --- a/clients/vitest/tests/e2e/example.test.js +++ b/clients/vitest/tests/e2e/example.test.js @@ -13,9 +13,8 @@ import { beforeAll, describe, expect, test } from 'vitest'; import { page } from 'vitest/browser'; -// Base URL for test-site assets (defined in vitest.e2e.config.js) -// eslint-disable-next-line no-undef -let baseUrl = __TEST_SITE_URL__; +// Base URL for test-site assets (configured in vitest.e2e.config.js) +let baseUrl = import.meta.env.TEST_SITE_URL; // Load test-site CSS before all tests beforeAll(async () => { diff --git a/clients/vitest/tests/vitest-plugin.spec.js b/clients/vitest/tests/vitest-plugin.spec.js index 7ea3a7fa..5dc4da85 100644 --- a/clients/vitest/tests/vitest-plugin.spec.js +++ b/clients/vitest/tests/vitest-plugin.spec.js @@ -118,10 +118,10 @@ describe('Vitest Plugin Integration', () => { let plugin = vizzlyPlugin(); config = plugin.config(config, { mode: 'test' }); - expect(config.define).toBeDefined(); - expect(config.define.__VIZZLY_SERVER_URL__).toBeDefined(); - expect(config.define.__VIZZLY_BUILD_ID__).toBeDefined(); - expect(config.define.__VIZZLY_FAIL_ON_DIFF__).toBeDefined(); + expect(config.test.env).toBeDefined(); + expect(config.test.env.VIZZLY_SERVER_URL).toBeDefined(); + expect(config.test.env.VIZZLY_BUILD_ID).toBeDefined(); + expect(config.test.env.VIZZLY_FAIL_ON_DIFF).toBeDefined(); }); it('should pass failOnDiff from discovered server config', async () => { @@ -144,10 +144,10 @@ describe('Vitest Plugin Integration', () => { let plugin = vizzlyPlugin(); let config = plugin.config({ test: {} }, { mode: 'test' }); - expect(JSON.parse(config.define.__VIZZLY_SERVER_URL__)).toBe( + expect(config.test.env.VIZZLY_SERVER_URL).toBe( 'http://localhost:47392' ); - expect(JSON.parse(config.define.__VIZZLY_FAIL_ON_DIFF__)).toBe('true'); + expect(config.test.env.VIZZLY_FAIL_ON_DIFF).toBe('true'); } finally { process.chdir(originalCwd); rmSync(tempDir, { recursive: true, force: true }); diff --git a/clients/vitest/vitest.e2e.config.js b/clients/vitest/vitest.e2e.config.js index fabc7240..e2131305 100644 --- a/clients/vitest/vitest.e2e.config.js +++ b/clients/vitest/vitest.e2e.config.js @@ -1,8 +1,8 @@ import { existsSync } from 'node:fs'; import { createServer } from 'node:http'; import { resolve } from 'node:path'; -import handler from 'serve-handler'; import { playwright } from '@vitest/browser-playwright'; +import handler from 'serve-handler'; import { defineConfig } from 'vitest/config'; import { vizzlyPlugin } from './src/index.js'; @@ -23,7 +23,7 @@ let server = createServer((req, res) => { }); // Listen on port 0 to get a random available port -await new Promise((resolve) => { +await new Promise(resolve => { server.listen(0, () => resolve()); }); @@ -47,8 +47,8 @@ export default defineConfig({ headless: true, }, include: ['tests/e2e/**/*.test.js'], - }, - define: { - __TEST_SITE_URL__: JSON.stringify(`http://localhost:${testSitePort}`), + env: { + TEST_SITE_URL: `http://localhost:${testSitePort}`, + }, }, }); diff --git a/package.json b/package.json index 3771d411..1be448d0 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "fix": "pnpm run format && pnpm run lint:fix" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.0.0" }, "publishConfig": { "access": "public", @@ -98,7 +98,7 @@ "@vizzly-testing/honeydiff": "^0.17.0", "ansis": "^4.2.0", "commander": "^15.0.0", - "cosmiconfig": "^9.0.0", + "cosmiconfig": "^10.0.1", "dotenv": "^17.2.1", "form-data": "^4.0.0", "glob": "^13.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6367333b..247f02b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,8 +28,8 @@ importers: specifier: ^15.0.0 version: 15.0.0 cosmiconfig: - specifier: ^9.0.0 - version: 9.0.2(typescript@7.0.2) + specifier: ^10.0.1 + version: 10.0.1 dotenv: specifier: ^17.2.1 version: 17.4.2 @@ -270,9 +270,6 @@ importers: '@vizzly-testing/cli': specifier: '>=0.24.0' version: 0.30.0(typescript@7.0.2) - cosmiconfig: - specifier: ^9.0.0 - version: 9.0.2(typescript@7.0.2) fast-xml-parser: specifier: ^5.8.0 version: 5.11.2 @@ -373,11 +370,11 @@ importers: specifier: ^2.5.10 version: 2.5.15 '@vitest/browser': - specifier: ^4.1.11 - version: 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11) + specifier: ^5.0.0 + version: 5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0) '@vitest/browser-playwright': - specifier: ^4.1.11 - version: 4.1.11(playwright@1.63.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11) + specifier: ^5.0.0 + version: 5.0.0(playwright@1.63.0)(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0) playwright: specifier: ^1.49.1 version: 1.63.0 @@ -385,8 +382,8 @@ importers: specifier: ^6.1.6 version: 6.1.7 vitest: - specifier: ^4.1.11 - version: 4.1.11(@types/node@26.6.4)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + specifier: ^5.0.0 + version: 5.0.0(@types/node@26.6.4)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) examples/custom-plugin: dependencies: @@ -1593,8 +1590,8 @@ packages: cpu: [x64] os: [win32] - '@blazediff/core@1.9.1': - resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@blazediff/core@1.10.0': + resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==} '@cacheable/memory@2.2.0': resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} @@ -2686,9 +2683,6 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@storybook/builder-vite@10.6.1': resolution: {integrity: sha512-kUMRRRS4MpJdxOLhrlQX3cpMYi2vVB3tIIRXyqWmAjVM6xT7OZ1SCQEMFPP+S3ToRqaYWZQb8aBqrJucbbYufA==} peerDependencies: @@ -3081,25 +3075,22 @@ packages: oxc-transform-react: optional: true - '@vitest/browser-playwright@4.1.11': - resolution: {integrity: sha512-riLBxPqwnJ0lWs2DN2WeUfYeKLoAjbP2Xx8cLQdSddzMi20sksIa6K2mPz79DyMZKKVKH2ksOC2yJvtNcZg8cg==} + '@vitest/browser-playwright@5.0.0': + resolution: {integrity: sha512-N+gED9y4/8pypaHjz/x0ah3CjoBr+N0hWWT+Gq4VXtMzyB+rUIdHni0ulzpD1j4H77iWy5Jg8PRMlVn6kjxo6g==} peerDependencies: playwright: '*' - vitest: 4.1.11 + vitest: 5.0.0 - '@vitest/browser@4.1.11': - resolution: {integrity: sha512-bwMovvAeuTFOK5kIFevw4VEf+1gVEICv4SYK4k3knJOxl6b1zEWud8mYKD73e1B0odAn174h1MofURy2TPWf3w==} + '@vitest/browser@5.0.0': + resolution: {integrity: sha512-JC9FG5xIRxPHXJPcdCaluIJcEoeM0IwGQ3xneuJk09LXKHRNs40BqWDWymQikbb20yOpYvzpKzmYgPRuSzKmvg==} peerDependencies: - vitest: 4.1.11 + vitest: 5.0.0 '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + '@vitest/mocker@5.0.0': + resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3112,26 +3103,25 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + '@vitest/pretty-format@5.0.0': + resolution: {integrity: sha512-PVRNuB3wpReb4SQEs4zTKM4KWFhQ5pw3spE8naoDJNB5T5aWRzGKHwXcLUllr0WeOTXpB6bSr3CJLo5+7XQSSQ==} '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + '@vitest/spy@5.0.0': + resolution: {integrity: sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==} + + '@vitest/ui@5.0.0': + resolution: {integrity: sha512-h2FIFwggCY2GxUd2UdQoYNVQkOIqEQLPhNREcl3FUiRsdzQep7NWwYbSmhGEA9nFLPDq5pXzRMcBZQU8Py83sg==} + peerDependencies: + vitest: 5.0.0 '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vitest/utils@5.0.0': + resolution: {integrity: sha512-dO++xL3vDfvhTAVimfkuQUA3k+JClIF1i1vAkPqpcGAthRmeWnXmHB7YPViPvgCwviX8u7Y5W1u2N//AaQr3fw==} '@vizzly-testing/bear-den@0.1.4': resolution: {integrity: sha512-sPRHB5U1HzxQqbRm5kjd1GER0SE1BiRZTGKPXQUHCtvladwNeY86NmLqhB9qTfXIJDXUYdRYetbHugeJVtsF4Q==} @@ -3184,7 +3174,7 @@ packages: '@vizzly-testing/ember@file:clients/ember': resolution: {directory: clients/ember, type: directory} - engines: {node: '>=22.0.0'} + engines: {node: ^22.18.0 || >=24.0.0} hasBin: true peerDependencies: '@vizzly-testing/cli': '>=0.12.0' @@ -3869,8 +3859,8 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} engines: {node: '>=18'} chalk@2.4.2: @@ -4243,6 +4233,10 @@ packages: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} + cosmiconfig@10.0.1: + resolution: {integrity: sha512-/zvGoYn0y27QTBmK+0B+oPdEPC4fKGUmJsMTQbDjqph00TrkqfVFW/M+C6ya54sYCgHC6QCwK/mI6djt8cEGHQ==} + engines: {node: ^22.18 || >= 24} + cosmiconfig@9.0.2: resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} engines: {node: '>=14'} @@ -4863,6 +4857,9 @@ packages: picomatch: optional: true + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + figures@2.0.0: resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} engines: {node: '>=4'} @@ -6298,10 +6295,6 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} - engines: {node: '>=12.20.0'} - obug@2.2.1: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} @@ -6501,10 +6494,6 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.5: - resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} - engines: {node: '>=12'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} @@ -7198,8 +7187,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -7490,11 +7479,12 @@ packages: tiny-lr@2.0.0: resolution: {integrity: sha512-f6nh0VMRvhGx4KCeK1lQ/jaL0Zdb5WdR+Jk8q9OSUQnaSDxAEGH1fgqLZ+cMl5EW3F2MGnCsalBO1IsnnogW1Q==} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} tinyglobby@0.2.16: @@ -7509,8 +7499,8 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} engines: {node: '>=14.0.0'} tinyspy@4.0.6: @@ -7794,8 +7784,8 @@ packages: yaml: optional: true - vite@8.3.2: - resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==} + vite@8.3.3: + resolution: {integrity: sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7837,23 +7827,23 @@ packages: yaml: optional: true - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.0: + resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.0 + '@vitest/browser-preview': 5.0.0 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.0 + '@vitest/coverage-v8': 5.0.0 + '@vitest/ui': 5.0.0 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -10158,7 +10148,7 @@ snapshots: '@biomejs/cli-win32-x64@2.5.15': optional: true - '@blazediff/core@1.9.1': {} + '@blazediff/core@1.10.0': {} '@cacheable/memory@2.2.0': dependencies: @@ -11322,8 +11312,6 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@standard-schema/spec@1.1.0': {} - '@storybook/builder-vite@10.6.1(storybook@10.6.1(prettier@3.9.9)(react@19.3.0))(vite@8.3.1(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': dependencies: storybook: 10.6.1(prettier@3.9.9)(react@19.3.0) @@ -11648,30 +11636,31 @@ snapshots: optionalDependencies: babel-plugin-react-compiler: 1.0.0 - '@vitest/browser-playwright@4.1.11(playwright@1.63.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11)': + '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0)': dependencies: - '@vitest/browser': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11) - '@vitest/mocker': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + '@vitest/browser': 5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0) + '@vitest/mocker': 5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) playwright: 1.63.0 - tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@26.6.4)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + tinyrainbow: 3.2.0 + vitest: 5.0.0(@types/node@26.6.4)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11)': + '@vitest/browser@5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0)': dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 + '@blazediff/core': 1.10.0 + '@vitest/mocker': 5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + '@vitest/ui': 5.0.0(vitest@5.0.0) + '@vitest/utils': 5.0.0 + magic-string: 1.4.3 pngjs: 7.0.0 sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@26.6.4)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) - ws: 8.21.3 + tinyrainbow: 3.2.0 + vitest: 5.0.0(@types/node@26.6.4)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + ws: 8.22.0 transitivePeerDependencies: - bufferutil - msw @@ -11686,48 +11675,38 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.11': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': + '@vitest/mocker@5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.0 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.3 optionalDependencies: - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) + vite: 8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.11': + '@vitest/pretty-format@5.0.0': dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 + tinyrainbow: 3.2.0 '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.6 - '@vitest/spy@4.1.11': {} + '@vitest/spy@5.0.0': {} + + '@vitest/ui@5.0.0(vitest@5.0.0)': + dependencies: + '@vitest/utils': 5.0.0 + fflate: 0.8.3 + flatted: 3.4.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyrainbow: 3.2.0 + vitest: 5.0.0(@types/node@26.6.4)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) '@vitest/utils@3.2.4': dependencies: @@ -11735,11 +11714,11 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.11': + '@vitest/utils@5.0.0': dependencies: - '@vitest/pretty-format': 4.1.11 + '@vitest/pretty-format': 5.0.0 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.2.0 '@vizzly-testing/bear-den@0.1.4(@heroicons/react@2.2.0(react@19.3.0))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(tailwindcss@4.3.3)': dependencies: @@ -12801,7 +12780,7 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 - chai@6.2.2: {} + chai@6.3.0: {} chalk@2.4.2: dependencies: @@ -13009,6 +12988,11 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + cosmiconfig@10.0.1: + dependencies: + env-paths: 2.2.1 + js-yaml: 5.4.3 + cosmiconfig@9.0.2(typescript@7.0.2): dependencies: env-paths: 2.2.1 @@ -14006,6 +13990,8 @@ snapshots: optionalDependencies: picomatch: 4.0.7 + fflate@0.8.3: {} + figures@2.0.0: dependencies: escape-string-regexp: 1.0.5 @@ -15469,8 +15455,6 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 - obug@2.1.3: {} - obug@2.2.1: {} odiff-bin@3.2.1: {} @@ -15679,8 +15663,6 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.5: {} - picomatch@4.0.7: {} pkce-challenge@5.0.1: {} @@ -16487,7 +16469,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.3.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -16909,9 +16891,9 @@ snapshots: transitivePeerDependencies: - supports-color - tinybench@2.9.0: {} + tinybench@6.1.4: {} - tinyexec@1.2.4: {} + tinyexec@1.3.0: {} tinyglobby@0.2.16: dependencies: @@ -16925,7 +16907,7 @@ snapshots: tinyrainbow@2.0.0: {} - tinyrainbow@3.1.0: {} + tinyrainbow@3.2.0: {} tinyspy@4.0.6: {} @@ -17211,7 +17193,7 @@ snapshots: terser: 5.51.2 yaml: 2.9.1 - vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1): + vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -17226,31 +17208,26 @@ snapshots: terser: 5.51.2 yaml: 2.9.1 - vitest@4.1.11(@types/node@26.6.4)(@vitest/browser-playwright@4.1.11)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)): + vitest@5.0.0(@types/node@26.6.4)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(jsdom@26.1.0(canvas@3.2.3))(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.0(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) + chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 - obug: 2.1.3 - pathe: 2.0.3 - picomatch: 4.0.5 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 + magic-string: 1.4.3 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) + vite: 8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.6.4 - '@vitest/browser-playwright': 4.1.11(playwright@1.63.0)(vite@8.3.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@4.1.11) + '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.3.3(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(vitest@5.0.0) + '@vitest/ui': 5.0.0(vitest@5.0.0) jsdom: 26.1.0(canvas@3.2.3) transitivePeerDependencies: - msw diff --git a/tests/utils/config-loader.test.js b/tests/utils/config-loader.test.js index c7de38fd..8ee87ccd 100644 --- a/tests/utils/config-loader.test.js +++ b/tests/utils/config-loader.test.js @@ -1,5 +1,11 @@ import assert from 'node:assert'; -import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { + existsSync, + mkdirSync, + mkdtempSync, + rmSync, + writeFileSync, +} from 'node:fs'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, it } from 'node:test'; import { @@ -116,6 +122,37 @@ describe('utils/config-loader', () => { assert.strictEqual(config.comparison.threshold, 1.5); }); + it('loads native TypeScript config with type-only helper imports', async () => { + let dir = mkdtempSync(join(testDir, 'typescript-')); + writeFileSync( + join(dir, 'types.ts'), + 'export interface Config { comparison: { threshold: number } }' + ); + writeFileSync( + join(dir, 'vizzly.config.ts'), + `import type { Config } from './types.ts'; +let threshold: number = 1.5; +export default { comparison: { threshold } } satisfies Config;` + ); + + let config = await loadConfig(join(dir, 'vizzly.config.ts')); + + assert.strictEqual(config.comparison.threshold, 1.5); + }); + + it('rejects TypeScript enums instead of silently using default config', async () => { + let dir = mkdtempSync(join(testDir, 'typescript-enum-')); + writeFileSync( + join(dir, 'vizzly.config.ts'), + `enum Threshold { Normal = 7 } +export default { comparison: { threshold: Threshold.Normal } };` + ); + + await assert.rejects(loadConfig(join(dir, 'vizzly.config.ts')), { + code: 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', + }); + }); + it('applies CLI overrides', async () => { let config = await loadConfig(null, { token: 'cli-token',