Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
82 changes: 82 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Node.js dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build outputs
dist
build
.next
out

# Test artifacts
test-results
playwright-report
coverage
*.lcov

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# IDE files
.vscode
.idea
*.swp
*.swo
*~

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Docker files - prevent these from being included in the image context
Dockerfile*
docker-compose*
.dockerignore

# CI/CD
.github
.husky

# Temporary files
tmp
temp
*.tmp
*.temp

# Log files
logs
*.log

# Cache directories
.cache
.parcel-cache
.eslintcache

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity
30 changes: 14 additions & 16 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ name: Node.js CI

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]

branches: ["main"]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"
- run: npm ci
- run: npm run qa
- run: npm run build --if-present
- run: npm test
- uses: actions/checkout@v4
with:
submodules: "true"
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"
- run: npm ci
- run: npm run qa
- run: npm run build --if-present
- run: npm test
81 changes: 81 additions & 0 deletions .github/workflows/visual-regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Visual Regression Tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:

jobs:
visual-tests:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- uses: actions/checkout@v4
with:
submodules: "true"

- name: Build Docker image
run: docker build -f Dockerfile.snapshot-tests -t playwright-test .

- name: Run visual regression tests
run: |
docker run --rm \
-v ${{ github.workspace }}/tools/snapshot-tests/test-results:/app/tools/snapshot-tests/test-results \
-v ${{ github.workspace }}/tools/snapshot-tests/playwright-report:/app/tools/snapshot-tests/playwright-report \
-e CI=true \
playwright-test \
sh -c "cd tools/snapshot-tests && npm run test"

- name: Upload Playwright Report
uses: actions/upload-artifact@v4
if: ${{ always() && hashFiles('tools/snapshot-tests/playwright-report/**') != '' }}
with:
name: playwright-report-${{ github.run_number }}
path: tools/snapshot-tests/playwright-report/
retention-days: 30

- name: Comment PR with Visual Test Report
uses: actions/github-script@v7
if: ${{ always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }}
with:
script: |
const testStatus = '${{ job.status }}' === 'success' ? '✅ Passed' : '❌ Failed';

const body = `## Visual Regression Test Report ${testStatus}

Github run id: ${{github.run_id}}

🔗 **Artifacts**: [Download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
`
Comment on lines +49 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Dedent the PR comment body to avoid code-block rendering

The template literal inherits the 12-space YAML indentation, so every line after the header is treated as a Markdown code block. The report link ends up inside that block and is no longer clickable, breaking the workflow’s UX. Build the string without leading spaces (e.g., join an array of lines) so the comment renders normally.

-            const body = `## Visual Regression Test Report ${testStatus}
-
-            Github run id: ${{github.run_id}}
-
-            🔗 **Artifacts**: [Download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
-            `
+            const body = [
+              `## Visual Regression Test Report ${testStatus}`,
+              '',
+              `GitHub run id: ${{ github.run_id }}`,
+              '',
+              `🔗 **Artifacts**: [Download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`
+            ].join('\n');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const body = `## Visual Regression Test Report ${testStatus}
Github run id: ${{github.run_id}}
🔗 **Artifacts**: [Download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
`
const body = [
`## Visual Regression Test Report ${testStatus}`,
'',
`GitHub run id: ${{ github.run_id }}`,
'',
`🔗 **Artifacts**: [Download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`
].join('\n');
🤖 Prompt for AI Agents
.github/workflows/visual-regression.yml around lines 49 to 54: the template
literal for the PR comment body is indented in YAML so each line gains leading
spaces and renders as a Markdown code block; rebuild the body without leading
spaces (for example, construct it by joining an array of lines or using
unindented concatenation) so the header and subsequent lines are not prefixed by
spaces and the artifact link renders as a normal clickable link.


const comments = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});

const botComment = comments.data.find(comment =>
comment.user.login === 'github-actions[bot]' &&
comment.body.includes('Visual Regression Test Report')
);

if (botComment) {
await github.rest.issues.updateComment({
comment_id: botComment.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
} else {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
}
100 changes: 100 additions & 0 deletions Dockerfile.snapshot-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
FROM mcr.microsoft.com/playwright:v1.55.1-noble AS base

RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

# ============================================
# Stage 1: Main Dependencies
# This stage only rebuilds when main package.json files change
# ============================================
FROM base AS main-dependencies

COPY package*.json ./
COPY shared/links-metadata/package*.json ./shared/links-metadata/

RUN npm ci

# ============================================
# Stage 2: Test Dependencies
# This stage only rebuilds when test package.json files change
# ============================================
FROM base AS test-dependencies

COPY tools/snapshot-tests/package*.json ./tools/snapshot-tests/

RUN cd tools/snapshot-tests && npm install

RUN cd tools/snapshot-tests && npx playwright install chromium

# ============================================
# Stage 3: Source and Build
# This stage handles source code and building
# ============================================
FROM main-dependencies AS builder

COPY .git ./.git
COPY .gitmodules ./

COPY scripts ./scripts/

COPY shared/links-metadata ./shared/links-metadata/

RUN cd shared/links-metadata

COPY tsconfig*.json ./
COPY vite.config.ts ./
COPY vitest.config.ts ./
COPY tailwind.config.js ./
COPY biome.jsonc ./
COPY index.html ./

COPY public ./public/
COPY src ./src/

RUN npm run build

# ============================================
# Stage 4: Test Runner
# Final stage for running tests
# ============================================
FROM test-dependencies AS test-runner

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=test-dependencies /app/tools/snapshot-tests/node_modules ./tools/snapshot-tests/
COPY --from=test-dependencies /app/tools/snapshot-tests/package*.json ./tools/snapshot-tests/
COPY tools/snapshot-tests/tests ./tools/snapshot-tests/tests
COPY tools/snapshot-tests/playwright.config.ts ./tools/snapshot-tests/

ENV PLAYWRIGHT_START_SERVER=true
ENV PLAYWRIGHT_PORT=5173
ENV PLAYWRIGHT_HOST=localhost
ENV CI=true

EXPOSE 5173

CMD ["/bin/sh", "-lc", "cd tools/snapshot-tests && npm run test:with-server"]

# ============================================
# Stage 5: Development (Optional)
# Use this stage for interactive development/debugging
# Build with: docker build --target development -f Dockerfile.playwright -t playwright-dev .
# Run with: docker run -it playwright-dev bash
# ============================================
FROM test-runner AS development

# Install additional dev tools if needed
RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
less \
&& rm -rf /var/lib/apt/lists/*

CMD ["/bin/bash"]
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,41 @@ $ npm ci # install dependencies
$ npm run dev # run the development version
```

# Running e2e (snapshots) tests locally

Visual snapshot tests checks for visual regression.

## Docker-based Testing (Recommended)

For consistent snapshots that match GitHub Actions:

```bash
# Build Docker images
npm run docker:build

# Run tests
npm run docker:test
# Then open tools/snapshot-test/playwright-report/index.html for visual regression report

# Update snapshots
npm run docker:test:update

```

## Local Testing

To run all visual snapshots tests locally:

```bash
cd tools/snapshot-tests
npm install
npm run test
```

One can also run tests with UI simply via:

```bash
npm start
```

**Note**: Local testing may produce different snapshots than CI due to environment differences. Use Docker-based testing for consistent results.
40 changes: 40 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
services:
snapshot-tests:
build:
context: .
dockerfile: Dockerfile.snapshot-tests
volumes:
- ./tools/snapshot-tests/test-results:/app/tools/snapshot-tests/test-results
- ./tools/snapshot-tests/playwright-report:/app/tools/snapshot-tests/playwright-report
environment:
- PLAYWRIGHT_START_SERVER=true
- PLAYWRIGHT_PORT=5173
- PLAYWRIGHT_HOST=localhost
- CI=true
- DISPLAY=:99
working_dir: /app/tools/snapshot-tests
command: ["npm", "run", "test"]
networks:
- playwright-network

snapshot-tests-update:
build:
context: .
dockerfile: Dockerfile.snapshot-tests
volumes:
- ./tools/snapshot-tests/test-results:/app/tools/snapshot-tests/test-results
- ./tools/snapshot-tests/playwright-report:/app/tools/snapshot-tests/playwright-report
environment:
- PLAYWRIGHT_START_SERVER=true
- PLAYWRIGHT_PORT=5173
- PLAYWRIGHT_HOST=localhost
- CI=true
- DISPLAY=:99
working_dir: /app/tools/snapshot-tests
command: "npm run test:update"
networks:
- playwright-network

networks:
playwright-network:
driver: bridge
Loading