it('sorts the prices', () => { cy.visit('/') // enter user information cy.get('[data-test="username"]').type('standard_user') cy.get('[data-test="password"]').type('secret_sauce') cy.get('[data-test="login-button"]').click() // transition to the inventory page cy.location('pathname').should('equal', '/inventory.html') // sort by price cy.get('.product_sort_container').select('lohi') // get the list of prices // convert to numbers // and confirm they are sorted cy.get('.inventory_item_price') .map('innerText') .mapInvoke('substr', 1) .map(Number) .print() .should('be.ascending') })
Let's say the test fails. How many items were there? Did it finish each section? In longer tests, it is hard to say without carefully watching the video of the entire test run. Here is a better way: move each comment into cy.log command. Make those logs bold using **...** Markdown syntax.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
it('sorts the prices', () => { cy.visit('/') cy.log('**enter user information**') cy.get('[data-test="username"]').type('standard_user') cy.get('[data-test="password"]').type('secret_sauce') cy.get('[data-test="login-button"]').click() cy.log('**transition to the inventory page**') cy.location('pathname').should('equal', '/inventory.html') cy.log('**sort by price**') cy.get('.product_sort_container').select('lohi') cy.log('**confirm the prices are sorted**') cy.get('.inventory_item_price') .map('innerText') .mapInvoke('substr', 1) .map(Number) .print() .should('be.ascending') })
Each comment like // enter user information becomes a command cy.log('**enter user information**'). It still explains what the test is about to do, plus it shows up in your screenshots and videos.
Here is how you can make it even better. Install cypress-log-to-term and include it from your Cypress config and spec files. Now you see all those logs in your terminal too.
Log the current subject
The plugin cypress-log-to-term has one powerful feature. It overwrites cy.log command AND makes it better. For example, let's log the important information about the test and what it "sees" on the page.
it('sorts the prices', () => { cy.visit('/') cy.log('**enter user information**') cy.get('[data-test="username"]').type('standard_user') cy.get('[data-test="password"]').type('secret_sauce') cy.get('[data-test="login-button"]').click() cy.log('**transition to the inventory page**') cy.location('pathname').should('equal', '/inventory.html').log('at %o') cy.log('**sort items by price**') cy.get('.product_sort_container').select('lohi') cy.log('**confirm the prices are sorted**') cy.get('.inventory_item_price') .map('innerText') .print('prices %o') .mapInvoke('substr', 1) .map(Number) .print() .should('be.ascending') .log('prices low to high %o') })
Notice the extra .log(...) commands attached:
1 2 3 4 5 6
cy.location('pathname').should('equal', '/inventory.html').log('at %o') ... cy.get('.inventory_item_price') ... .should('be.ascending') .log('prices low to high %o')
I have extended the cy.log to take the formatting into account plus use the current subject, if any. Thus the terminal shows really useful information right away.
Use cy-spok
Imagine your application is making an Ajax request. You want to validate both the request and the response objects, plus the server response HTTP status code.
it('checks the calculate network call', () => { cy.visit('/calculator.html') cy.get('#num1').type('5') cy.get('#num2').type('2') cy.intercept('POST', '/calculate').as('calculate') cy.get('#add').click() cy.wait('@calculate') .its('response.statusCode') .should('equal', 200) // need to get the intercept again to check the request cy.get('@calculate') .its('request') .should((request) => { expect(request.method, 'method').to.equal('POST') expect(request.body, 'body').to.deep.equal({ a: 5, b: 2, operation: '+', }) }) // if we want to check the response, need to get it again cy.get('@calculate') .its('response') .should((response) => { expect(response.body, 'body').to.deep.equal({ a: 5, b: 2, operation: '+', answer: 7, }) }) })
Ughh, 20 lines of code just to validate the request and response in the single call. Even with this much code, the Command Log does not even show the objects and the actual values, since Chai truncates the assertion messages. Let's improve it using cy-spok.
Using cy-spok we can confirm all the properties in a nested deep object, and we can confirm some of the properties, leaving others unchecked. We can also use predicate functions and built-in predicate checks. For example: