Day 5

We're back and it's Day 5! If you're a total Cypress n00b then I suggest starting from Day 0.

Last week we figured out how to sign in a user without interacting with the DOM and looked briefly at how we could approach testing My Account. You were challenged with creating a new spec containing a signIn function invoked by a beforeEach hook and a test to update the user's email address. You get extra credit if you created a cy.signIn custom command, signed in from a global hook or pulled credentials from Cypress environment configuration. Let's review!

In my-account.spec.js, first define the signIn function which takes username and password:

function signIn (username, password) {
  // we already have the logic here
  // include the `visit` command for now
}

Create a top-level describe block with a beforeEach that invokes signIn. Use a data object again to avoid hard-coding the credentials:

describe('My Account', () => {
  const data = {
    username: 'Allie2',
    password: 's3cret'
  }
  beforeEach('sign in', () => {
    signIn(data.username, data.password)
  })
})

// `signIn` can go here so it's out of the way (because of hoisting)
function signIn (username, password) {
  ...
}

Let's test out what we have so far by starting the app (npm start ) and starting the runner (npx cypress open). You'll need a test with something in it:

it('update account information', () => {
  cy.pause()
})

[image]

So far, so good! Click on My Account and you'll see the user is taken to /user/settings. We could click on that in the test but we already tested that link in the Home spec, so instead we'll just visit it.

it('update account information', () => {
  cy.visit('/user/settings')
})

Now we no longer need cy.visit('/') in our signIn function.

[image]

Looks like we don't have routes so we can pull them in from another test into a beforeEach, but let's create a setupRoutes function to invoke in the hook:

beforeEach('setup routes', () => {
  setupRoutes()
})

...

function setupRoutes() {
  cy.server()
  ...
}

Now we're ready to interact with the form (inputting an email address and saving the change). We can use the Selector Playground to get the Email field.

[image]

Now we can use .type(data.email) to type the email address in, right?

[image]

Oops! Looks like we'll need to chain .clear() to clear the field first, then we can click on the Save button.

[image]

Bingo! It works! We have a couple more routes we need to add to our setupRoutes function first, then we can wait on them. In the Command Log, select the wait for updating the user so we can see the contents of the request body from the Console.

[image]

You were tasked with asserting the expected email address in the request to update the user. It looks like the path to the email address is just request.body.email. Easy enough.

it('update account information', () => {
  cy.get('[data-test=user-settings-email-input]')
    .clear()
    .type(data.email)
  cy.get('[data-test=user-settings-submit]')
    .click()
  cy.wait('@updateUser')
    .its('request.body.email')
    .should('equal', data.email)
  cy.wait('@checkAuth')
})

It's worth pointing out an advantage of not hard-coding the email address: we're more certain that we're making an assertion on the same text we typed into the email field.


Let's explore commands.js. As described in the comments (and in the docs they reference), custom commands can be added using Cypress.Commands.add and existing commands can be overwritten with Cypress.Commands.overwrite. There are three flavors - parent, child and dual commands - that differ in how they handle the subject being passed to them. For now, we're only concerned with the simplest, parent-type command which doesn't accept a subject.

If we were to move our signIn function to commands.js and add it as a custom command:

Cypress.Commands.add('signIn', signIn)

then we can use it in our spec like this:

beforeEach('sign in', () => {
  cy.signIn(data.username, data.password)
})

In fact, we can use this command in any spec because it's now part of the global cy namespace.



Take-home Challenge

  1. Add a cy.setupRoutes command and use it in the My Account spec
  2. Add the new command to a global hook
  3. Update all tests to only depend on the global hooks to sign in and setup routes

GO TOP

🎉 You've successfully subscribed to iheartjs!
OK