From 637f52b388278b29f2148a4011824b55fd4dbe3d Mon Sep 17 00:00:00 2001 From: Saninn Salas Diaz <5490201+distante@users.noreply.github.com> Date: Sat, 18 Apr 2026 11:54:56 +0200 Subject: [PATCH 1/2] fix: filter ion-page-hidden elements before asserting hydration in JQuery/Chainable branches In the JQuery and Chainable branches, the hydration assertion ran on the full element set before hidden-page elements were filtered out. If all elements were on hidden pages and lacked the .hydrated class, Cypress's .not() command would retry until timeout rather than returning an empty set. The fix uses jQuery's native .not() synchronously inside a .then() callback to filter hidden elements first, then skips the hydration assertion when the filtered set is empty. --- .../e2e/get-from-supported-selector.spec.ts | 44 +++++++++++++++++++ src/helpers/get-from-supported-selector.ts | 27 +++++++----- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/cypress/e2e/get-from-supported-selector.spec.ts b/cypress/e2e/get-from-supported-selector.spec.ts index a3cb02b..9587b93 100644 --- a/cypress/e2e/get-from-supported-selector.spec.ts +++ b/cypress/e2e/get-from-supported-selector.spec.ts @@ -96,6 +96,50 @@ describe('Get From Supported Selector', () => { }); }); + describe('filters hidden-page elements BEFORE asserting hydration', () => { + beforeEach(() => { + cy.document().then((doc) => { + doc.body.insertAdjacentHTML( + 'beforeend', + `
+
Not Hydrated Hidden
+
+
+
Visible Hydrated
+
`, + ); + }); + }); + + it('Chainable - does not fail when a hidden element lacks .hydrated', () => { + getFromSupportedSelector(cy.get('.testing-order-bug')) + .should('have.length', 1) + .should('have.text', 'Visible Hydrated'); + }); + + it('JQuery - does not fail when a hidden element lacks .hydrated', () => { + cy.get('.testing-order-bug').then(($els) => { + getFromSupportedSelector($els) + .should('have.length', 1) + .should('have.text', 'Visible Hydrated'); + }); + }); + + it('Chainable - returns empty set without timing out when all matches are hidden', () => { + getFromSupportedSelector( + cy.get('.testing-order-bug').filter(':not(.hydrated)'), + ).should('have.length', 0); + }); + + it('JQuery - returns empty set without timing out when all matches are hidden', () => { + cy.get('.testing-order-bug') + .filter(':not(.hydrated)') + .then(($els) => { + getFromSupportedSelector($els).should('have.length', 0); + }); + }); + }); + describe('waiting for components hydration', () => { it('css selector', () => { getFromSupportedSelector('.ion-range-to-test-hydration') diff --git a/src/helpers/get-from-supported-selector.ts b/src/helpers/get-from-supported-selector.ts index a31a648..2551a2e 100644 --- a/src/helpers/get-from-supported-selector.ts +++ b/src/helpers/get-from-supported-selector.ts @@ -10,19 +10,10 @@ export function getFromSupportedSelector( } if (isJQuery(selector)) { - return filterOutHiddenPage( - cy - .wrap(selector) - .should('have.class', 'hydrated') as CypressIonicReturn, - ); + return filterThenAssertHydrated(cy.wrap(selector) as CypressIonicReturn); } - return filterOutHiddenPage( - (selector as unknown as CypressIonicReturn).should( - 'have.class', - 'hydrated', - ), - ); + return filterThenAssertHydrated(selector as unknown as CypressIonicReturn); } function filterOutHiddenPage( @@ -33,6 +24,20 @@ function filterOutHiddenPage( ) as unknown as CypressIonicReturn; } +function filterThenAssertHydrated( + subject: CypressIonicReturn, +): CypressIonicReturn { + return subject.then((elements) => { + const $visible = elements.not( + '.ion-page-hidden, .ion-page-hidden *', + ) as unknown as JQuery; + if ($visible.length === 0) { + return cy.wrap($visible); + } + return cy.wrap($visible).should('have.class', 'hydrated'); + }) as unknown as CypressIonicReturn; +} + function isJQuery( selector: SupportedSelectors, ): selector is JQuery { From 9cbf2a7d65260f256fae4f4c3932c59df92343a3 Mon Sep 17 00:00:00 2001 From: Saninn Salas Diaz <5490201+distante@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:12:58 +0200 Subject: [PATCH 2/2] fix: filter hidden ion-page elements before hydration assertion Route the string-selector path through the same filter-then-assert flow as Chainable/JQuery inputs, so hidden page matches are removed before checking hydrated class. Prevents timeout retries when all matches are inside ion-page-hidden and never hydrated, returning an empty set instead. Add a Cypress regression test for the css-selector hidden-only non-hydrated scenario. --- cypress/e2e/get-from-supported-selector.spec.ts | 16 ++++++++++++++++ src/helpers/get-from-supported-selector.ts | 17 +++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/get-from-supported-selector.spec.ts b/cypress/e2e/get-from-supported-selector.spec.ts index 9587b93..f94521f 100644 --- a/cypress/e2e/get-from-supported-selector.spec.ts +++ b/cypress/e2e/get-from-supported-selector.spec.ts @@ -138,6 +138,22 @@ describe('Get From Supported Selector', () => { getFromSupportedSelector($els).should('have.length', 0); }); }); + + it('css selector - returns empty set without timing out when all matches are hidden and not hydrated', () => { + cy.document().then((doc) => { + doc.body.insertAdjacentHTML( + 'beforeend', + `
+
Hidden Only
+
`, + ); + }); + + getFromSupportedSelector('.testing-order-hidden-only').should( + 'have.length', + 0, + ); + }); }); describe('waiting for components hydration', () => { diff --git a/src/helpers/get-from-supported-selector.ts b/src/helpers/get-from-supported-selector.ts index 2551a2e..0429ead 100644 --- a/src/helpers/get-from-supported-selector.ts +++ b/src/helpers/get-from-supported-selector.ts @@ -1,4 +1,7 @@ import { CypressIonicReturn, SupportedSelectors } from '../interfaces'; + +const HIDDEN_PAGE_SELECTOR = '.ion-page-hidden, .ion-page-hidden *'; + /** * @internal */ @@ -6,7 +9,7 @@ export function getFromSupportedSelector( selector: SupportedSelectors, ): CypressIonicReturn { if (typeof selector === 'string') { - return filterOutHiddenPage(cy.get(`${selector}.hydrated`)); + return filterThenAssertHydrated(cy.get(selector)); } if (isJQuery(selector)) { @@ -16,21 +19,11 @@ export function getFromSupportedSelector( return filterThenAssertHydrated(selector as unknown as CypressIonicReturn); } -function filterOutHiddenPage( - subject: CypressIonicReturn, -): CypressIonicReturn { - return subject.not( - '.ion-page-hidden, .ion-page-hidden *', - ) as unknown as CypressIonicReturn; -} - function filterThenAssertHydrated( subject: CypressIonicReturn, ): CypressIonicReturn { return subject.then((elements) => { - const $visible = elements.not( - '.ion-page-hidden, .ion-page-hidden *', - ) as unknown as JQuery; + const $visible = elements.not(HIDDEN_PAGE_SELECTOR) as unknown as JQuery; if ($visible.length === 0) { return cy.wrap($visible); }