Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: remove reporter logs for collapsed tests in run mode #25632

Merged
merged 11 commits into from
Jan 31, 2023
6 changes: 5 additions & 1 deletion cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ _Released 01/31/2023 (PENDING)_
- Fixed an issue where alternative Microsoft Edge Beta, Canary, and Dev binary versions were not being discovered by Cypress.
Fixes [#25455](https://github.com/cypress-io/cypress/issues/25455).

**Performance:**

- Improved memory consumption in `run` mode by removing reporter logs for successful tests.
mschile marked this conversation as resolved.
Show resolved Hide resolved
Fixes [#25230](https://github.com/cypress-io/cypress/issues/25230).

**Dependency Updates:**

- Upgraded [`underscore.string`](https://github.com/esamattis/underscore.string/blob/HEAD/CHANGELOG.markdown) from `3.3.5` to `3.3.6` to reference rebuilt assets after security patch to fix regular expression DDOS exploit.
Fixed in [#25574](https://github.com/cypress-io/cypress/pull/25574).

## 12.4.1

Expand Down
2 changes: 2 additions & 0 deletions packages/driver/cypress/support/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { $ } = Cypress

const isActuallyInteractive = Cypress.config('isInteractive')

Cypress.config('isActuallyInteractive', isActuallyInteractive)

window.top.__cySkipValidateConfig = true

if (!isActuallyInteractive) {
Expand Down
2 changes: 1 addition & 1 deletion packages/driver/src/cypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ class $Cypress {

// this event is how the reporter knows how to display
// stats and runnable properties such as errors
this.emit('test:after:run', ...args)
this.emit('test:after:run', args[0], this.config('isInteractive'))
mschile marked this conversation as resolved.
Show resolved Hide resolved
this.maybeEmitCypressInCypress('mocha', 'test:after:run', args[0])

if (this.config('isTextTerminal')) {
Expand Down
234 changes: 234 additions & 0 deletions packages/reporter/cypress/e2e/memory.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import { EventEmitter } from 'events'
import { RootRunnable } from '../../src/runnables/runnables-store'
import { MobxRunnerStore } from '@packages/app/src/store/mobx-runner-store'

let runner: EventEmitter
let runnables: RootRunnable
const { _ } = Cypress

function visitAndRenderReporter (studioEnabled: boolean = false, studioActive: boolean = false) {
cy.fixture('runnables_memory').then((_runnables) => {
runnables = _runnables
})

runner = new EventEmitter()

const runnerStore = new MobxRunnerStore('e2e')

runnerStore.setSpec({
name: 'foo.js',
relative: 'relative/path/to/foo.js',
absolute: '/absolute/path/to/foo.js',
})

cy.visit('/').then((win) => {
win.render({
studioEnabled,
runner,
runnerStore,
})
})

cy.get('.reporter').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', { studioActive })
})

return runnerStore
}

describe('tests', () => {
beforeEach(() => {
visitAndRenderReporter()
})

context('run mode', () => {
beforeEach(() => {
_.each(runnables.suites, (suite) => {
_.each(suite.tests, (test) => {
runner.emit('test:after:run', test, false)
})
})
})

it('clears logs for a collapsed test', () => {
cy.contains('passed')
.as('passed')
.closest('.runnable')
.should('have.class', 'test')
.find('.runnable-instruments').should('not.exist')

cy.get('@passed').click()

cy.get('@passed')
.parents('.collapsible').first()
.find('.attempt-item').eq(0)
.contains('No commands were issued in this test.')

cy.percySnapshot()
})

it('retains logs for an expanded test', () => {
cy.contains('failed')
.parents('.collapsible').first()
.should('have.class', 'is-open')
.find('.collapsible-content')
.should('be.visible')

cy.contains('failed')
.parents('.collapsible').first()
.find('.attempt-item')
.eq(0)
.find('.attempt-1')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.percySnapshot()
})

it('retains logs for failed attempt and clears logs for passed attempt after retry', () => {
cy.contains('passed after retry')
.parents('.collapsible').first()
.should('not.have.class', 'is-open')
.find('.collapsible-content')
.should('not.exist')

cy.contains('passed after retry').click()

cy.contains('passed after retry')
.parents('.collapsible').first()
.find('.attempt-item').as('attempts')

cy.get('@attempts').eq(0).as('firstAttempt')
.find('.collapsible')
.should('not.have.class', 'is-open')
.find('.collapsible-indicator').should('not.exist')

cy.get('@firstAttempt')
.contains('Attempt 1')
.click()

cy.get('@firstAttempt')
.find('.attempt-1')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.get('@attempts').eq(1).as('secondAttempt')
.find('.collapsible')
.should('have.class', 'is-open')
.find('.collapsible-indicator').should('not.exist')

cy.get('@secondAttempt')
.contains('No commands were issued in this test.')

cy.percySnapshot()
})

it('retains logs for failed attempts', () => {
cy.contains('failed with retries')
.parents('.collapsible').first()
.find('.attempt-item').as('attempts')

cy.get('@attempts').eq(0).as('firstAttempt')
.find('.collapsible')
.should('not.have.class', 'is-open')
.find('.collapsible-indicator').should('not.exist')

cy.get('@firstAttempt')
.contains('Attempt 1')
.click()

cy.get('@firstAttempt')
.find('.attempt-1')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.get('@attempts').eq(1).as('secondAttempt')
.find('.collapsible')
.should('have.class', 'is-open')
.find('.collapsible-content')
.should('be.visible')

cy.get('@secondAttempt')
.find('.attempt-2')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.contains('failed with retries')
.scrollIntoView()
.percySnapshot()
})
})

context('open mode', () => {
beforeEach(() => {
_.each(runnables.suites, (suite) => {
_.each(suite.tests, (test) => {
runner.emit('test:after:run', test, true)
})
})
})

it('retains logs for a collapsed test', () => {
cy.contains('passed')
.as('passed')
.closest('.runnable')
.should('have.class', 'test')
.find('.runnable-instruments').should('not.exist')

cy.get('@passed').click()

cy.get('@passed')
.parents('.collapsible').first()
.find('.attempt-item')
.eq(0)
.find('.attempt-1')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.percySnapshot()
})

it('retains logs for an expanded test', () => {
cy.contains('failed')
.parents('.collapsible').first()
.should('have.class', 'is-open')
.find('.collapsible-content')
.should('be.visible')

cy.contains('failed')
.parents('.collapsible').first()
.find('.attempt-item')
.eq(0)
.find('.attempt-1')
.within(() => {
cy.get('.sessions-container')
cy.get('.runnable-agents-region')
cy.get('.runnable-routes-region')
cy.get('.runnable-commands-region')
})

cy.percySnapshot()
})
})
})
12 changes: 6 additions & 6 deletions packages/reporter/cypress/e2e/unit/test_model.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('Test model', () => {

command.isLongRunning = true

test.finish({} as UpdatableTestProps)
test.finish({} as UpdatableTestProps, false)
expect(test.isLongRunning).to.be.false
})
})
Expand Down Expand Up @@ -282,37 +282,37 @@ describe('Test model', () => {
it('sets the test as inactive', () => {
const test = createTest()

test.finish({} as UpdatableTestProps)
test.finish({} as UpdatableTestProps, false)
expect(test.isActive).to.be.false
})

it('updates the state of the test', () => {
const test = createTest()

test.finish({ state: 'failed' } as UpdatableTestProps)
test.finish({ state: 'failed' } as UpdatableTestProps, false)
expect(test.state).to.equal('failed')
})

it('updates the test err', () => {
const test = createTest()

test.finish({ err: { name: 'SomeError' } as Err } as UpdatableTestProps)
test.finish({ err: { name: 'SomeError' } as Err } as UpdatableTestProps, false)
expect(test.err.name).to.equal('SomeError')
})

it('sets the hook to failed if it exists', () => {
const test = createTest({ hooks: [{ hookId: 'h1', hookName: 'before each' }] })

test.addLog(createCommand({ instrument: 'command' }))
test.finish({ failedFromHookId: 'h1', err: { message: 'foo' } as Err } as UpdatableTestProps)
test.finish({ state: 'failed', failedFromHookId: 'h1', err: { message: 'foo' } as Err } as UpdatableTestProps, false)
expect(test.lastAttempt.hooks[1].failed).to.be.true
})

it('does not throw error if hook does not exist', () => {
const test = createTest()

expect(() => {
test.finish({ hookId: 'h1' } as UpdatableTestProps)
test.finish({ hookId: 'h1' } as UpdatableTestProps, false)
}).not.to.throw()
})
})
Expand Down
Loading