diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 000000000..f925e615d --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,36 @@ +name: Chromatic + +on: + push: + branches: + - main + pull_request: + branches: ["**"] + +jobs: + chromatic: + name: Visual Regression Testing + runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} + # Skip for forked PRs since they don't have access to secrets + if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'push' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for Chromatic to track changes + + - uses: ./.github/actions/setup-cmux + + - name: Generate version file + run: ./scripts/generate-version.sh + + - name: Build Storybook + run: bun x storybook build --stats-json + + - name: Run Chromatic + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + exitZeroOnChanges: true + onlyChanged: true + diff --git a/Makefile b/Makefile index 3a217d5ba..3fbb97d42 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ include fmt.mk .PHONY: test test-unit test-integration test-watch test-coverage test-e2e .PHONY: dist dist-mac dist-win dist-linux .PHONY: docs docs-build docs-watch -.PHONY: storybook storybook-build test-storybook +.PHONY: storybook storybook-build test-storybook chromatic .PHONY: benchmark-terminal .PHONY: ensure-deps .PHONY: check-eager-imports check-bundle-size check-startup @@ -252,6 +252,9 @@ storybook-build: node_modules/.installed src/version.ts ## Build static Storyboo test-storybook: node_modules/.installed ## Run Storybook interaction tests (requires Storybook to be running or built) @bun x test-storybook +chromatic: node_modules/.installed ## Run Chromatic for visual regression testing + @bun x chromatic --exit-zero-on-changes + ## Benchmarks benchmark-terminal: ## Run Terminal-Bench with the cmux agent (use TB_DATASET/TB_SAMPLE_SIZE/TB_ARGS to customize) @TB_DATASET=$${TB_DATASET:-terminal-bench-core==0.1.1}; \ diff --git a/bun.lock b/bun.lock index b3f43700e..ca8a63105 100644 --- a/bun.lock +++ b/bun.lock @@ -58,6 +58,7 @@ "@typescript/native-preview": "^7.0.0-dev.20251014.1", "@vitejs/plugin-react": "^4.0.0", "babel-plugin-react-compiler": "^1.0.0", + "chromatic": "^13.3.1", "cmdk": "^1.0.0", "concurrently": "^8.2.0", "dotenv": "^17.2.3", @@ -1074,6 +1075,8 @@ "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + "chromatic": ["chromatic@13.3.1", "", { "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0" }, "optionalPeers": ["@chromatic-com/cypress", "@chromatic-com/playwright"], "bin": { "chroma": "dist/bin.js", "chromatic": "dist/bin.js", "chromatic-cli": "dist/bin.js" } }, "sha512-qJ/el70Wo7jFgiXPpuukqxCEc7IKiH/e8MjTzIF9uKw+3XZ6GghOTTLC7lGfeZtosiQBMkRlYet77tC4KKHUng=="], + "chromium-pickle-js": ["chromium-pickle-js@0.2.0", "", {}, "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw=="], "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], diff --git a/chromatic.config.json b/chromatic.config.json new file mode 100644 index 000000000..89522bddf --- /dev/null +++ b/chromatic.config.json @@ -0,0 +1,9 @@ +{ + "projectId": "68e30fca49979473fc9abc73", + "buildScriptName": "storybook:build", + "storybookBuildDir": "storybook-static", + "exitZeroOnChanges": true, + "exitOnceUploaded": true, + "autoAcceptChanges": false +} + diff --git a/package.json b/package.json index cadb9bba3..54bb6a86c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "docs:watch": "make docs-watch", "storybook": "make storybook", "storybook:build": "make storybook-build", - "test:storybook": "make test-storybook" + "test:storybook": "make test-storybook", + "chromatic": "make chromatic" }, "dependencies": { "@ai-sdk/anthropic": "^2.0.29", @@ -98,6 +99,7 @@ "@typescript/native-preview": "^7.0.0-dev.20251014.1", "@vitejs/plugin-react": "^4.0.0", "babel-plugin-react-compiler": "^1.0.0", + "chromatic": "^13.3.1", "cmdk": "^1.0.0", "concurrently": "^8.2.0", "dotenv": "^17.2.3", @@ -105,10 +107,10 @@ "electron-builder": "^24.6.0", "electron-devtools-installer": "^4.0.0", "electron-mock-ipc": "^0.3.12", + "escape-html": "^1.0.3", "eslint": "^9.36.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "escape-html": "^1.0.3", "jest": "^30.1.3", "mermaid": "^11.12.0", "playwright": "^1.56.0",