Skip to content

feat(regression-test): render the suite with the new dark theme#2628

Open
balzss wants to merge 11 commits into
masterfrom
regression-test-dark-theme
Open

feat(regression-test): render the suite with the new dark theme#2628
balzss wants to merge 11 commits into
masterfrom
regression-test-dark-theme

Conversation

@balzss

@balzss balzss commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Render the visual regression suite across multiple themes. The regression-test app selects an InstUI theme from the ?theme= query param (defaulting to canvas) and exposes the applied theme via data-theme; the Cypress spec drives one screenshot per page per theme (canvas, light, dark), so baselines are named <slug>-<theme>.png.
  • Paint the page background from the active theme's own background.page semantic token, so each theme is captured on the surface it's actually used on — the dark theme renders on its near-black surface rather than on white.
  • Decouple the a11y check from screenshot capture so an a11y failure can't leave a page without a visual baseline, and accumulate a11y/console-error results across themes so a failure in one theme doesn't abort the others.
  • Enhance the ui-scripts visual-diff report with changed-region highlighting and a theme facet filter.
  • Add a live HTML view to the report lightbox that iframes the rendered page (a static export of the app is published next to the report), and clean up per-PR reports from gh-pages when the PR closes.

Jira

  • INSTUI-5089 — Improve visual regression tests
  • INSTUI-5124 — Add a live HTML rendered-page view to the visual-diff report
  • INSTUI-5123 — Remove stale visual-regression reports from gh-pages when their PR closes

Test Plan

  • Once the visual-regression CI runs, review the published report — each component page should appear once per theme, and the theme facet chips should filter the rows by theme.
  • In the report lightbox, the HTML button should load the live rendered page (check a dark-theme page in particular).
  • When this PR closes, the visual-regression/pr-2628/ directory should be removed from gh-pages.

🤖 Generated with Claude Code

Apply the new theming system's `dark` theme globally via the
InstUISettingsProvider `theme` prop so the visual regression
screenshots exercise the new theme instead of the default canvas theme.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@balzss balzss self-assigned this Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://instructure.design/pr-preview/pr-2628/

Built to branch gh-pages at 2026-07-23 08:56 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Visual regression report

⚠️ Changes detected.

Status Count
Unchanged 0
Changed 1
New 96
Removed 32

📊 View full report

Diff images (33)

alert.png — baseline no longer produced

avatar.png — baseline no longer produced

badge.png — baseline no longer produced

billboard.png — baseline no longer produced

breadcrumb.png — baseline no longer produced

button-and-derivatives.png — baseline no longer produced

byline.png — baseline no longer produced

calendar.png — baseline no longer produced

checkbox.png — baseline no longer produced

checkboxgroup.png — baseline no longer produced

colorpicker.png — baseline no longer produced

contextview.png — baseline no longer produced

custom-and-lucide-icons.png — baseline no longer produced

dateinput-dateinput2.png — baseline no longer produced

datetimeinput.png — baseline no longer produced

diff-demo.png — 6324 pixels differ

drilldown.png — baseline no longer produced

filedrop.png — baseline no longer produced

form-errors.png — baseline no longer produced

heading.png — baseline no longer produced

img.png — baseline no longer produced

link.png — baseline no longer produced

menu.png — baseline no longer produced

metric-pill-tag-timeselect-text.png — baseline no longer produced

options.png — baseline no longer produced

pagination.png — baseline no longer produced

progressbar.png — baseline no longer produced

select-simpleselect.png — baseline no longer produced

table.png — baseline no longer produced

tabs.png — baseline no longer produced

tooltip.png — baseline no longer produced

treebrowser.png — baseline no longer produced

view.png — baseline no longer produced

Baselines come from the visual-baselines branch. They refresh on every merge to master.

An axe violation throws in `cy.checkA11y`, which failed the Cypress test and
triggered two problems in the visual regression pipeline: the deterministic
afterEach screenshot was skipped (the page got a blank/missing baseline), and
Cypress' automatic failure screenshot (the error overlay) leaked into the diff
as a spurious "new" image because it has a different filename.

Always take the afterEach screenshot even when the test failed, and disable
Cypress' failure screenshots. The a11y assertion still fails the run as a gate,
but the visual baselines are now clean and complete regardless of a11y state.

This bug was latent from the start; it only surfaced once a change introduced an
actual a11y violation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 8, 2026
…dark themes

Render each component page under multiple themes and screenshot each one, so the
visual regression suite compares canvas, light, and dark side by side.

- layout.tsx reads the `?theme=` query param and applies the matching theme via
  InstUISettingsProvider. It renders `canvas` for the first paint and switches in
  an effect to avoid a hydration mismatch, exposing `data-theme` on <html> so the
  spec can wait for the theme to be applied before screenshotting.
- spec.cy.ts is now data-driven: a PAGES list (slug/title/wait/a11y-skip) times a
  THEMES list. Each page is screenshotted as `<slug>-<theme>` before the axe check
  so an a11y failure can never drop a visual baseline. Axe runs per theme with
  skipFailures and the totals are asserted once at the end, keeping a11y a gate
  across all themes without aborting the other themes' screenshots.
- Screenshot capture moved out of the support afterEach into the spec.

Screenshot filenames change from `<title>` to `<slug>-<theme>`, so the first run
re-baselines every image.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 8, 2026
…r to the visual-diff report

Make the report easier to read for the multi-theme regression suite:

- The Diff image now renders the actual screenshot with a bright outline (and
  faint fill) around each changed region, grouped from the changed-pixel mask via
  a coarse-grid connected-components pass. This replaces pixelmatch's faded
  ghost + scattered red pixels, which was hard to interpret — especially for
  large or anti-aliased changes. Diff PNGs are now generated only for changed
  pairs.
- A new --facets flag renders a row of one-click filter chips (e.g.
  canvas/light/dark) that filter screenshots by the "<name>-<facet>" suffix,
  alongside the existing status filter and name search. Generic; no theme names
  are hardcoded in the tool.
- The visual regression workflow passes --facets canvas,light,dark.

Adds unit tests for the region-boxing logic.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@balzss
balzss marked this pull request as ready for review July 8, 2026 14:18
@balzss
balzss requested a review from Copilot July 8, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the visual regression testing setup to support capturing screenshots across multiple InstUI themes (via ?theme=) and enhances the visual diff report to better highlight where changes occurred and to optionally filter results by “facet” (e.g., theme).

Changes:

  • Add query-param-driven theming to the regression-test app layout and expose the applied theme via data-theme.
  • Refactor the Cypress suite to generate visits/screenshots/meta per page and per theme, and disable Cypress’ automatic failure screenshots.
  • Improve ui-scripts visual-diff report output with changed-region highlighting and optional facet filter chips (wired into the workflow).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
regression-test/src/app/layout.tsx Select InstUI theme via ?theme= and expose data-theme for test synchronization
regression-test/README.md Document per-theme screenshot capture and new page-spec workflow
regression-test/cypress/support/e2e.ts Remove global screenshot/meta afterEach (now driven by spec)
regression-test/cypress/e2e/spec.cy.ts Generate per-page, per-theme visits/screenshots/a11y checks from data arrays
regression-test/cypress.config.ts Disable failure screenshots to avoid polluting visual baselines
packages/ui-scripts/lib/commands/visual-diff.ts Produce highlighted “diff” images + facet filter UI in report
packages/ui-scripts/lib/node_tests/visual-diff.test.ts Add unit tests for changed-mask → bounding-box extraction
.github/workflows/visual-regression.yml Pass facet list to visual-diff so the report can filter by theme

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +60 to +65
useEffect(() => {
const requested = new URLSearchParams(window.location.search).get('theme')
if (requested && requested in themes) {
setThemeKey(requested as ThemeKey)
}
}, [])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct in principle — in walks the prototype chain, so ?theme=toString would resolve themeKey to a non-theme value. Leaving as-is for now: in this internal harness the param is driven solely by the fixed THEMES list in the Cypress spec, never untrusted input, so a prototype key can't occur in practice and would at worst produce an obviously-broken screenshot. Happy to guard with Object.hasOwn(themes, requested) if you'd prefer the belt-and-suspenders.

Comment on lines +170 to +175
THEMES.forEach((theme) => {
cy.visit(`${BASE_URL}/${slug}?theme=${theme}`)
// Wait until the requested theme has actually been applied before doing
// anything else (layout.tsx sets data-theme in an effect after mount).
cy.get(`html[data-theme="${theme}"]`)
if (wait) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in bab17fd. The stub was replaced on every cy.visit, and since each test now visits once per theme, the afterEach only saw the final (dark) visit — console errors in the canvas/light renders were dropped. The outgoing stub's callCount is now folded into a running total on each window:before:load, reset in a beforeEach, and the afterEach asserts the accumulated total. A console.error in any theme now fails the test.

Comment thread regression-test/cypress.config.ts Outdated
Comment on lines +33 to +36
// Never capture Cypress' automatic failure screenshots (the error overlay).
// Visual regression baselines come solely from the deterministic afterEach
// `cy.screenshot`; a failure screenshot has a different filename and would
// otherwise leak into the diff as a spurious "new" image.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This, as well as the "Build & capture." section (line 15) in docs/contributing/testing/visual-regression.md, could be updated to reflect the current workflow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in bab17fd — comment updated. Screenshots now come from the cy.screenshot calls in the spec (per theme), not an afterEach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — fixed in bab17fd. Updated both the cypress.config comment and the "Build & capture" step in docs/contributing/testing/visual-regression.md to reflect that screenshots are taken directly in the spec, once per theme.

Comment on lines +392 to +396
<div class="filter" id="facet-filter">
<span style="font-size:12px;color:#666;">Theme:</span>
<button data-facet="all" class="active">All</button>
${facets.map((f) => `<button data-facet="${f}">${f}</button>`).join('')}
</div>`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Leaving as-is. This would only matter if facets came from untrusted input; they're hardcoded in the workflow (--facets canvas,light,dark), and the whole report is a trusted template that interpolates many values this way. No untrusted path exists, so escaping just these two would be inconsistent without a real benefit. Can revisit if facets ever become user-supplied.

Comment on lines +74 to +76
<InstUISettingsProvider
theme={themes[themeKey]}
instanceCounterMap={new Map()}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated the PR description to match the current behavior — defaults to canvas and captures canvas/light/dark via ?theme=, one screenshot per page per theme.

github-actions Bot pushed a commit that referenced this pull request Jul 8, 2026
@balzss
balzss requested a review from git-nandor July 15, 2026 11:24
@git-nandor

Copy link
Copy Markdown
Contributor

regression-test/README.md contains an outdated link:
Current:
../docs/testing/visual-regression.md
Correct:
../docs/contributing/testing/visual-regression.md

Comment thread regression-test/cypress.config.ts Outdated
Comment on lines +33 to +36
// Never capture Cypress' automatic failure screenshots (the error overlay).
// Visual regression baselines come solely from the deterministic afterEach
// `cy.screenshot`; a failure screenshot has a different filename and would
// otherwise leak into the diff as a spurious "new" image.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This, as well as the "Build & capture." section (line 15) in docs/contributing/testing/visual-regression.md, could be updated to reflect the current workflow.

Each test now visits the page once per theme, and every cy.visit re-stubs
console.error. The afterEach only saw the final visit's stub, so console errors
during the canvas or light render were silently dropped. Accumulate the call
count across visits and assert the total.

Also refresh stale docs: correct the visual-regression guide link in the
regression-test README, and update the cypress.config comment and the guide's
capture step to reflect screenshots being taken directly in the spec (per
theme) rather than in an afterEach hook.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@balzss

balzss commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@git-nandor fixed in bab17fd — the regression-test README now links to ../docs/contributing/testing/visual-regression.md.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread regression-test/README.md Outdated
- **Detect unexpected console errors** — the spec's `afterEach` hook asserts `console.error` was not called.

See the [visual regression testing guide](../docs/testing/visual-regression.md) for the full CI pipeline, the diff report UI, and tuning notes.
Each page is captured once **per theme** (`canvas`, `light`, `dark`), so screenshots are named `<slug>-<theme>.png`. The theme is selected with the `?theme=<key>` query param, which `src/app/layout.tsx` reads and applies via `InstUISettingsProvider`. Add or remove themes with the `THEMES` array in `cypress/e2e/spec.cy.ts` (this multiplies the screenshot/baseline count).
On every PR:

1. **Build & capture.** GitHub Actions builds InstUI, builds the `regression-test` Next.js app as a static site, and serves it. Cypress visits every page under `regression-test/src/app/*`, waits for it to render, and calls `cy.screenshot()` in an `afterEach` hook, producing one PNG per test case.
1. **Build & capture.** GitHub Actions builds InstUI, builds the `regression-test` Next.js app as a static site, and serves it. Cypress visits every page under `regression-test/src/app/*` once per theme (via the `?theme=` query param), waits for each to render, and calls `cy.screenshot()` directly in the spec, producing one PNG per page/theme combination.
Paint the page background from the active theme's background.page semantic token
so every theme is screenshotted on the surface it's actually used on. Most
visibly, the dark theme now renders on its near-black surface instead of white.
Canvas stays white; light shifts to its faint-grey page surface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
The lightbox gains an HTML mode alongside Baseline/Actual/Diff/Slider that iframes the live
rendered page, so a reviewer can inspect the real DOM, computed styles, and text next to the
pixels. The page URL is derived from the screenshot name via the existing meta map and facet
suffixes (appUrlFor), and shown only when the page still exists and the app was published.

To back it, CI rebuilds the regression-test app as a static export with a basePath matching its
published location and drops it next to the report; visual-diff is passed --app-path app. The
basePath is env-driven (REGRESSION_APP_BASE_PATH) so local dev and the Cypress capture build are
unaffected.

Published reports previously accumulated on gh-pages forever, and shipping the app bundle per PR
makes that worse, so the workflow now also removes a PR's report directory when the PR closes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
balzss and others added 2 commits July 21, 2026 14:11
…Page screenshots capture it

The background was set only on <html>. Cypress capture: 'fullPage' stitches the
body onto its own canvas and does not reliably paint the <html> background into
that image, so the dark surface showed in a real browser (and the report's HTML
view) but not in the captured screenshots. Set it on <body> too, with
min-height: 100vh so short pages still fill the frame.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The HTTP+SSE endpoint (mcp.atlassian.com/v1/sse) is deprecated after 2026-06-30.
Switch to the Streamable HTTP transport (type: http, /v1/mcp).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
…its factory

newTheme.semantics is a factory (primitives) => tokens, not a resolved object, so
reading `.semantics.color.background.page` off it was always undefined and the
background fell back to transparent (white) for every theme. Call the factory
with newTheme.primitives — the same way emotion's useComputedTheme resolves it.
Verified against the compiled themes: dark #10141A, light #F2F4F5, canvas #ffffff.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
@balzss
balzss requested a review from hajnaldo July 21, 2026 13:19
@git-nandor
git-nandor requested a review from HerrTopi July 21, 2026 15:58
Land three report improvements:

- Default the status filter to "Changed" (falling back to "All" when nothing
  changed) so reviewers see regressions first, and apply filters on load.
- Close the lightbox on Escape and navigate with the arrow keys.
- Rework the diff image to a Chromatic-style pixel highlight: dim and
  desaturate the actual screenshot into a faint backdrop and paint the exact
  changed pixels (dilated for visibility) in the accent color, replacing the
  coarse bounding boxes. Extracts a tested dilateMask helper.

Also add a TEMP diff-demo fixture and a workflow step that injects it so the
published report always has one "changed" row to preview the new rendering
(this PR's -<theme> screenshot renames leave zero real "changed" rows). Both
are marked for removal before merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jul 23, 2026

@hajnaldo hajnaldo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All looks good for now - maybe later when we use it we'll have suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants