Skip to content

Commit

Permalink
fix: regression where failed events could cause a passing test to dis…
Browse files Browse the repository at this point in the history
…play as failed (#15037)
  • Loading branch information
panzarino committed Feb 15, 2021
1 parent 484f1eb commit b4c0117
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 14 deletions.
14 changes: 13 additions & 1 deletion packages/reporter/cypress/integration/unit/test_model_spec.ts
Expand Up @@ -29,7 +29,6 @@ const createCommand = (props: Partial<CommandProps> = {}) => {
testId: 'r3',
timeout: 4000,
wallClockStartedAt: new Date().toString(),

} as CommandProps

return _.defaults(props, defaults)
Expand Down Expand Up @@ -262,6 +261,19 @@ describe('Test model', () => {
test.updateLog(createCommand({ timeout: 6000 }))
expect(test.lastAttempt.commands[0].timeout).to.equal(6000)
})

// https://github.com/cypress-io/cypress/issues/14978
it('does not change test state based on log state', () => {
const test = createTest()

test.addLog(createCommand({ state: 'active' }))
expect(test.lastAttempt.commands[0].state).to.equal('active')
expect(test.state).to.equal('processing')

test.updateLog(createCommand({ state: 'failed' }))
expect(test.lastAttempt.commands[0].state).to.equal('failed')
expect(test.state).to.equal('processing')
})
})

context('#removeLog', () => {
Expand Down
6 changes: 1 addition & 5 deletions packages/reporter/src/attempts/attempt-model.ts
Expand Up @@ -109,15 +109,11 @@ export default class Attempt {
}
}

@action updateLog (props: LogProps) {
updateLog (props: LogProps) {
const log = this._logs[props.id]

if (log) {
log.update(props)

if (log.state === 'failed') {
this._state = 'failed'
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/reporter/src/test/test-model.ts
Expand Up @@ -168,7 +168,7 @@ export default class Test extends Runnable {
}
}

if (props.err) {
if (props.err || props.state) {
this._withAttempt(this.currentRetry, (attempt: Attempt) => {
attempt.update(props)
})
Expand Down
13 changes: 13 additions & 0 deletions packages/runner/cypress/fixtures/studio/error_hooks_spec.js
@@ -0,0 +1,13 @@
describe('suite', () => {
beforeEach(() => {
cy.visit('the://url')

cy.get('body').then(() => {
throw new Error('Failing Test')
})
})

it('test', () => {
cy.get('body')
})
})
11 changes: 11 additions & 0 deletions packages/runner/cypress/fixtures/studio/error_test_spec.js
@@ -0,0 +1,11 @@
describe('suite', () => {
beforeEach(() => {
cy.visit('the://url')
})

it('test', () => {
cy.get('body').then(() => {
throw new Error('Failing Test')
})
})
})
75 changes: 75 additions & 0 deletions packages/runner/cypress/integration/studio.ui.spec.js
Expand Up @@ -42,6 +42,7 @@ describe('studio ui', () => {
cy.get('.runner').find('.url').should('have.value', '')

cy.get('.runner').find('.url-menu').should('be.visible')
cy.get('.runner').find('.url-menu').find('.btn-submit').should('be.disabled')

cy.percySnapshot()
})
Expand All @@ -63,6 +64,25 @@ describe('studio ui', () => {
})
})

// doesn't actually test visiting, just ui state
it('allows user to visit inputted url and prompts for interaction after visit', () => {
runIsolatedCypress('cypress/fixtures/studio/basic_spec.js', {
config: {
baseUrl: null,
},
state: {
studioTestId: 'r5',
},
})
.then(() => {
cy.get('.runner').find('.url').type('the://url')
cy.get('.runner').find('.url-menu').find('.btn-submit').click()

cy.get('.reporter').contains('the://url').closest('.command-wrapper-text').contains('visit')
cy.get('.reporter').contains('Interact with your site to add test commands.')
})
})

it('displays modal when available commands is clicked', () => {
runIsolatedCypress('cypress/fixtures/studio/basic_spec.js', {
state: {
Expand All @@ -76,4 +96,59 @@ describe('studio ui', () => {
cy.get('reach-portal').should('not.exist')
})
})

describe('error state', () => {
it('displays error state when extending a failed test', () => {
runIsolatedCypress('cypress/fixtures/studio/error_test_spec.js', {
state: {
studioTestId: 'r3',
},
})
.then(() => {
cy.get('.reporter').contains('test').closest('.runnable').should('have.class', 'runnable-failed')
cy.get('.reporter').contains('Studio cannot add commands to a failing test.').should('exist')

cy.get('.runner').find('.iframes-container').should('have.class', 'studio-is-failed')

cy.percySnapshot()
})
})

it('displays error state when a before hook fails', () => {
runIsolatedCypress('cypress/fixtures/studio/error_hooks_spec.js', {
state: {
studioTestId: 'r3',
},
})
.then(() => {
cy.get('.reporter').contains('test').closest('.runnable').should('have.class', 'runnable-failed')
cy.get('.reporter').contains('Studio cannot add commands to a failing test.').should('exist')

cy.get('.runner').find('.iframes-container').should('have.class', 'studio-is-failed')
})
})

it('displays error state when cy.visit() fails on user inputted url', () => {
runIsolatedCypress('cypress/fixtures/studio/basic_spec.js', {
config: {
baseUrl: null,
},
state: {
studioTestId: 'r5',
},
visitUrl: 'http://localhost:3500/foo',
visitSuccess: false,
})
.then(() => {
cy.get('.runner').find('.url').type('the://url')
cy.get('.runner').find('.url-menu').find('.btn-submit').click()

cy.get('.reporter').contains('test 3').closest('.runnable').should('have.class', 'runnable-failed')
cy.get('.reporter').contains('the://url').closest('.command-wrapper-text').contains('visit')
cy.get('.reporter').contains('Studio cannot add commands to a failing test.').should('exist')

cy.get('.runner').find('.iframes-container').should('have.class', 'studio-is-failed')
})
})
})
})
3 changes: 2 additions & 1 deletion packages/runner/cypress/support/helpers.js
Expand Up @@ -106,6 +106,7 @@ function createCypress (defaultOptions = {}) {
config: { video: false },
onBeforeRun () {},
visitUrl: 'http://localhost:3500/fixtures/dom.html',
visitSuccess: true,
})

return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js')
Expand Down Expand Up @@ -254,7 +255,7 @@ function createCypress (defaultOptions = {}) {

.withArgs('backend:request', 'resolve:url')
.yieldsAsync({ response: {
isOkStatusCode: true,
isOkStatusCode: opts.visitSuccess,
isHtml: true,
url: opts.visitUrl,
} })
Expand Down
21 changes: 15 additions & 6 deletions packages/runner/src/lib/event-manager.js
Expand Up @@ -388,19 +388,15 @@ const eventManager = {
Cypress.on('log:added', (log) => {
const displayProps = Cypress.runner.getDisplayPropsForLog(log)

if (studioRecorder.isActive) {
displayProps.hookId = studioRecorder.hookId
}
this._interceptStudio(displayProps)

reporterBus.emit('reporter:log:add', displayProps)
})

Cypress.on('log:changed', (log) => {
const displayProps = Cypress.runner.getDisplayPropsForLog(log)

if (studioRecorder.isActive) {
displayProps.hookId = studioRecorder.hookId
}
this._interceptStudio(displayProps)

reporterBus.emit('reporter:log:state:changed', displayProps)
})
Expand Down Expand Up @@ -556,6 +552,19 @@ const eventManager = {
}
},

_interceptStudio (displayProps) {
if (studioRecorder.isActive) {
displayProps.hookId = studioRecorder.hookId

if (displayProps.name === 'visit' && displayProps.state === 'failed') {
studioRecorder.testFailed()
reporterBus.emit('test:set:state', studioRecorder.testError, _.noop)
}
}

return displayProps
},

emit (event, ...args) {
localBus.emit(event, ...args)
},
Expand Down
7 changes: 7 additions & 0 deletions packages/runner/src/studio/studio-recorder.js
Expand Up @@ -68,6 +68,13 @@ export class StudioRecorder {
return this.isActive && !this.url && !this.isFailed
}

@computed get testError () {
return {
id: this.testId,
state: 'failed',
}
}

@computed get saveError () {
return {
id: this.testId,
Expand Down

4 comments on commit b4c0117

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4c0117 Feb 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.5.0/circle-develop-b4c0117e89d9e83441c0b9db5aefefbd682a6804/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4c0117 Feb 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppVeyor has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.5.0/appveyor-develop-b4c0117e89d9e83441c0b9db5aefefbd682a6804/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4c0117 Feb 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppVeyor has built the win32 ia32 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.5.0/appveyor-develop-b4c0117e89d9e83441c0b9db5aefefbd682a6804/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on b4c0117 Feb 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.5.0/circle-develop-b4c0117e89d9e83441c0b9db5aefefbd682a6804/cypress.tgz

Please sign in to comment.