Skip to content

Commit 1a16b24

Browse files
committed
test(nextjs-sandbox): distill gauntlet regressions into stories + e2e
Lock the regression value of the external community gauntlet (transit, oak, console, proposalsapp, anticapture, safe-wallet, t-performance-dash, usaco-guide) into committed stories + Playwright e2e so the bridge can't silently regress once those projects are gone: - CSS url() passthrough: root-absolute (safe-wallet /vercel.svg) and data: custom-property (transit) URLs survive css-loader via isRuntimeCssUrl. - __barrel_optimize__ over TS source: new @sandboxes/nextjs-barrel re-export barrel reproduces the @mui TS-as-JS break (safe-wallet) that makeBarrelRule fixes; OptimizedImports gains a TsBarrel story. - Styling stacks: Tailwind (PostCSS), SCSS modules (@rsbuild/plugin-sass via rsbuildFinal), styled-components (next-swc transform), @emotion. - Font e2e asserts the ported loader's deterministic inter-normal class. Adds @emotion, styled-components, tailwindcss, postcss, @rsbuild/plugin-sass to the sandbox; pnpm-lock.yaml synced.
1 parent 1efd872 commit 1a16b24

23 files changed

Lines changed: 699 additions & 117 deletions

e2e/tests/nextjs.spec.ts

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,12 @@ test.describe(sandbox.name, () => {
8080
const heading = frame.getByRole('heading', { name: 'Inter (Google Font)' })
8181
await expect(heading).toBeVisible()
8282

83-
// next/font emits a hashed class prefixed with `__className_` / `__Inter`;
84-
// exact hash is non-deterministic so match loosely.
83+
// The ported @storybook/nextjs font loader emits a deterministic
84+
// `<family>-<style>` class (e.g. `inter-normal`), not Next's native
85+
// `__className_<hash>`. See loaders/storybook-nextjs-font-loader.cjs.
8586
const wrapper = heading.locator('..')
8687
const className = await wrapper.getAttribute('class')
87-
expect(className).toMatch(/(__Inter|__className_)/)
88+
expect(className).toMatch(/inter-normal/)
8889
})
8990

9091
test('next/font exposes a CSS variable that resolves to the font family', async ({
@@ -366,4 +367,83 @@ test.describe(sandbox.name, () => {
366367
await expect(probe).toBeVisible()
367368
await expect(probe).toHaveText(/resolved to empty module/)
368369
})
370+
371+
test('root-absolute url() in CSS builds and passes through as a runtime URL', async ({
372+
page,
373+
}) => {
374+
// Regression target (safe-wallet): Rsbuild's css-loader tried to resolve
375+
// `/vercel.svg` as a module and failed the build; we mirror Next.js and
376+
// leave root-absolute urls untouched (isRuntimeCssUrl).
377+
const frame = await openStory(page, 'stories-cssabsoluteurl--default')
378+
const probe = frame.getByTestId('css-abs-url-probe')
379+
await expect(probe).toBeVisible()
380+
await expect(probe).toHaveCSS('background-image', /url\(.*vercel\.svg.*\)/)
381+
})
382+
383+
test('data: URL in a CSS custom property survives the css-loader passthrough', async ({
384+
page,
385+
}) => {
386+
// Regression target (transit/proposalsapp): css-loader must not try to
387+
// resolve a data:/external URL as a module.
388+
const frame = await openStory(page, 'stories-cssexternalurl--default')
389+
const probe = frame.getByTestId('css-ext-url-probe')
390+
await expect(probe).toBeVisible()
391+
await expect(probe).toHaveCSS(
392+
'background-image',
393+
/url\(["']?data:image\/svg\+xml/,
394+
)
395+
})
396+
397+
test('optimizePackageImports compiles a TS re-export barrel via __barrel_optimize__', async ({
398+
page,
399+
}) => {
400+
// Regression target (safe-wallet @mui): the barrel matchResource bypasses
401+
// the .tsx rule, so without makeBarrelRule the TS source would be parsed as
402+
// raw JS and throw. lucide-react (JS) doesn't exercise this; this does.
403+
const frame = await openStory(page, 'stories-optimizedimports--ts-barrel')
404+
await expect(frame.getByTestId('barrel-ts-badge')).toHaveText('ok')
405+
})
406+
407+
test('Tailwind utilities are expanded by the PostCSS pipeline', async ({
408+
page,
409+
}) => {
410+
// Regression target: 6/8 gauntlet projects run Tailwind through Rsbuild's
411+
// PostCSS. Arbitrary-value utilities prove `@tailwind utilities` expanded.
412+
const frame = await openStory(page, 'stories-tailwind--default')
413+
const probe = frame.getByTestId('tw-probe')
414+
await expect(probe).toBeVisible()
415+
await expect(probe).toHaveCSS('color', 'rgb(255, 71, 133)')
416+
await expect(probe).toHaveCSS('font-weight', '700')
417+
})
418+
419+
test('SCSS modules compile via @rsbuild/plugin-sass and apply scoped styles', async ({
420+
page,
421+
}) => {
422+
// Regression target (oak): pins the consumer contract that .scss works when
423+
// @rsbuild/plugin-sass is enabled, with CSS-module scoping intact.
424+
const frame = await openStory(page, 'stories-scss--default')
425+
const probe = frame.getByTestId('scss-probe')
426+
await expect(probe).toBeVisible()
427+
await expect(probe).toHaveCSS('border-color', 'rgb(0, 128, 0)')
428+
})
429+
430+
test('styled-components applies styles via the next-swc transform', async ({
431+
page,
432+
}) => {
433+
// Regression target (oak): the styled-components SWC transform flows through
434+
// our extracted next-swc loader chain.
435+
const frame = await openStory(page, 'stories-styledcomponents--default')
436+
const probe = frame.getByTestId('sc-probe')
437+
await expect(probe).toBeVisible()
438+
await expect(probe).toHaveCSS('color', 'rgb(255, 71, 133)')
439+
})
440+
441+
test('@emotion/styled injects styles into the preview', async ({ page }) => {
442+
// Regression target (safe-wallet): @emotion/styled resolves and injects
443+
// styles through Rsbuild's bundle.
444+
const frame = await openStory(page, 'stories-emotion--default')
445+
const probe = frame.getByTestId('emotion-probe')
446+
await expect(probe).toBeVisible()
447+
await expect(probe).toHaveCSS('color', 'rgb(255, 199, 0)')
448+
})
369449
})

0 commit comments

Comments
 (0)