Roralee
Roralee

Reputation: 548

Cypress - have 1 test call another test and run it

We are using Cypress to automate unit, functional, and presentation layer tests. Our development methodology has been along the lines of blending Atomic design in a React environment. We are balancing the relentless react modularity vs global/regional/local scoping of CSS. As such, our test organization is falling along the same organizational lines.

For example, I have .../support/button_all.js that tests the UI presentation in all 5 of our site's breakpoints (yes, 5 - don't ask). Atomic Pattern Tests - All buttons

describe('Global Button Patterns', () => {
  context('mobile', () => {
    beforeEach(function() {
      cy.viewport(320, 740)
    })

    it('GLOBAL & XS bp: Buttons display as designed', () => {
      // $pageURL needs be set/redefined
      // by local test variable
      // cy.visit('/')

      // All Buttons
      //cy.document()
      cy.get('[data-cy=button]')
        .should('be.visible')
        .should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
        .should('have.css', 'border-radius', '4px')
        .should('have.css', 'font-size', '11px')
        .should('have.css', 'font-weight', '800')
        .should('have.css', 'letter-spacing', '0.96px')
        .should('have.css', 'text-transform', 'uppercase')
      cy.get('[data-cy=button')
        .should('have.css', 'min-height')
        .should('be.gte', '30px')
      cy.get('[data-cy=button]')
        .should('have.css', 'min-width')
        .should('be.gte', '117px')
      cy.get('[data-cy=button]')
        .should('have.css', 'border-width')
        .and('be.gte', '1px')
        .and('be.lte', '2px')
      cy.get('[data-cy=button]')
        .should('have.css', 'line-height')
        .and('match', /^15/)
      cy.get('[data-cy=button]')
        .should('have.css', 'padding-right')
        .and('match', /^17/)

      // Button - Outline Light
      cy.get('.btn-outline-light')
        .should('have.css', 'border-color', 'rgb(255, 255, 255)')
        .should('have.css', 'color', 'rgb(255, 255, 255)')

      // Button - Outline Dark
      cy.get('.btn-outline-dark')
        .should('have.css', 'border-color', 'rgb(55, 71, 79)')
        .should('have.css', 'color', 'rgb(55, 71, 79)')

      // Button Light (used for Login buttons)

      // Button Light states - :focus, :hover, :active
      // Test manually for the moment.
    })
  })

  context('tablet', () => {
    beforeEach(function() {
      cy.viewport(640, 1136)
    })

    it('SM bp: Default button displays as designed ', () => {
      // cy.visit('/')

      // All Buttons
      cy.get('[data-cy=button]')
        .should('be.visible')
        .should('have.css', 'font-size', '11.5px')
        .should('have.css', 'letter-spacing', '1.23px')
      cy.get('[data-cy=button]')
        .should('have.css', 'min-width')
        .should('be.gte', '131px')
      cy.get('[data-cy=button]')
        .should('have.css', 'line-height')
        .and('match', /^17/)
      cy.get('[data-cy=button]')
        .should('have.css', 'padding-right')
        .and('match', /^21/)
    })
  })

  context('laptop', () => {
    beforeEach(function() {
      cy.viewport(960, 600)
    })

    it('MD bp: Default button displays as designed', () => {
      // cy.visit('/')

      // All Buttons
      cy.get('[data-cy=button]')
        .should('be.visible')
        .should('have.css', 'font-size', '12.5px')
        .should('have.css', 'letter-spacing', '1.34px')
      cy.get('[data-cy=button]')
        .should('have.css', 'min-width')
        .should('be.gte', '139px')
      cy.get('[data-cy=button]')
        .should('have.css', 'line-height')
        .and('match', /^18/)
      cy.get('[data-cy=button]')
        .should('have.css', 'padding-right')
        .and('match', /^21/)
    })
  })
})

I want to include this test in the integration/components/hero.js file. The hero.js contains 5 breakpoint contexts. I want the button_all.js file to run after it runs the the breakpoint contexts.

Component Tests - hero.js

import Buttons from '../../support/buttons_all.js'

describe('Hero Component', () => {
  context('mobile', () => {
    beforeEach(function() {
      cy.viewport(320, 740)
    })

    // global pattern test(s)
    after(function() {
      // runs once after all tests in the block
    })

    it('displays Hero Component in mobile bp as designed', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'padding', '14px 0px 40px')
        .should('have.css', 'width')
        .should('be.gt', '292px')
      cy.get('[data-cy=hero]')
        .should('have.css', 'background')
      cy.get('[data-cy=hero] h1')
        .should('have.css', 'color', 'rgb(255, 255, 255)')
        .should('have.css', 'font-size', '38px')
        .should('have.css', 'line-height').and('match', /^43/)
      cy.get('[data-cy=hero] p')
        .should('have.css', 'color', 'rgb(255, 255, 255)')
        .should('have.css', 'margin-top', '8px')
        .should('have.css', 'font-size', '15px')
        .should('have.css', 'line-height').and('match', /^18/)
      cy.get('[data-cy=hero] .btn-outline-light')
        .should('have.css', 'margin-top', '12px')
        .should('have.css', 'margin-bottom').and('match', /0/)
    })
  })

  context('tablet', () => {
    beforeEach(function() {
      cy.viewport(640, 1136)
    })

    it('displays Hero Component in tablet bp as designed', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'padding', '28px 0px 30px')
        .should('have.css', 'width')
        .should('be.gt', '580px')
      cy.get('[data-cy=hero] h1')
        .should('have.css', 'font-size', '48px')
        .should('have.css', 'width')
        .should('be.gt', '300px')
      cy.get('[data-cy=hero] h1')
        .should('have.css', 'line-height').and('match', /^54/)
      cy.get('[data-cy=hero] p')
        .should('have.css', 'margin-top', '8px')
        .should('have.css', 'font-size', '15.5px')
      cy.get('[data-cy=hero] .btn-outline-light')
        .should('have.css', 'margin-top', '12px')
    })
  })

  context('laptop', () => {
    beforeEach(function() {
      cy.viewport(960, 600)
    })

    it('displays Hero Component in laptop bp as designed', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'padding', '42px 0px 87.9936px')
        .should('have.css', 'width')
        .should('be.gt', '860px')
      cy.get('[data-cy=hero] h1')
        .should('have.css', 'font-size', '52px')
        .should('have.css', 'line-height').and('match', /^60/)
      cy.get('[data-cy=hero] p')
        .should('have.css', 'font-size', '16px')
        .should('have.css', 'margin-top', '10px')
        .should('have.css', 'line-height').and('match', /^20/)
      cy.get('[data-cy=hero] .btn-outline-light')
        .should('have.css', 'margin-top', '20px')
    })
  })

  context('desktop', () => {
    beforeEach(function() {
      cy.viewport(1280, 850)
    })

    it('displays Hero Component in desktop bp as designed', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'padding', '56px 0px 129.997px')
        .should('have.css', 'width')
        .should('be.gt', '1168px')
      cy.get('[data-cy=hero] h1')
        .should('have.css', 'font-size', '60px')
        .should('have.css', 'line-height').and('match', /^70/)
      cy.get('[data-cy=hero] p')
        .should('have.css', 'font-size', '16px')
        .should('have.css', 'margin-top', '10px')
        .should('have.css', 'line-height').and('match', /^20/)
    })
  })

  context('oversized', () => {
    beforeEach(function() {
      cy.viewport(1600, 2048)
    })

    it('displays Hero Component in oversized bp as designed', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'padding', '70px 0px 188px')
    })
  })

  context('buttons_all.js', function(){

  })
})

After this is done, I want to go to my integration/pages/homepage/hero_spec.js which declares where to go and tests the actual content in this area of the page. I want the hero.js test to run after the contexts contained in hero_spec.js.

Homepage tests - hero_spec.js

import HeroComponent from '../components/hero.js'

describe('Homepage - Hero Component', () => {
  context('mobile', () => {
    beforeEach(function() {
      cy.viewport(320, 740)
    })

    // global pattern test(s)
    after(function() {
      // runs once after all tests in the block
    })

    it('displays Homepage Hero Component as designed in mobile bp', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'background-image')
        .should('contain', 'live-life-confidence-1x1', 'contain', '.jpg')
      cy.get('[data-cy=hero] h1')
        .contains('Lorem Ipsum')
      cy.get('[data-cy=hero] p')
        .contains('Lorem ipsum sum amit dolor...')
      cy.get('[data-cy=hero] .btn-outline-light')
        .contains('About us')
        .should('have.attr', 'href')
        .and('match', /about-us/)
        .then((href) => {
          cy.visit(href)
        })
    })
  })

  context('tablet', () => {
    beforeEach(function() {
      cy.viewport(640, 1136)
    })

    after(function() {
      // runs once after all tests in the block
    })

    it('displays Homepage Hero Component as designed in tablet bp', () => {
      cy.visit('/')

      cy.get('[data-cy=hero]')
        .should('have.css', 'background-image')
        .should('contain', 'live-life-confidence-2x1', 'contain', '.jpg')
    })
  })

})

My problem is figuring out how to pull in tests modularly. I see in this example, that I can import and call a pure JS file: https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/unit-testing__application-code/cypress/integration/unit_test_application_code_spec.js

While Cypress' documentation is excellent, it seems to be silent on being able to accomplish this. I'd appreciate a direction to look in to solve this problem.

Upvotes: 15

Views: 15637

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23513

In the example recipe you cited, the imports are all SUT, not other tests.

To run button_all.js inside another test, wrap it's code in a function. You can also pass params to modify each run.

export function runButtonAllTests(testParams) {

  describe('Global Button Patterns', () => {
    context('mobile', () => {
      if (testParams.allBreakpoints) {
      ...

}

Run it by calling the function. Note I keep it in the support folder rather than the integration folder to stop Cypress from including it in 'run all'.

import { runButtonAllTests } from '../../support/run_button_all_tests.js'

describe('Homepage - Hero Component', () => {
  context('mobile', () => {
    before(function() {
      cy.viewport(320, 740)
      cy.visit('/')
    })

    context('buttons_all.js', function() {
      runButtonAllTests()
    })

Upvotes: 19

Related Questions