Skip to content
Draft
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
14 changes: 14 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Quality checklist

- [ ] `npm run build`
- [ ] `npm run test`
- [ ] `npm run test:e2e`
- [ ] `npm run test:visual`

## Visual baseline changes

- [ ] No visual baseline changed, or the changed PNGs were generated with `npm run test:visual:update`.
- [ ] Every changed baseline and generated diff was visually inspected.
- [ ] `npm run test:visual` was run again without update mode and `visual-report/report.json` was reviewed.

Never accept baseline changes solely to make a failing test green; explain the intentional UI change below.
82 changes: 82 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: quality-gate

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run validate:data
- run: npm run quality:data:check
- run: npm run build
- run: npm run test
- uses: actions/upload-artifact@v4
with:
name: production-build
path: dist
if-no-files-found: error

browser-and-visual:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run test:e2e
- run: npm run test:visual
- run: npm run test:reconstructions
- run: npm run test:reconstructions:check-evidence
- uses: actions/upload-artifact@v4
if: always()
with:
name: visual-quality-report
path: |
visual-report/report.json
test-results
playwright-report
if-no-files-found: warn

deploy-pages:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [build-and-test, browser-and-visual]
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
node_modules/
dist/
test-results/
playwright-report/
visual-report/
source/
.workbuddy/
.DS_Store
Expand All @@ -8,4 +12,3 @@ source/
repro/
*-proof.png
redesign-*.png

45 changes: 45 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Architecture

## Runtime

`index.html` boots `src/main.tsx`, which renders the React application in `src/App.tsx`. The UI is fully data-driven: `preview-data.json` remains the single source for all 232 gallery entries, while the 232 preview images remain local under `previews/`.

The React layer owns filtering, selection persistence, clipboard actions, keyboard interaction, responsive rendering, and the back-to-top control. It does not duplicate or regenerate the gallery dataset.

## Data boundary

`src/gallery-schema.ts` defines a discriminated Zod schema for component-library and award-project entries. Both runtime import and `npm run validate:data` use this schema. The CLI validation adds filesystem checks for every referenced preview and returns a machine-readable JSON summary.

Invalid records, duplicate IDs, missing images, an unexpected item count, or an animation marked successful without a successful reproduction fail the quality gate before Vite builds.

Round 2 adds a target-count-independent admission rubric in `docs/quality-rubric.json`. It evaluates asset decoding and dimensions, source or license evidence, ID/link/image uniqueness, metadata completeness, and target usability. `docs/generated/data-quality-report.json` records every check and reason for all 232 entries. CI regenerates the report in memory and rejects stale committed evidence.

## Reproducible visual gate

Playwright runs the functional suite in two Chromium device profiles: desktop at 1280×820 and a Pixel 7 mobile profile. CI installs Playwright's pinned Chromium binary; local macOS runs may use installed Chrome when a CDN download is unavailable.

The visual suite uses Chromium at device scale factor 1 for desktop and mobile screenshots. Reproducibility controls are:

- Manrope and Noto Sans SC fonts are bundled with the app;
- locale is `zh-CN`, timezone is `Asia/Shanghai`, and color scheme is dark;
- viewport and device scale factor are explicit;
- `?visual=1` disables animation, transition, smooth scrolling, and caret rendering;
- screenshots wait for `document.fonts.ready` and the first gallery card;
- no network-hosted runtime assets are used.

`tests/visual/compare.ts` computes both Pixelmatch changed-pixel ratio and SSIM, writes PNG diff images, and feeds `visual-report/report.json`. The gates are:

- Pixelmatch changed-pixel ratio ≤ 0.5%;
- SSIM ≥ 0.99.

Only `npm run test:visual:update` can create or replace baselines. A normal failing run never writes baselines. Baseline changes must be inspected in `tests/visual/baselines/`, followed by `npm run test:visual` and `npm run test:visual:review`, before they are committed.

Browser engines rasterize fonts differently, so pixel metrics are intentionally measured on one pinned engine. Device-profile coverage tests responsive behavior without conflating engine rasterization with application regressions.

## Independent reconstruction evidence

Three standalone reference pages under `public/reference-cases/` are compared with separately authored React candidates under `src/reconstructions/`. The candidate cannot import reference code or assets. Static scans reject images, canvas pixel copying, base64/data URLs, reference imports, CSS URL images, and test overlays. Runtime checks require zero images, canvases, or URL backgrounds.

The comparator calculates screenshot dimensions, page overflow, dominant-color delta, DOM tag-vector similarity, full-image Pixelmatch, Sobel-edge Pixelmatch, and SSIM. It writes reference/candidate/diff/edge-diff PNGs and JSON. The capability gates are SSIM ≥ 0.99, changed pixels ≤ 0.5%, edge differences ≤ 1%, structure similarity ≥ 0.98, color delta ≤ 5, matched dimensions, no overflow, and clean anti-cheat results.

Normal CI writes transient reconstruction evidence under `test-results/`. Only `npm run test:reconstructions:update-evidence` refreshes reviewed evidence under `docs/generated/reconstructions/`.
Loading
Loading