diff --git a/packages/react-router/src/ReactRouter/IonRouter.tsx b/packages/react-router/src/ReactRouter/IonRouter.tsx index fcadd6561a6..6faee1e3a50 100644 --- a/packages/react-router/src/ReactRouter/IonRouter.tsx +++ b/packages/react-router/src/ReactRouter/IonRouter.tsx @@ -107,7 +107,8 @@ class IonRouterInner extends React.PureComponent { } const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search; - if (leavingUrl !== location.pathname) { + const currentUrl = location.pathname + (location.search || ''); + if (leavingUrl !== currentUrl) { if (!this.incomingRouteParams) { if (action === 'REPLACE') { this.incomingRouteParams = { diff --git a/packages/react-router/test/base/tests/e2e/specs/routing.cy.js b/packages/react-router/test/base/tests/e2e/specs/routing.cy.js index fd28ee573c0..258e528a364 100644 --- a/packages/react-router/test/base/tests/e2e/specs/routing.cy.js +++ b/packages/react-router/test/base/tests/e2e/specs/routing.cy.js @@ -344,6 +344,25 @@ describe('Routing Tests', () => { cy.get('div.ion-page[data-pageid=home-details-page-1] [data-testid="details-input"]').should('have.value', '1'); }); + it('Details 1 with Query Params > pop a same-URL history entry, should stay on details 1', () => { + // Regression: popstate over a same-URL entry on a search-bearing route used to trigger a false POP transition. + cy.visit(`http://localhost:${port}/routing`); + cy.ionNav('ion-item', 'Details 1 with Query Params'); + cy.ionPageVisible('home-details-page-1'); + cy.location('search').should('eq', '?hello=there'); + + cy.window().then((win) => { + win.history.pushState({ marker: true }, '', win.location.href); + }); + + cy.go('back'); + + // No teleport: still on details 1, URL unchanged. + cy.ionPageVisible('home-details-page-1'); + cy.location('pathname').should('eq', '/routing/tabs/home/details/1'); + cy.location('search').should('eq', '?hello=there'); + }); + /* Tests to add: Test that lifecycle events fire