diff --git a/packages/react-router/test/base/src/pages/nested-params/NestedParams.tsx b/packages/react-router/test/base/src/pages/nested-params/NestedParams.tsx index a92f525306..58714b3ea4 100644 --- a/packages/react-router/test/base/src/pages/nested-params/NestedParams.tsx +++ b/packages/react-router/test/base/src/pages/nested-params/NestedParams.tsx @@ -63,6 +63,7 @@ const UserLayout: React.FC = () => { } /> } /> } /> + } /> @@ -84,6 +85,79 @@ const UserDetails: React.FC = () => { Go to Settings + + Go to Profile Edit + + + + ); +}; + +/** + * Deeply nested layout: /nested-params/user/:userId/profile/* + * This tests 4 levels of outlet nesting with relative paths to validate + * that derivePathnameToMatch correctly handles depth > 2 parent segments. + */ +const ProfileLayout: React.FC = () => { + const { userId } = useParams<{ userId: string }>(); + + return ( + + + + Profile Layout + + + + Profile layout user: {userId ?? 'missing'} + + } /> + } /> + } /> + + + + ); +}; + +const ProfileEdit: React.FC = () => { + const { userId } = useParams<{ userId: string }>(); + + return ( + + + + Profile Edit + + + + Profile edit user: {userId ?? 'missing'} + + Go to Profile View + + + Back to Details + + + + ); +}; + +const ProfileView: React.FC = () => { + const { userId } = useParams<{ userId: string }>(); + + return ( + + + + Profile View + + + + Profile view user: {userId ?? 'missing'} + + Back to Profile Edit + ); diff --git a/packages/react-router/test/base/tests/e2e/specs/nested-params.cy.js b/packages/react-router/test/base/tests/e2e/specs/nested-params.cy.js index 9ac4597487..35d7e1f7bc 100644 --- a/packages/react-router/test/base/tests/e2e/specs/nested-params.cy.js +++ b/packages/react-router/test/base/tests/e2e/specs/nested-params.cy.js @@ -222,6 +222,95 @@ describe('Nested Params', () => { }); }); + /** + * Deep nesting tests (4 levels) to validate derivePathnameToMatch + * handles relative paths correctly at depth > 2 parent segments. + * + * Route structure: + * /nested-params/* (App level) + * user/:userId/* (NestedParamsRoot outlet) + * details (UserLayout outlet) + * settings (UserLayout outlet) + * profile/* (UserLayout outlet) + * edit (ProfileLayout outlet - 4th level) + * view (ProfileLayout outlet - 4th level) + */ + + it('/nested-params > Deep nesting > Navigate to profile edit (4 levels deep)', () => { + cy.visit(`http://localhost:${port}/nested-params`); + cy.ionPageVisible('nested-params-landing'); + + // Navigate to user 42 details + cy.get('#go-to-user-42').click(); + cy.get('[data-testid="user-details-param"]').should('contain', 'Details view user: 42'); + + // Navigate to profile edit (4th level) + cy.get('#go-to-profile-edit').click(); + cy.get('[data-testid="profile-layout-param"]').should('contain', 'Profile layout user: 42'); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + cy.location('pathname').should('eq', '/nested-params/user/42/profile/edit'); + }); + + it('/nested-params > Deep nesting > Direct navigation to 4th level route', () => { + // Navigate directly to a deeply nested route + cy.visit(`http://localhost:${port}/nested-params/user/42/profile/edit`); + + cy.get('[data-testid="profile-layout-param"]').should('contain', 'Profile layout user: 42'); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + }); + + it('/nested-params > Deep nesting > Navigate between 4th level siblings (edit <-> view)', () => { + cy.visit(`http://localhost:${port}/nested-params/user/42/profile/edit`); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + + // Navigate to profile view (sibling at 4th level) + cy.get('#go-to-profile-view').click(); + cy.get('[data-testid="profile-view-param"]').should('contain', 'Profile view user: 42'); + cy.location('pathname').should('eq', '/nested-params/user/42/profile/view'); + + // Navigate back to profile edit + cy.get('#back-to-profile-edit').click(); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + cy.location('pathname').should('eq', '/nested-params/user/42/profile/edit'); + }); + + it('/nested-params > Deep nesting > Back navigation from 4th level to root', () => { + cy.visit(`http://localhost:${port}/`); + cy.ionPageVisible('home'); + + // Navigate to nested-params + cy.contains('ion-item', 'Nested Params').click(); + cy.ionPageVisible('nested-params-landing'); + + // Navigate to user 42 details + cy.get('#go-to-user-42').click(); + cy.get('[data-testid="user-details-param"]').should('contain', 'Details view user: 42'); + + // Navigate to profile edit (4th level) + cy.get('#go-to-profile-edit').click(); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + + // Navigate to profile view (sibling at 4th level) + cy.get('#go-to-profile-view').click(); + cy.get('[data-testid="profile-view-param"]').should('contain', 'Profile view user: 42'); + + // Browser back: view -> edit + cy.go('back'); + cy.get('[data-testid="profile-edit-param"]').should('contain', 'Profile edit user: 42'); + + // Browser back: edit -> details + cy.go('back'); + cy.get('[data-testid="user-details-param"]').should('contain', 'Details view user: 42'); + + // Browser back: details -> landing + cy.go('back'); + cy.ionPageVisible('nested-params-landing'); + + // Browser back: landing -> home + cy.go('back'); + cy.ionPageVisible('home'); + }); + it('/nested-params > Sibling route transitions > No nested scrollbars during transition', () => { // Start directly on the details page cy.visit(`http://localhost:${port}/nested-params/user/99/details`);