Skip to content

test(button): add missing coverage and consolidate snapshots - #31627

Open
thetaPC wants to merge 26 commits into
ionic-modularfrom
FW-6838-tests
Open

thetaPC wants to merge 26 commits into
ionic-modularfrom
FW-6838-tests

Conversation

@thetaPC

@thetaPC thetaPC commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: resolves internal


What is the current behavior?

Button's tests skip the ionic theme in several specs, take a separate screenshot for every variant, and never check the default shape, fill and size. In md, activated screenshots don't show the ripple, which only appears on a real press.

What is the new behavior?

No component code changes. This sets the baselines the button migration is compared against, so its snapshot diffs come only from the migration.

  • Adds spec tests for the default shape per theme, and the default fill and size inside parents such as ion-buttons and ion-item
  • Adds the ionic theme to the expand, icon, item buttons and toolbar colors specs, and runs the spinner spec in every theme
  • Adds the medium size to the size spec, alone and in ion-buttons, and a disabled button in each color to the basic, outline and clear pages
  • Consolidates the shape, states, size, strong, wrap, badge and a11y scale screenshots into one grid per spec
  • Test pages freeze the ripple on .ion-activated elements, and goto waits for it, so md activated screenshots show the pressed state. This also updates the activated snapshots of card, item, item-sliding, tab bar and toolbar
  • Skips the a11y check for a pressed outline button in md's dark palette (TODO(FW-6417)). Its text fails contrast once the ripple is visible, and that predates this PR

Does this introduce a breaking change?

  • Yes
  • No

Other information

@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Nov 10, 2026 3:06am UTC

Request Review

@github-actions github-actions Bot added the package: core @ionic/core package label Oct 21, 2026
@thetaPC
thetaPC added this pull request to stack #31629 October 21, 2026 23:00
@thetaPC
thetaPC marked this pull request as ready for review October 27, 2026 23:25
@thetaPC
thetaPC requested a review from a team as a code owner October 27, 2026 23:25
@thetaPC
thetaPC requested a review from ShaneK October 27, 2026 23:25
@thetaPC
thetaPC removed this pull request from stack #31629 October 28, 2026 22:48
@thetaPC
thetaPC added this pull request to stack #31667 October 28, 2026 22:54
# Conflicts:
#	core/scripts/testing/scripts.js
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/basic/button.e2e.ts-snapshots/button-diff-md-rtl-Mobile-Safari-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/clear/button.e2e.ts-snapshots/button-fill-clear-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-ltr-Mobile-Safari-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/button/test/outline/button.e2e.ts-snapshots/button-outline-md-rtl-Mobile-Safari-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/item/test/buttons/item.e2e.ts-snapshots/item-buttons-diff-md-rtl-Mobile-Safari-linux.png
#	core/src/components/item/test/colors/item.e2e.ts-snapshots/item-colors-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/item/test/colors/item.e2e.ts-snapshots/item-colors-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/item/test/colors/item.e2e.ts-snapshots/item-colors-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/item/test/states/item.e2e.ts-snapshots/item-states-diff-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/item/test/states/item.e2e.ts-snapshots/item-states-diff-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/item/test/states/item.e2e.ts-snapshots/item-states-diff-md-ltr-Mobile-Safari-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-Mobile-Safari-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-ltr-dark-Mobile-Safari-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-Mobile-Safari-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-md-rtl-dark-Mobile-Safari-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-ltr-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-ltr-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-rtl-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-rtl-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-rtl-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-md-rtl-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/colors/toolbar.e2e.ts-snapshots/toolbar-colors-md-ltr-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/colors/toolbar.e2e.ts-snapshots/toolbar-colors-md-ltr-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/colors/toolbar.e2e.ts-snapshots/toolbar-colors-md-ltr-Mobile-Safari-linux.png

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks really good, the consolidated grids are a lot easier to read through! Just a few things, mainly the ripple freeze not quite reaching ionic-md.

Comment thread core/scripts/testing/scripts.js Resolved
Comment thread core/scripts/testing/scripts.js Resolved
# Conflicts:
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-large-badge-ionic-md-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-medium-badge-ionic-md-rtl-light-Mobile-Safari-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-ltr-light-Mobile-Safari-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/button/test/badge/button.e2e.ts-snapshots/button-small-badge-ionic-md-rtl-light-Mobile-Safari-linux.png

@ShaneK ShaneK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the round 2 updates, just a couple of small follow-ups on the define hold!

Comment thread core/scripts/testing/scripts.js Outdated
Comment on lines 157 to 163
if (themeQuery) {
holdComponentDefinitions(themeLoaded.then(setThemeConfig).catch(() => {}));
}

themeReady = loadThemeTokens(themeName, themeLoaded).finally(() => {
window.__ionicTestThemeReady = true;
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if (themeQuery) {
holdComponentDefinitions(themeLoaded.then(setThemeConfig).catch(() => {}));
}
themeReady = loadThemeTokens(themeName, themeLoaded).finally(() => {
window.__ionicTestThemeReady = true;
});
let definitionsReady = Promise.resolve();
if (themeQuery) {
definitionsReady = themeLoaded.then(setThemeConfig).catch(() => {});
holdComponentDefinitions(definitionsReady);
}
/*
* While the definitions are held no component connects, so Stencil fires
* `appload` from its 30ms fallback before any component has loaded. Wait
* for the held components to load before the page counts as ready.
*/
themeReady = Promise.all([loadThemeTokens(themeName, themeLoaded), definitionsReady])
.then(() =>
Promise.all(
[...document.querySelectorAll('*')]
.filter((el) => el.tagName.startsWith('ION-'))
.map((el) => el.componentOnReady?.())
)
)
.finally(() => {
window.__ionicTestThemeReady = true;
});

While the definitions are held, no Ionic component connects during bootstrap, so Stencil's 30ms appload fallback fires with nothing defined and sets testAppLoaded early. If the tokens arrive after Ionic boots, goto can return before the components hydrate. With the tokens delayed 150ms, goto came back with 0 to 39 of the 113 elements on button/test/shape hydrated in Chromium and WebKit, and the page was blank at that point. It's rare without a forced delay (1 in 10-15 loads on the preview, never on localhost), but on a slow runner it would show up as a flaky blank screenshot. Waiting on componentOnReady for the held elements fixed it in every case I tried without changing goto timing or the e2e results. What do you think?

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.

Great find, thanks for tracking that down. Done in e8bb570.

* back the definitions of Ionic's components until the theme's config is
* set means none of them renders without it.
*
* Only pages with the theme in the URL query need this: `setContent`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hand-opened pages without ionic:theme= in the query don't get the hold, so they can still render with the base config when the tokens are slow. With no params or just ?ionic:mode=md, the md chips in chip/test/basic came out subtle and round instead of bold and soft once the tokens were delayed 400ms. CI never hits this since goto always adds ionic:theme=, but it's the same hand-opened vs snapshot mismatch you brought up on the ripple thread. I'm not sure what the right gate is though, since setContent pages load at a bare # and can't be told apart by the hash. Checking location.pathname !== '/' worked when I tried it, but it feels pretty fragile. How do you want to handle these pages? Totally fine to leave it as is too.

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.

I'll leave it as is. CI always passes the theme, and so do the compare and probe scripts and the How to test links in the PR descriptions, so only a bare URL typed by hand can hit it, and adding ?ionic:theme=md fixes it. Those pages can't be told apart from setContent pages without a check like the pathname one, which I'd rather not depend on.

Comment thread core/scripts/testing/scripts.js Outdated
Comment on lines +271 to +281
ready.then(() => {
released = true;
});

// If a specific palette is requested, modify the palette structure
// to set the enabled property to 'always'
// TODO(FW-4004): Implement dark palette
if (paletteName === 'dark' && theme.palette?.dark) {
theme.palette.dark.enabled = 'always';
// TODO(FW-4005): Implement high contrast palette
} else if (paletteName === 'high-contrast' && theme.palette?.highContrast) {
theme.palette.highContrast.enabled = 'always';
// TODO(FW-4005): Implement high contrast dark palette
} else if (paletteName === 'high-contrast-dark' && theme.palette?.highContrastDark) {
theme.palette.highContrastDark.enabled = 'always';
customElements.define = (name, constructor, options) => {
if (released || !name.startsWith('ion-')) {
define(name, constructor, options);
return;
}

ready.then(() => define(name, constructor, options));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
ready.then(() => {
released = true;
});
// If a specific palette is requested, modify the palette structure
// to set the enabled property to 'always'
// TODO(FW-4004): Implement dark palette
if (paletteName === 'dark' && theme.palette?.dark) {
theme.palette.dark.enabled = 'always';
// TODO(FW-4005): Implement high contrast palette
} else if (paletteName === 'high-contrast' && theme.palette?.highContrast) {
theme.palette.highContrast.enabled = 'always';
// TODO(FW-4005): Implement high contrast dark palette
} else if (paletteName === 'high-contrast-dark' && theme.palette?.highContrastDark) {
theme.palette.highContrastDark.enabled = 'always';
customElements.define = (name, constructor, options) => {
if (released || !name.startsWith('ion-')) {
define(name, constructor, options);
return;
}
ready.then(() => define(name, constructor, options));
ready.finally(() => {
released = true;
});
customElements.define = (name, constructor, options) => {
if (released || !name.startsWith('ion-')) {
define(name, constructor, options);
return;
}
ready.finally(() => define(name, constructor, options));

Nit: the comment says the definitions are held until ready settles, but they're only released when it resolves, so a rejected ready would leave every ion-* element undefined. The caller catches, so nothing hits this today. Using finally makes it match the comment. Up to you!

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.

Done in e545fdd.

thetaPC and others added 2 commits November 9, 2026 11:28
…s ready

Co-authored-by: Shane <561207+ShaneK@users.noreply.github.com>
Co-authored-by: Shane <561207+ShaneK@users.noreply.github.com>
# Conflicts:
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-ltr-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-ltr-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-rtl-dark-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-rtl-dark-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-icon-buttons-ionic-md-rtl-light-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-ionic-md-ltr-light-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-ionic-md-ltr-light-Mobile-Firefox-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-ionic-md-rtl-light-Mobile-Chrome-linux.png
#	core/src/components/toolbar/test/basic/toolbar.e2e.ts-snapshots/toolbar-basic-text-icon-buttons-ionic-md-rtl-light-Mobile-Firefox-linux.png

This branch was successfully deployed

1 active deployment
Preview — 6f5f6c81 Deployed Nov 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core @ionic/core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants