diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 326b3e0..61fa59e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,6 +110,44 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm build + visual-tests: + name: Visual Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v5 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install Playwright Chromium + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: pnpm exec playwright install --with-deps chromium + + - name: Install Playwright deps only + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: pnpm exec playwright install-deps chromium + + - name: Run visual regression tests + run: pnpm --filter @trails-cool/planner test:visual + + - name: Upload screenshots on failure + if: failure() + uses: actions/upload-artifact@v7 + with: + name: visual-snapshots-diff + path: apps/planner/app/**/__screenshots__/ + retention-days: 7 + e2e: name: E2E Tests needs: build diff --git a/.github/workflows/update-visual-snapshots.yml b/.github/workflows/update-visual-snapshots.yml index 0d45db9..fff88e0 100644 --- a/.github/workflows/update-visual-snapshots.yml +++ b/.github/workflows/update-visual-snapshots.yml @@ -62,19 +62,19 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.ref || github.event.inputs.branch || github.ref }} # Use a token with push rights so the commit-back step can push token: ${{ secrets.GITHUB_TOKEN }} - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - node-version-file: ".nvmrc" + node-version: 24 cache: "pnpm" - name: Install dependencies @@ -96,7 +96,7 @@ jobs: - name: Upload snapshots as artifact if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: visual-snapshots path: apps/planner/app/**/__screenshots__/ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-darwin.png new file mode 100644 index 0000000..591786a Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-linux.png new file mode 100644 index 0000000..d95c7d2 Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/drag-select-chromium-linux.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-darwin.png new file mode 100644 index 0000000..8666b52 Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-linux.png new file mode 100644 index 0000000..caa43ae Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/elevation-chromium-linux.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-darwin.png new file mode 100644 index 0000000..3f91e8f Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-linux.png new file mode 100644 index 0000000..ac9948b Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/grade-chromium-linux.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-darwin.png new file mode 100644 index 0000000..747dfba Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-linux.png new file mode 100644 index 0000000..1a60c8c Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/hover-chromium-linux.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-darwin.png new file mode 100644 index 0000000..2f8227e Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-linux.png new file mode 100644 index 0000000..4b3af8a Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/plain-chromium-linux.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-darwin.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-darwin.png new file mode 100644 index 0000000..ef2851e Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-darwin.png differ diff --git a/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-linux.png b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-linux.png new file mode 100644 index 0000000..248a9ed Binary files /dev/null and b/apps/planner/app/lib/__screenshots__/elevation-chart-draw.browser.test.tsx/surface-chromium-linux.png differ diff --git a/apps/planner/app/lib/elevation-chart-draw.browser.test.tsx b/apps/planner/app/lib/elevation-chart-draw.browser.test.tsx index 04735b6..1327f41 100644 --- a/apps/planner/app/lib/elevation-chart-draw.browser.test.tsx +++ b/apps/planner/app/lib/elevation-chart-draw.browser.test.tsx @@ -1,6 +1,6 @@ -import { describe, it, expect } from "vitest"; -import { render } from "@testing-library/react"; -import { page } from "@vitest/browser/context"; +import { describe, it, expect, afterEach } from "vitest"; +import { render, cleanup } from "@testing-library/react"; +import { page } from "vitest/browser"; import { drawElevationChart } from "./elevation-chart-draw"; import type { DrawChartParams } from "./elevation-chart-draw"; @@ -46,6 +46,7 @@ function ChartFixture({ params }: { params: DrawChartParams }) { } describe("drawElevationChart visual regression", () => { + afterEach(() => cleanup()); it("renders plain color mode", async () => { render(); await expect(page.getByTestId("chart")).toMatchScreenshot("plain.png"); diff --git a/apps/planner/package.json b/apps/planner/package.json index a2093e2..a92baad 100644 --- a/apps/planner/package.json +++ b/apps/planner/package.json @@ -11,7 +11,7 @@ "lint": "eslint .", "test": "vitest run", "test:visual": "vitest run --config vitest.browser.config.ts", - "test:visual:update": "vitest run --config vitest.browser.config.ts --update-snapshots" + "test:visual:update": "vitest run --config vitest.browser.config.ts -u" }, "dependencies": { "@codemirror/commands": "^6.10.3", @@ -55,6 +55,7 @@ "@types/react-dom": "catalog:", "@types/ws": "^8.18.1", "@vitest/browser": "^4.1.5", + "@vitest/browser-playwright": "^4.1.5", "leaflet.markercluster": "^1.5.3", "pino-pretty": "^13.1.3", "tailwindcss": "catalog:", diff --git a/apps/planner/vitest.browser.config.ts b/apps/planner/vitest.browser.config.ts index 06b8978..4a2d83a 100644 --- a/apps/planner/vitest.browser.config.ts +++ b/apps/planner/vitest.browser.config.ts @@ -1,5 +1,6 @@ import { defineConfig } from "vitest/config"; import { resolve } from "node:path"; +import { playwright } from "@vitest/browser-playwright"; // Separate config for Vitest browser visual regression tests. // Run with: pnpm --filter @trails-cool/planner test:visual @@ -10,9 +11,6 @@ import { resolve } from "node:path"; // (.github/workflows/update-visual-snapshots.yml), triggered manually or // by adding the `update-snapshots` label to a PR. export default defineConfig({ - esbuild: { - jsx: "automatic", - }, resolve: { alias: { "~": resolve(import.meta.dirname, "app"), @@ -23,10 +21,7 @@ export default defineConfig({ include: ["app/**/*.browser.test.{ts,tsx}"], browser: { enabled: true, - // Provider is resolved from whichever @vitest/browser peer is installed. - // playwright is the default when @playwright/test is available. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - provider: "playwright" as any, + provider: playwright(), instances: [{ browser: "chromium" }], }, }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3059ad..f46ec23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,7 +196,7 @@ importers: version: 8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4) vitest: specifier: ^4.1.5 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/browser-playwright@4.1.5)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) apps/journal: dependencies: @@ -568,7 +568,10 @@ importers: version: 8.18.1 '@vitest/browser': specifier: ^4.1.5 - version: 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))) + version: 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) + '@vitest/browser-playwright': + specifier: ^4.1.5 + version: 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) leaflet.markercluster: specifier: ^1.5.3 version: 1.5.3(leaflet@1.9.4) @@ -3936,6 +3939,12 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@vitest/browser-playwright@4.1.5': + resolution: {integrity: sha512-CWy0lBQJq97nionyJJdnaU4961IXTl43a7UCu5nHy51IoKxAt6PVIJLo+76rVl7KOOgcWHNkG4kbJu/pW7knvA==} + peerDependencies: + playwright: '*' + vitest: 4.1.5 + '@vitest/browser@4.1.5': resolution: {integrity: sha512-iCDGI8c4yg+xmjUg2VsygdAUSIIB4x5Rht/P68OXy1hPELKXHDkzh87lkuTcdYmemRChDkEpB426MmDjzC0ziA==} peerDependencies: @@ -11741,7 +11750,20 @@ snapshots: dependencies: vite: 8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4) - '@vitest/browser@4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)))': + '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5)': + dependencies: + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) + playwright: 1.59.1 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/browser-playwright@4.1.5)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5)': dependencies: '@blazediff/core': 1.9.1 '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) @@ -11750,7 +11772,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/browser-playwright@4.1.5)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) ws: 8.20.0 transitivePeerDependencies: - bufferutil @@ -16125,7 +16147,7 @@ snapshots: tsx: 4.21.0 yaml: 2.8.4 - vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)): + vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/browser-playwright@4.1.5)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)): dependencies: '@vitest/expect': 4.1.5 '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4)) @@ -16150,6 +16172,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.6.2 + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@25.6.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.5) jsdom: 29.1.1(canvas@3.2.3) transitivePeerDependencies: - msw