From 093c671e3e1733d76ba6760e76af8b934fe4b318 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 14 Nov 2023 18:04:09 -0500 Subject: [PATCH] test(angular): remove routing waits in tests (#28532) Issue number: N/A --------- ## What is the current behavior? Our Angular E2E tests are brittle because they rely on arbitrary `cy.wait` calls to account for asynchronous routing. This leads to flaky tests on CI and seemingly random test failures when we make adjustments to the Ionic Anguar routing integration (see: https://github.com/ionic-team/ionic-framework/pull/28188) Additionally, our test execution for the navigation tests is quite slow because transitions are enabled. As a result, we need to wait hundreds of ms per test just for the transitions to finish. ## What is the new behavior? - Updated the `testStack` command to use a new `getStack` [Cypress query](https://docs.cypress.io/api/cypress-api/custom-queries). These queries come with automatic retrying built-in. By leveraging this query in the `testStack` command, we can avoid the arbitrary waits. - Added `ionic:_testing=true` query strings to the navigation tests. This causes Ionic to disable any transitions so the tests execute faster. - Removed most of the arbitrary `cy.wait` calls. I kept the swipe to go back `cy.wait` -- I wasn't quite sure how to reduce flakiness on that one. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --- .../test/base/cypress/support/commands.js | 18 ++++++++++++++---- .../test/base/e2e/src/lazy/navigation.spec.ts | 4 +--- .../base/e2e/src/lazy/nested-outlet.spec.ts | 2 +- .../test/base/e2e/src/lazy/router-link.spec.ts | 8 +------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/angular/test/base/cypress/support/commands.js b/packages/angular/test/base/cypress/support/commands.js index 888a1ff7477..65436721fcc 100644 --- a/packages/angular/test/base/cypress/support/commands.js +++ b/packages/angular/test/base/cypress/support/commands.js @@ -41,11 +41,21 @@ Cypress.Commands.add('ionSwipeToGoBack', (complete = false, selector = 'ion-rout cy.wait(150); }); +/** + * getStack is a query because it has automatic + * retries built in which will let us account for + * async routing without having to use + * arbitrary cy.wait calls. + */ +Cypress.Commands.addQuery('getStack', (selector) => { + return () => { + const el = cy.$$(selector); + return Array.from(el.children()).map((el) => el.tagName.toLowerCase()); + } +}); + Cypress.Commands.add('testStack', (selector, expected) => { - cy.document().then((doc) => { - const children = Array.from(doc.querySelector(selector).children).map((el) => el.tagName.toLowerCase()); - expect(children).to.deep.equal(expected); - }); + cy.getStack(selector).should('deep.equal', expected); }); Cypress.Commands.add('testLifeCycle', (selector, expected) => { diff --git a/packages/angular/test/base/e2e/src/lazy/navigation.spec.ts b/packages/angular/test/base/e2e/src/lazy/navigation.spec.ts index 17c1f93a288..c7d06d6b1fd 100644 --- a/packages/angular/test/base/e2e/src/lazy/navigation.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/navigation.spec.ts @@ -1,11 +1,9 @@ describe('Navigation', () => { beforeEach(() => { - cy.visit('/lazy/navigation'); + cy.visit('/lazy/navigation/page1?ionic:_testing=true'); }) it('should navigate correctly', () => { - cy.visit('/lazy/navigation/page1'); - cy.wait(2000); cy.testStack('ion-router-outlet', ['app-navigation-page2', 'app-navigation-page1']); cy.get('app-navigation-page2').should('have.attr', 'aria-hidden').and('equal', 'true'); diff --git a/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts b/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts index 786b2b44efe..f35c73635e1 100644 --- a/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/nested-outlet.spec.ts @@ -1,6 +1,6 @@ describe('Nested Outlet', () => { beforeEach(() => { - cy.visit('/lazy/nested-outlet/page'); + cy.visit('/lazy/nested-outlet/page?ionic:_testing=true'); }) it('should navigate correctly', () => { diff --git a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts index 5ca72825f51..5550d6326fc 100644 --- a/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/router-link.spec.ts @@ -1,6 +1,6 @@ describe('Router Link', () => { beforeEach(() => { - cy.visit('/lazy/router-link'); + cy.visit('/lazy/router-link?ionic:_testing=true'); }); describe('router-link params and fragments', () => { @@ -9,7 +9,6 @@ describe('Router Link', () => { const id = 'MyPageID=='; it('should go to a page with properly encoded values', () => { - cy.visit('/lazy/router-link?ionic:_testing=true'); cy.get('#queryParamsFragment').click(); const expectedPath = `${encodeURIComponent(id)}`; @@ -24,7 +23,6 @@ describe('Router Link', () => { }); it('should return to a page with preserved query param and fragment', () => { - cy.visit('/lazy/router-link?ionic:_testing=true'); cy.get('#queryParamsFragment').click(); cy.get('#goToPage3').click(); @@ -148,7 +146,6 @@ function testForward() { } function testRoot() { - cy.wait(200); cy.testStack('ion-router-outlet', ['app-router-link-page']); cy.testLifeCycle('app-router-link-page', { ionViewWillEnter: 1, @@ -159,7 +156,6 @@ function testRoot() { cy.get('app-router-link-page #canGoBack').should('have.text', 'false'); cy.go('back'); - cy.wait(100); cy.testStack('ion-router-outlet', ['app-router-link']); cy.testLifeCycle('app-router-link', { ionViewWillEnter: 1, @@ -170,7 +166,6 @@ function testRoot() { } function testBack() { - cy.wait(500); cy.testStack('ion-router-outlet', ['app-router-link-page']); cy.testLifeCycle('app-router-link-page', { ionViewWillEnter: 1, @@ -181,7 +176,6 @@ function testBack() { cy.get('app-router-link-page #canGoBack').should('have.text', 'false'); cy.go('back'); - cy.wait(100); cy.testStack('ion-router-outlet', ['app-router-link']); cy.testLifeCycle('app-router-link', { ionViewWillEnter: 1,