diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 7443b288e350..e81b15b05b85 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -1,4 +1,12 @@ +## 14.0.1 + +_Released 1/28/2024 (PENDING)_ + +**Bugfixes:** + +- Actions performed in `after` hooks, like `.click()` and `.type()` will now correctly retry and perform the action when a test fails. Fixes [#2831](https://github.com/cypress-io/cypress/issues/2831). + ## 14.0.0 _Released 1/7/2024 (PENDING)_ diff --git a/packages/driver/cypress/e2e/commands/actions/click.cy.js b/packages/driver/cypress/e2e/commands/actions/click.cy.js index 5e909247cd38..0ef45d4962c6 100644 --- a/packages/driver/cypress/e2e/commands/actions/click.cy.js +++ b/packages/driver/cypress/e2e/commands/actions/click.cy.js @@ -1723,6 +1723,52 @@ describe('src/cy/commands/actions/click', () => { cy.get('#dom').invoke('css', 'scrollBehavior').then((scrollBehavior) => expect(scrollBehavior).to.eq('smooth')) }) }) + + describe('retries in after hook when failures', () => { + it('clicks element in hook', (done) => { + cy.on('fail', (err) => { + expect(err.message).contain('expected true to be false') + done() + }) + + expect(true).to.be.false + }) + + after(() => { + const onClick = cy.stub() + + const $button = cy.$$('#button') + + $button.on('click', onClick) + + cy.get('#button').click().then(() => { + expect(onClick).to.be.calledOnce + }) + }) + }) + + describe('retries in afterEach hook when failures', () => { + it('clicks element in hook', (done) => { + cy.on('fail', (err) => { + expect(err.message).contain('expected true to be false') + done() + }) + + expect(true).to.be.false + }) + + afterEach(() => { + const onClick = cy.stub() + + const $button = cy.$$('#button') + + $button.on('click', onClick) + + cy.get('#button').click().then(() => { + expect(onClick).to.be.calledOnce + }) + }) + }) }) describe('assertion verification', () => { diff --git a/packages/driver/cypress/e2e/commands/actions/type.cy.js b/packages/driver/cypress/e2e/commands/actions/type.cy.js index a750f673e0cb..a8c13ee329eb 100644 --- a/packages/driver/cypress/e2e/commands/actions/type.cy.js +++ b/packages/driver/cypress/e2e/commands/actions/type.cy.js @@ -478,6 +478,52 @@ describe('src/cy/commands/actions/type - #type', () => { cy.get(':text:first').type('foo', { scrollBehavior: false, timeout: 200 }) }) + + describe('retries in after hook when failures', () => { + it('types in element in hook', (done) => { + cy.on('fail', (err) => { + expect(err.message).contain('expected true to be false') + done() + }) + + expect(true).to.be.false + }) + + after(() => { + const input = cy.$$('input:text:first') + + input.val('') + + expect(input).to.have.value('') + + cy.get('input:text:first').type('foo').then(($input) => { + expect($input).to.have.value('foo') + }) + }) + }) + + describe('retries in afterEach hook when failures', () => { + it('types in element in hook', (done) => { + cy.on('fail', (err) => { + expect(err.message).contain('expected true to be false') + done() + }) + + expect(true).to.be.false + }) + + afterEach(() => { + const input = cy.$$('input:text:first') + + input.val('') + + expect(input).to.have.value('') + + cy.get('input:text:first').type('foo').then(($input) => { + expect($input).to.have.value('foo') + }) + }) + }) }) describe('input types where no extra formatting required', () => { diff --git a/packages/driver/src/cy/retries.ts b/packages/driver/src/cy/retries.ts index 7ad67b052828..2cfedf637dce 100644 --- a/packages/driver/src/cy/retries.ts +++ b/packages/driver/src/cy/retries.ts @@ -105,8 +105,7 @@ export const create = (Cypress: ICypress, state: StateFunc, timeout: $Cy['timeou const ended = () => { // we should NOT retry if // 1. our promise has been canceled - // 2. or we have an error - // 3. or if the runnables has changed + // 2. or if the runnables has changed // although bluebird SHOULD cancel these retries // since they're all connected - apparently they @@ -116,7 +115,7 @@ export const create = (Cypress: ICypress, state: StateFunc, timeout: $Cy['timeou // bug in bluebird with not propagating cancellations // fast enough in a series of promises // https://github.com/petkaantonov/bluebird/issues/1424 - return state('canceled') || state('error') || runnableHasChanged() + return state('canceled') || runnableHasChanged() } return Promise