[test] Cover docs landing-page composites with Argos#48589
Draft
LukasTy wants to merge 10 commits into
Draft
Conversation
Add Argos screenshot coverage for the ~27 bespoke composites under `docs/src/components/product*/*.tsx` that assemble the product landing pages (`/material-ui`, `/x`, `/core`, `/design-kits`, `/templates`). The existing fixture glob walks `docs/data/**` only, so these composites have historically regressed silently until someone opened the page. Ports the approach from mui/mui-x#22283 with one material-ui-specific addition: a tiny `next/*` stub layer in the regression bundle's Vite config. Nearly every product composite directly or transitively imports `Link` from `@mui/internal-core-docs/Link`, which calls `useRouter()` from `next/router`; without the stub, render either crashes at runtime or fails to bundle (the `AppLayout` barrel pulls `next/router` in via `AppLayoutHead`/`AppLayoutDocs`/`AppSearch`). - `stubs/{next-router,next-link,next-head}.{ts,tsx}` (~30 lines total): `useRouter` returns safe defaults, `next/link` renders a plain anchor (visually equivalent for Argos), `next/head` is `() => null`. - `vite.config.mts`: alias `next/router`, `next/link`, `next/head` to the stubs. - `MarketingWrapper.tsx`: wraps composite fixtures with `BrandingProvider` so they can read docs branding tokens (`palette.primaryDark`, `palette.gradients`, `applyDarkStyles`). - `index.jsx`: second `import.meta.glob` for `docs/src/components/product*/[A-Z]*.tsx`; PascalCase → kebab-case for the suite name (`productDesignKit` → suite `docs-product-design-kit`); composites get wrapped in `MarketingWrapper`. - `demoMeta.ts`: adds a `viewport` field on `ScreenshotRule`, exports `DEFAULT_VIEWPORT`, extends `parseRoute` to recognise composite routes and round-trip the kebab-case back to a PascalCase directory, and appends two viewport rules — `product*/**` → 1280×800, `productX/**` → 1440×900 (last-match-wins). - `index.test.js`: per-route `page.setViewportSize(rule?.viewport ?? DEFAULT_VIEWPORT)` before each fixture render. - `demoMeta.test.ts`: new cases for the composite route regex, including the kebab-case → PascalCase round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploy previewhttps://deploy-preview-48589--material-ui.netlify.app/ Bundle size
Check out the code infra dashboard for more information about this PR. |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- `stubs/next-link.tsx`: `NextLinkStubProps` was extending
`AnchorHTMLAttributes<HTMLAnchorElement>` but widening `href` from
`string | undefined` to `string | { pathname?: string }`. Omit
`href` from the base before redeclaring it.
- `MarketingWrapper.tsx`: importing `BrandingProvider` from
`@mui/internal-core-docs/branding` pulled in the package's barrel,
whose sibling modules use extensionless relative imports
(`from './brandingTheme'`). That's fine under the package's own
`moduleResolution: 'bundler'`, but the `test/tsconfig.json` typecheck
runs under `nodenext` and surfaced them as TS2835. Deep-import
`brandingLightTheme` from the leaf module (which has zero relative
imports) and inline the minimal light-mode `ThemeProvider` wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caught by CI's `pnpm dedupe --check`. The `eslint-plugin-import` peer info had a nested `eslint-import-resolver-typescript` resolver specifier that pnpm dedupe simplifies to the flat form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's `test_types` job (which runs `tsc -p test/tsconfig.json` under `moduleResolution: 'nodenext'`) was failing to resolve `@mui/material/themeCssVarsAugmentation` and friends when typechecking walked into `packages-internal/core-docs/src/branding/brandingTheme.ts` via `MarketingWrapper.tsx`'s deep import. The augmentations resolve fine in the package's own typecheck (`bundler` resolution) and in the Vite bundle, but not in `test/`'s nodenext context against the test package's node_modules layout. Workaround: route the import through a local `brandingThemeShim` — the `.d.ts` declares the theme as a `@mui/material/styles` `Theme`, and the `.js` does the runtime re-export. TS picks the `.d.ts` and never walks into the augmentation source; Vite picks the `.js` and resolves it normally. Also added `regressions/brandingThemeShim.js` to `test/tsconfig.json`'s exclude as belt-and-suspenders so TS doesn't accidentally include the runtime shim and start chasing imports through it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ing-composites-argos # Conflicts: # test/regressions/index.test.js
The broad `product*/[A-Z]*.tsx` glob pulled in all 27 composites, but most are marketing chrome — heroes that are mostly links/images, pricing tables, Figma-kit promos, template thumbnails — with no real component integration for Argos to guard. Replace it with an explicit allowlist of the 9 composites that render real MUI / MUI X components in a composed layout, where a CSS/theme regression would actually show. Kept: - productMaterial: MaterialHero, MaterialStyling, MaterialTheming - productX: XHero, XGridFullDemo, XDateRangeDemo, XChartsDemo, XTreeViewDemo, XTheming For MUI X, each product is covered by its isolated demo rather than via the `XComponents` switcher composite (which renders these same demos behind tabs) — that drops the tab chrome and the duplicate grid section (`XDataGrid`), giving one clean screenshot per product. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
XHero and XDateRangeDemo build their picker default values from `dayjs()` at *module scope* (e.g. `const startDate = dayjs().date(10)`), which runs when the eager fixture glob loads the bundle — before any render-time or per-test mock could intervene. Without a fixed clock the calendars (and any date columns in the grid demos) would shift every run and churn the Argos baseline. Install a fixed `Date` via an inline script in `index.html`, ahead of the module bundle, so it is in place before any composite module evaluates. Native `new Date(arg)`, `Date.parse`, `Date.UTC` and `instanceof Date` keep working; only the "now" reading is pinned. Mirrors the fixed-date approach used by mui-x's regression bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous inline `Date` override was hand-rolled and I incorrectly described it as mirroring mui-x (mui-x uses sinon `useFakeTimers`; this bundle does not). Replace the shim with the reviewed approach from mui/base-ui#4337: `Reflect.construct` preserves the native `Date.prototype` (timezone-aware date libraries enumerate its own methods) and forwards `new.target`. Also pin `timezoneId: 'UTC'` on the playwright page — without it the frozen instant would render in the CI machine's local timezone and still churn the baseline. This was missing before. Kept as an inline classic <script> (rather than base-ui's module + fixture-extraction in mui#4370): a classic script runs synchronously before the deferred module bundle, so it beats the eager-glob import hoisting that mui#4370 worked around — without refactoring the glob loading that drives the entire VRT suite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With coverage narrowed to `productMaterial` and `productX`, the kebab-case <-> PascalCase round-trip (added for multi-word products like `productDesignKit`) is dead code. Replace it with a plain lower-case / re-capitalize of the single-word product segment in both `index.jsx` (suite name) and `demoMeta.ts` (`parseRoute`), and drop the now-irrelevant multi-segment test case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Argos screenshot coverage for the bespoke composites that assemble the product landing pages (
/material-ui,/x). They live underdocs/src/components/product*/*.tsxand were uncovered by the fixture loader, which globsdocs/data/**only.Coverage is an explicit allowlist of 9 composites that render real MUI / MUI X components in a composed layout — the ones where a CSS/theme regression would actually show. The other ~18
product*composites are marketing chrome (heroes that are mostly links/images, pricing tables, Figma-kit promos, template thumbnails) and are intentionally left out.docs-product-materialMaterialHero,MaterialStyling,MaterialThemingdocs-product-xXHero,XGridFullDemo,XDateRangeDemo,XChartsDemo,XTreeViewDemo,XThemingFor MUI X, each product is covered by its isolated demo rather than via the
XComponentsswitcher composite (which renders these same demos behind tabs) — that drops the tab chrome and the duplicateXDataGridsection, giving one clean screenshot per product (grid, pickers, charts, tree view) plus the hero and the custom-theme grid.Ported from mui/mui-x#22283, with two material-ui-specific additions (stub layer + date freeze, below).
How it works
next/*stub layer (test/regressions/stubs/, aliased invite.config.mts). Every kept composite imports from the@mui/internal-core-docs/AppLayoutbarrel, whose siblings (AppLayoutHead/AppLayoutDocs/AppSearch) importnext/router,next/link,next/head;MaterialHeroalso importsLinkdirectly. Without the stubs the real Next modules get bundled and crash at runtime outside a Next router context (verified: removing them swaps the stubs for the real modules and the bundle balloons). The stubs return safe defaults so<Link>renders a plain anchor (visually equivalent for Argos).MarketingWrappersupplies the docs branding theme (light) so composites can read branding tokens (palette.primaryDark,palette.gradients,applyDarkStyles). It routes through a.d.ts/.jsshim (brandingThemeShim) so thenodenexttest typecheck doesn't choke on the theme file's@mui/material/*module augmentations.demoMeta.ts):product*/**→ 1280×800,productX/**→ 1440×900. The runner appliespage.setViewportSizeper route (important now that pages come from a pool and may have been resized by a prior test).index.htmlinline script +timezoneId: 'UTC').XHeroandXDateRangeDemocompute picker defaults fromdayjs()at module scope, which runs when the eager fixture glob loads — too early for a render-time mock. TheDateoverride follows mui/base-ui#4337 (Reflect.construct, preserves the native prototype) and is delivered as an inline classic<script>so it runs before the deferred module bundle — sidestepping the eager-glob import hoisting that base-ui#4370 had to work around, without refactoring the suite-wide glob loading. The page is pinned to UTC so the frozen instant renders identically across CI machines. (Note: this is not mui-x's mechanism — mui-x uses sinonuseFakeTimers.)Why
These composites combine many components into marketing-style layouts and regress easily (CSS bleed, theme tweaks, internal style changes) without anyone noticing until the page is opened — and they sit outside the
docs/data/**glob, so they had no coverage.Test plan
pnpm test:regressions:build— bundle builds clean; exactly the 9 allowlisted composites are included.pnpm eslint test/regressions/,pnpm prettier --check,pnpm typescript(all 19 projects) — all green.demoMeta.test.ts(9/9) — incl. the composite route regex.dayjs:dayjs()pins to the fixed instant,dayjs().date(10)/.add(28,'day')are deterministic, explicitnew Date(arg),Date.UTC, andinstanceof Datestill work.XHero/grids, the per-product viewport layout, and the frozen calendars inXHero/XDateRangeDemo) before accepting.Follow-ups (not in this PR)
next/*stub pattern to mui-x to retire theirChartFeaturesGrid/ChartComponentsGrid/ChartComponentsUpcomingexclusions.home/,showcase/) is now mostly mechanical with the stub + wrapper in place.🤖 Generated with Claude Code