fix: restore pseudo states and correct iframe sizing in Vitest SDKs - #356
Open
gregberge wants to merge 2 commits into
Open
fix: restore pseudo states and correct iframe sizing in Vitest SDKs#356gregberge wants to merge 2 commits into
gregberge wants to merge 2 commits into
Conversation
Screenshots taken through `@argos-ci/vitest` and the `@argos-ci/storybook` Vitest plugin were cut off, with the bottom of the page left blank. Both SDKs grow the Vitest tester iframe to fit the content, because anything overflowing the iframe box is never painted. They did that from `beforeScreenshot`, which the Playwright SDK runs *before* it waits for images and fonts. An `<img>` that has not loaded takes no space, so the iframe was sized from a partially-loaded layout and everything below that height was never painted. Run the stabilization first, so the content has reached its final size before it is measured. The iframe was also never restored afterwards. The `"initial"` branch of `setIframeViewportSize` was guarded on the backed-up width being truthy, but the original inline width is an empty string, and the default (non-`viewports`) path never asked for a restore at all. Vitest reuses the same iframe for every test in a file, so each screenshot inherited the largest previous one — a single `<button>` was captured at 2000x1600 in our own e2e suite. Track the backup under a dedicated attribute, whose presence marks the size as saved, and restore it once the screenshot is taken. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Since the Storybook 10.5 upgrade, stories relying on `storybook-addon-pseudo-states` lost their pseudo state: `Primary Hover` rendered as a plain blue button instead of the red hovered one. The addon only rewrites `:hover` rules into `.pseudo-hover` ones when it receives `storyRendered` on the Storybook channel, and it grabs that channel when it is imported. Portable stories (Vitest) run without the preview runtime, so no channel is installed and `addons.getChannel()` falls back to a mock one. Up to 10.4 that mock was created once and cached, so the addon's listener and the `storyRendered` Argos emits landed on the same object. Since 10.5 a throwaway mock is returned per call, so the event went nowhere: the root element still got its `pseudo-hover-all` class, but no rule had been rewritten to match it. Install a real channel in a setup file registered ahead of the user's, so it exists before any addon preview module captures one. The existing setup file stays last: `afterEach` hooks run in reverse registration order, and that is what makes the screenshot happen before Storybook unmounts the story. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Two fixes for regressions in the Vitest-based SDKs, surfaced after the dependency upgrade in #354.
1. Pseudo states lost in portable stories (
@argos-ci/storybook)This is what build 39470 flagged. Out of 30 diffs, exactly 2 changed:
example-button--primary-hoverin both modes. The baseline is a red (hovered) button, the head is a plain blue one.storybook-addon-pseudo-statesrewrites:hoverrules into.pseudo-hoverones only when it receivesstoryRenderedon the Storybook channel, and it grabs that channel when it is imported. Portable stories run without the preview runtime, so no channel is installed andaddons.getChannel()falls back to a mock one:this.setChannel(channel)), so the addon's listener and thestoryRenderedArgos emits landed on the same object.Instrumenting at screenshot time showed it precisely: the root element still had its
pseudo-hover-allclass, but every CSS rule had__processed: nullandgetChannel().listeners("storyRendered").length === 0.The fix installs a real
Channelfrom a setup file registered ahead of the user's, so one exists before any addon preview module captures it. The existing setup file stays last on purpose —afterEachhooks run in reverse registration order, and that is what makes the screenshot happen before Storybook unmounts the story.2. Screenshots cut off, and iframe size leaking between tests
Both
@argos-ci/vitestand the@argos-ci/storybookVitest plugin grow the Vitest tester iframe to fit the content, because anything overflowing the iframe box is never painted. Two bugs there:The iframe was measured before the content had settled. The fit ran from
beforeScreenshot, which the Playwright SDK calls beforewaitForReadiness. An<img>that has not loaded takes no space, so the iframe was sized from a partially-loaded layout and everything below that height came out blank. Now stabilization runs first, so images and fonts are loaded before the content is measured.The grown iframe was never restored. The
"initial"branch ofsetIframeViewportSizewas guarded on the backed-up width being truthy, but the original inline width is an empty string — and the default (non-viewports) path never asked for a restore at all. Vitest reuses one iframe per file, so each screenshot inherited the largest previous one: a single<button>was being captured at 2000x1600 in our own e2e suite. The backup now lives under a dedicated attribute whose presence marks the size as saved, and it is restored once the screenshot is taken.Tests
packages/vitest/e2e: a Vite middleware serves an image after a delay, and the test decodes the resulting PNG to assert the bottom band is not blank. Confirmed to fail without the stabilization fix.packages/vitest/e2e: a small capture taken after thetall/wideones asserts it did not inherit their size. Confirmed to fail without the restore fix.packages/vitest/src/command.test.ts: pins that stabilization runs before the iframe is measured, and that the size is restored.packages/storybook/src/vitest-plugin.test.ts(new unit project): pins both setup-file positions. Confirmed to fail if theunshiftbecomes apush.Snapshot sizes in the Vitest e2e suite are now content-correct (
aria.png2000x1600 → 414x896),example-button--primary-hoverrenders red again and differs fromexample-button--primaryin both modes, and the Storybook Vitest e2e produces identical dimensions to before for every other story.Note
Fix 2 is verified against a deterministic local reproduction, but it is not what build 39470 showed — that build was entirely fix 1. It was written for the second, user-reported "screenshots are cut again" case, which I could not open. It applies if that project runs Storybook + Vitest (or
@argos-ci/vitest); if it uses the Playwright-based Storybook test-runner, no iframe fit is involved and this will not address it. Worth confirming before we call that report closed.🤖 Generated with Claude Code