@revoloo/cypress6
Version:
Cypress.io end to end testing tool
373 lines (304 loc) • 12.4 kB
text/typescript
import { EventEmitter } from 'events'
import { RootRunnable } from '../../src/runnables/runnables-store'
import { addCommand } from '../support/utils'
describe('commands', () => {
let runner: EventEmitter
let runnables: RootRunnable
const inProgressStartedAt = (new Date(2000, 0, 1)).toISOString()
beforeEach(() => {
cy.fixture('runnables_commands').then((_runnables) => {
runnables = _runnables
})
runner = new EventEmitter()
cy.visit('/').then((win) => {
win.render({
runner,
spec: {
name: 'foo',
absolute: '/foo/bar',
relative: 'foo/bar',
},
})
})
cy.get('.reporter').then(() => {
runner.emit('runnables:ready', runnables)
runner.emit('reporter:start', {})
addCommand(runner, {
id: 9,
name: 'get',
message: '#in-progress',
state: 'pending',
timeout: 4000,
wallClockStartedAt: inProgressStartedAt,
})
})
cy.contains('http://localhost:3000') // ensure test content has loaded
// ensure the page is loaded before proceeding
// this makes visual snapshots stable
cy.get('.focus-tests-text').should('be.visible')
})
it('displays all the commands', () => {
cy.get('.command').should('have.length', 9)
cy.percySnapshot()
})
it('includes the type class', () => {
cy.contains('#exists').closest('.command')
.should('have.class', 'command-type-parent')
cy.contains('#doesnt-exist').closest('.command')
.should('have.class', 'command-type-child')
})
it('includes the name class', () => {
cy.contains('#exists').closest('.command')
.should('have.class', 'command-name-get')
})
it('includes the state class', () => {
cy.contains('#exists').closest('.command')
.should('have.class', 'command-state-passed')
cy.contains('#doesnt-exist').closest('.command')
.should('have.class', 'command-state-failed')
cy.contains('#in-progress').closest('.command')
.should('have.class', 'command-state-pending')
})
it('displays the number', () => {
cy.contains('http://localhost:3000').closest('.command-message').siblings('.command-number')
.should('have.text', '1')
cy.contains('#exists').closest('.command-message').siblings('.command-number')
.should('have.text', '2')
cy.contains('#doesnt-exist').closest('.command-message').siblings('.command-number')
.should('have.text', '3')
cy.contains('.some-els').closest('.command-message').siblings('.command-number')
.should('have.text', '4')
})
it('events have is-event class, no number, and type in parentheses', () => {
cy.contains('GET ---').closest('.command')
.should('have.class', 'command-is-event')
cy.contains('GET ---').closest('.command-message').siblings('.command-number')
.should('have.text', '')
cy.contains('GET ---').closest('.command-message').siblings('.command-method')
.should('have.text', '(xhr stub)')
})
it('includes the scaled class when the message is over 100 chars', () => {
cy.contains('Lorem ipsum').closest('.command')
.should('have.class', 'command-scaled')
})
it('does not render with the scaled class when the message is less than 100 chars', () => {
cy.contains('#exists').closest('.command')
.should('not.have.class', 'command-scaled')
})
it('renders markdown in message', () => {
cy.contains('Lorem ipsum').closest('.command').find('.command-message').within(() => {
cy.get('strong').should('have.text', 'dolor')
cy.get('em').should('have.text', 'sit')
})
})
it('shows indicator when specified', () => {
cy.contains('GET ---').closest('.command').find('.command-message .fa-circle')
.should('be.visible')
})
it('includes the renderProps indicator as a class name when specified', () => {
cy.contains('Lorem ipsum').closest('.command').find('.command-message .fa-circle')
.should('have.class', 'bad')
})
describe('progress bar', () => {
const getProgress = () => {
return cy.contains('#in-progress')
.closest('.command')
.find('.command-progress span')
}
it('calculates correct scale factor', () => {
// take the wallClockStartedAt of this command and add 2500 milliseconds to it
// in order to simulate the command having run for 2.5 seconds of the total 4000 timeout
const date = new Date(inProgressStartedAt).setMilliseconds(2500)
cy.clock(date, ['Date'])
// close and open tests so it freshly mounts
cy.contains('test 1').click().click()
getProgress().should(($span) => {
expect($span.attr('style')).to.contain('animation-duration: 1500ms')
expect($span.attr('style')).to.contain('transform: scaleX(0.375)')
// ensures that actual scale factor hits 0 within default timeout
// this matrix is equivalent to scaleX(0)
expect($span).to.have.css('transform', 'matrix(0, 0, 0, 1, 0, 0)')
})
})
it('recalculates correct scale factor after being closed', () => {
// take the wallClockStartedAt of this command and add 1000 milliseconds to it
// in order to simulate the command having run for 1 second of the total 4000 timeout
const date = new Date(inProgressStartedAt).setMilliseconds(1000)
cy.clock(date, ['Date'])
// close and open tests so it freshly mounts
cy.contains('test').click().click()
getProgress().should(($span) => {
expect($span.attr('style')).to.contain('animation-duration: 3000ms')
expect($span.attr('style')).to.contain('transform: scaleX(0.75)')
})
// set the clock ahead as if time has passed
cy.tick(2000)
cy.contains('test 1').click().click()
getProgress().should(($span) => {
expect($span.attr('style')).to.contain('animation-duration: 1000ms')
expect($span.attr('style')).to.contain('transform: scaleX(0.25)')
})
})
})
context('invisible indicator', () => {
it('does not display invisible icon when visible', () => {
cy.contains('#exists').closest('.command').find('.command-invisible')
.should('not.be.visible')
})
it('displays invisible icon when not visible', () => {
cy.contains('#doesnt-exist').closest('.command').find('.command-invisible')
.should('be.visible')
})
it('displays a tooltip when hovering', () => {
cy.contains('#doesnt-exist').closest('.command').find('.command-invisible').trigger('mouseover')
cy.get('.cy-tooltip')
.should('be.visible')
.should('have.text', 'This element is not visible.')
})
it('displays different text when multiple elements', () => {
cy.contains('.invisible').closest('.command').find('.command-invisible').trigger('mouseover')
cy.get('.cy-tooltip')
.should('be.visible')
.should('have.text', 'One or more matched elements are not visible.')
})
})
context('elements indicator', () => {
it('shows number of elements when 0 or greater than 1', () => {
cy.contains('#doesnt-exist').closest('.command').find('.num-elements')
.should('be.visible').and('have.text', '0')
cy.contains('.some-els').closest('.command').find('.num-elements')
.should('be.visible').and('have.text', '4')
})
it('does not show number of elements when 0', () => {
cy.contains('#exists').closest('.command').find('.num-elements')
.should('not.be.visible')
})
it('renders a tooltip when hovering', () => {
cy.contains('.some-els').closest('.command').find('.num-elements').trigger('mouseover')
cy.get('.cy-tooltip').should('be.visible').should('have.text', '4 matched elements')
})
})
context('duplicates', () => {
it('collapses consecutive duplicate events into one', () => {
cy.get('.command-name-xhr').should('have.length', 3)
})
it('displays number of duplicates', () => {
cy.contains('GET --- /dup').closest('.command').find('.num-duplicates')
.should('have.text', '4')
})
it('expands all events after clicking arrow', () => {
cy.contains('GET --- /dup').closest('.command').find('.command-expander').click()
cy.get('.command-name-xhr').should('have.length', 6)
cy.contains('GET --- /dup').closest('.command').find('.duplicates')
.should('be.visible')
.find('.command').should('have.length', 3)
})
})
context('clicking', () => {
it('pins the command', () => {
cy.contains('#exists').click()
.closest('.command')
.should('have.class', 'command-is-pinned')
})
it('shows a tooltip', () => {
cy.contains('#exists').click()
cy.get('.cy-tooltip').should('have.text', 'Printed output to your console')
})
it('tooltip disappears after 1500ms', () => {
cy.clock()
cy.contains('#exists').click()
cy.tick(1500)
cy.get('.cy-tooltip').should('not.exist')
})
it('emits runner:console:log', () => {
cy.spy(runner, 'emit')
cy.contains('#exists').click()
cy.wrap(runner.emit).should('be.calledWith', 'runner:console:log', 2)
})
it('shows the snapshot', () => {
cy.spy(runner, 'emit')
cy.contains('#exists').click()
cy.wrap(runner.emit).should('be.calledWith', 'runner:show:snapshot', 2)
})
it('unpins after clicking again, does not emit runner:console:log again', () => {
cy.spy(runner, 'emit')
cy.contains('#exists').click()
cy.contains('#exists').click()
// @ts-ignore
cy.wrap(runner.emit.withArgs('runner:console:log')).should('be.calledOnce')
})
it('unpins after clicking another command, pins that one', () => {
cy.spy(runner, 'emit')
cy.contains('#exists').click()
cy.contains('#doesnt-exist').click()
cy.contains('#exists').closest('.command')
.should('not.have.class', 'command-is-pinned')
cy.contains('#doesnt-exist').closest('.command')
.should('have.class', 'command-is-pinned')
})
})
context('mousing over', () => {
beforeEach(() => {
cy.spy(runner, 'emit')
cy.clock()
cy.get('.command').first().trigger('mouseover')
})
it('shows snapshot after 50ms passes', () => {
cy.wrap(runner.emit).should('not.be.calledWith', 'runner:show:snapshot')
cy.tick(50)
cy.wrap(runner.emit).should('be.calledWith', 'runner:show:snapshot', 1)
})
describe('then mousing out', () => {
beforeEach(() => {
cy.tick(50)
cy.get('.command').first().trigger('mouseout')
})
it('hides the snapshot after 50ms pass without another mouse over', () => {
cy.tick(50)
cy.wrap(runner.emit).should('be.calledWith', 'runner:hide:snapshot', 1)
})
it('does not hide the snapshot if there is another mouseover before 50ms passes', () => {
cy.wrap(runner.emit).should('not.be.calledWith', 'runner:hide:snapshot')
})
})
})
context('studio commands', () => {
beforeEach(() => {
addCommand(runner, {
id: 10,
number: 7,
name: 'get',
message: '#studio-command-parent',
state: 'success',
isStudio: true,
type: 'parent',
})
addCommand(runner, {
id: 11,
name: 'click',
message: '#studio-command-child',
state: 'success',
isStudio: true,
type: 'child',
})
})
it('studio commands have command-is-studio class', () => {
cy.contains('#studio-command-parent').closest('.command')
.should('have.class', 'command-is-studio')
cy.contains('#studio-command-child').closest('.command')
.should('have.class', 'command-is-studio')
})
it('only parent studio commands display remove button', () => {
cy.contains('#studio-command-parent').closest('.command')
.find('.studio-command-remove').should('be.visible')
cy.contains('#studio-command-child').closest('.command')
.find('.studio-command-remove').should('not.be.visible')
})
it('emits studio:remove:command with number when delete button is clicked', () => {
cy.spy(runner, 'emit')
cy.contains('#studio-command-parent').closest('.command')
.find('.studio-command-remove').click()
cy.wrap(runner.emit).should('be.calledWith', 'studio:remove:command', 7)
})
})
})