Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 0 additions & 187 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,190 +212,3 @@ jobs:

- name: Build
run: pnpm run build

visual-regression:
name: Visual Regression
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout Repo
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'

- name: Setup pnpm
uses: pnpm/action-setup@v5

- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Cache Playwright Browsers
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
playwright-${{ runner.os }}-

- name: Install Playwright Chromium
working-directory: apps/storybook
run: npx playwright install chromium --with-deps

- name: Build Storybook
run: pnpm turbo run build --filter=@cocso-ui/storybook

- name: Run Visual Regression Tests
id: vr-test
continue-on-error: true
working-directory: apps/storybook
run: |
npx serve storybook-static -p 6006 &
npx wait-on http://localhost:6006 --timeout 60000
SNAPSHOT_COUNT=$(find __snapshots__ -maxdepth 1 -name "*.png" 2>/dev/null | wc -l)
if [ "$SNAPSHOT_COUNT" -eq 0 ]; then
echo "::warning::No baseline snapshots found. Run the 'Update Visual Regression Baselines' workflow to generate them."
echo "has_baselines=false" >> $GITHUB_OUTPUT
else
echo "has_baselines=true" >> $GITHUB_OUTPUT
pnpm test:visual -- --ci
fi

- name: Push Screenshots to Branch
id: push-screenshots
if: github.event_name == 'pull_request' && steps.vr-test.outputs.has_baselines == 'true'
continue-on-error: true
env:
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.number }}
run: |
BRANCH="vr-screenshots-${PR_NUMBER}"
SNAPSHOTS="apps/storybook/__snapshots__"

STAGING=$(mktemp -d)
mkdir -p "$STAGING/screenshots"

# before: committed baseline snapshots
for f in "${SNAPSHOTS}"/*.png; do
[ -f "$f" ] || continue
id=$(basename "$f" .png)
cp "$f" "$STAGING/screenshots/before-${id}.png"
done

# after: freshly captured screenshots from this run
if [ -d "${SNAPSHOTS}/__current__" ]; then
for f in "${SNAPSHOTS}/__current__"/*.png; do
[ -f "$f" ] || continue
id=$(basename "$f" .png)
cp "$f" "$STAGING/screenshots/after-${id}.png"
done
fi

# diff: generated by jest-image-snapshot on mismatch
if [ -d "${SNAPSHOTS}/__diff_output__" ]; then
for f in "${SNAPSHOTS}/__diff_output__"/*.png; do
[ -f "$f" ] || continue
name=$(basename "$f" -diff.png)
cp "$f" "$STAGING/screenshots/diff-${name}.png"
done
fi

# Prevent Vercel from deploying this screenshots-only branch
echo '{"ignoreCommand":"exit 0"}' > "$STAGING/vercel.json"

cd "$STAGING"
git init
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git remote add origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
git add .
git commit -m "vr: screenshots for PR #${PR_NUMBER}"
git push --force origin "HEAD:refs/heads/${BRANCH}"

- name: Generate Visual Regression Report
if: github.event_name == 'pull_request' && steps.vr-test.outputs.has_baselines == 'true' && steps.push-screenshots.outcome == 'success'
env:
PR_NUMBER: ${{ github.event.number }}
run: |
node << 'EOF'
const fs = require('fs');
const path = require('path');

const PR_NUMBER = process.env.PR_NUMBER;
const REPO = process.env.GITHUB_REPOSITORY;
const BRANCH = `vr-screenshots-${PR_NUMBER}`;
const BASE_URL = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/screenshots`;

const SNAPSHOTS_DIR = 'apps/storybook/__snapshots__';
const DIFF_DIR = path.join(SNAPSHOTS_DIR, '__diff_output__');

const allSnapshots = fs.readdirSync(SNAPSHOTS_DIR)
.filter(f => f.endsWith('.png') && !f.startsWith('.'))
.sort()
.map(f => f.replace('.png', ''));

let diffed = [];
try {
diffed = fs.readdirSync(DIFF_DIR)
.filter(f => f.endsWith('-diff.png'))
.map(f => f.replace('-diff.png', ''));
} catch {}

const changed = allSnapshots.filter(id => diffed.includes(id));
const unchanged = allSnapshots.filter(id => !diffed.includes(id));

function formatName(id) {
return id
.replace(/^components-/, '')
.replace(/--/g, ' / ')
.replace(/-/g, ' ')
.replace(/\b\w/g, c => c.toUpperCase());
}

const lines = ['<!-- cocso-ui-visual-regression -->', '## Visual Regression Report', ''];

if (changed.length === 0) {
lines.push(`✅ **All ${allSnapshots.length} snapshot(s) matched — no visual regressions.**`);
} else {
lines.push(`⚠️ **${changed.length} of ${allSnapshots.length} screenshot(s) changed:**`, '');
for (const id of changed) {
lines.push(`### ${formatName(id)}`, '');
lines.push('| Before | After | Diff |');
lines.push('|--------|-------|------|');
lines.push(`| ![Before](${BASE_URL}/before-${id}.png) | ![After](${BASE_URL}/after-${id}.png) | ![Diff](${BASE_URL}/diff-${id}.png) |`);
lines.push('');
}
}

if (unchanged.length > 0) {
const label = changed.length === 0
? `${unchanged.length} screenshot(s) — all matching baseline`
: `${unchanged.length} screenshot(s) unchanged`;
lines.push('', `<details><summary>${label}</summary>`, '');
for (const id of unchanged) {
lines.push(`- ${formatName(id)}`);
}
lines.push('', '</details>');
}

lines.push('', '---', '*Generated by cocso-ui Visual Regression*');

fs.writeFileSync('vr-report.md', lines.join('\n'));
EOF

- name: Post Visual Regression Comment
if: github.event_name == 'pull_request' && steps.vr-test.outputs.has_baselines == 'true' && steps.push-screenshots.outcome == 'success'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: visual-regression
path: vr-report.md

- name: Fail if Visual Regressions Detected
if: steps.vr-test.outcome == 'failure'
run: |
echo "::error::Visual regressions detected. See the PR comment for before/after/diff images."
exit 1
Loading
Loading