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.
Cypress can run recorded tests in parallel across multiple machines since version 3.1. 0. While parallel tests can also technically run on a single machine, we do not recommend it since this machine would require significant resources to run your tests efficiently.
cypress run --spec <spec> Run tests specifying a single test file to run instead of all tests. The spec path should be an absolute path or can relative to the current working directory.
Cypress provides two ways to test cases. Either using the Cypress UI Test Runner or from the CLI using the "cypress run" command. A specific test case can be executed on the CLI using the "--spec" option. We can change the browser for a specific test run on CLI using the "--browser" option.
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()
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With