May 17, 2023
e2e-testing - cypress - web development - qa testing
2 minutes

Creating Custom Commands in Cypress using the PrevSubject option

How can we create functions in cypress like `.type`, `.click`, etc...? Yes, on this page we will learn how we can do that.

Basically, the prevSubject option controls whether the function will be used with a subject, to have the result like this for example:

cy.get(selector).nameOfYourFunction(params)
OptionAcceptsDefault
prevSubjectBoolean, String or Arrayfalse

The prevSubject accepts the following values:

  • false: Ignore any previous subjects
  • true: Receives the previous subject as the first param (example 1)
  • optional: May start a chain, or use an existing chain (require some conditional to control - example 2)

In addition to controlling the command's implicit behavior, you can also add declarative subject validations such as:

  • element: Requires the previous subject be a DOM element
  • document: Requires the previous subject be the document
  • window: Requires the previous subject be the window

Examples:

You can see the examples here, and run that if you want: Commands file in my application

Example 1

Look that in this example we are passing an array with the element value to prevSubject, it means that we just can pass an element as the subject.

Not is necessary to pass it as an array, we can pass just the "element" for this example.

Cypress.Commands.add('handleEmail', { prevSubject: [true, 'element'] }, (subject, text) => {
    cy.wrap(subject).type('invalid-email')
    cy.get('[data-cy=sign-in]').click()
    cy.toastError()
    cy.wrap(subject).clear().type(text)
})

Also, we can pass more than 1 validation to each function, like:

Cypress.Commands.add('handleSomething', { prevSubject: ['element', 'window', 'document'] }, (subject, ...args) => {
    // Something that can use 3 type of prevSubjects...
})

Example 2

We can also create a function that, in addition to being able to use it with prevSubject, could be used in the normal model, take a look:

/**
 * This function can be used with prevSubject or not.
 * 
 * @memberOf cy
 * @function handlePassword
 */
Cypress.Commands.add('handlePassword', { prevSubject: ['optional', 'element'] }, (subject, text) => {
    if (subject) {
        cy.wrap(subject).type(text).clear()
    } else {
        cy.get('[data-cy=password]').type(text)
    }
})

Possible usages:

1: With prevSubject

cy.get('[data-cy=password]').handlePassword('St0n6Pass5rd')

2: Without prevSubject

cy.handlePassword('St0n6Pass5rd')

Thanks for reading, any suggestion is welcome!