{
  "doc": {
    "id": "api/cypress-api/custom-commands",
    "title": "Custom Commands in Cypress",
    "description": "Learn how to create custom Cypress commands and overwrite existing commands.",
    "section": "api",
    "source_path": "/llm/markdown/api/cypress-api/custom-commands.md",
    "version": "524ff5211e60b5d53e55d6ad976d83966f66e7cd",
    "updated_at": "2026-04-30T14:20:05.396Z",
    "headings": [
      {
        "id": "api/cypress-api/custom-commands#custom-commands",
        "text": "Custom Commands",
        "level": 1
      },
      {
        "id": "api/cypress-api/custom-commands#syntax",
        "text": "Syntax",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-commands#usage",
        "text": "Usage",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#arguments",
        "text": "Arguments",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#examples",
        "text": "Examples",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-commands#parent-commands",
        "text": "Parent Commands",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#click-link-containing-text",
        "text": "Click link containing text",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#check-a-token",
        "text": "Check a token",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#download-a-file",
        "text": "Download a file",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#commands-to-work-with-sessionstorage",
        "text": "Commands to work with sessionStorage",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#log-in-command-using-ui",
        "text": "Log in command using UI",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#log-in-command-using-request",
        "text": "Log in command using request",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#log-out-command-using-ui",
        "text": "Log out command using UI",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#log-out-command-using-localstorage",
        "text": "Log out command using localStorage[",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#create-a-user",
        "text": "Create a user",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#child-commands",
        "text": "Child Commands",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#custom-console-command",
        "text": "Custom console command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#dual-commands",
        "text": "Dual Commands",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#custom-dual-command",
        "text": "Custom Dual Command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#overwrite-existing-commands",
        "text": "Overwrite Existing Commands",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#overwrite-visit-command",
        "text": "Overwrite visit command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#overwrite-type-command",
        "text": "Overwrite type command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#overwrite-screenshot-command",
        "text": "Overwrite screenshot command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#overwrite-click-command",
        "text": "Overwrite click command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#validations",
        "text": "Validations",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-commands#require-element",
        "text": "Require Element",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#allow-multiple-types",
        "text": "Allow Multiple Types",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#trigger",
        "text": ".trigger()",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#optional-with-types",
        "text": "Optional with Types",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#notes",
        "text": "Notes",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-commands#command-logging",
        "text": "Command Logging",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#cy-hover-and-cy-mount",
        "text": "cy.hover() and cy.mount()",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#best-practices",
        "text": "Best Practices",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-commands#1-dont-make-everything-a-custom-command",
        "text": "1. Don't make everything a custom command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#2-dont-overcomplicate-things",
        "text": "2. Don't overcomplicate things",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#3-dont-do-too-much-in-a-single-command",
        "text": "3. Don't do too much in a single command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#4-skip-your-ui-as-much-as-possible",
        "text": "4. Skip your UI as much as possible",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#5-write-typescript-definitions",
        "text": "5. Write TypeScript definitions",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#6-create-a-function-that-adds-the-custom-command",
        "text": "6. Create a function that adds the custom command",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-commands#history",
        "text": "History",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-commands#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "content": {
    "type": "root",
    "children": [
      {
        "type": "heading",
        "depth": 1,
        "children": [
          {
            "type": "text",
            "value": "Custom Commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Cypress comes with its own API for creating custom commands and overwriting existing commands. The built in Cypress commands use the very same API that's defined below."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "There are two API available for adding custom commands:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Syntax",
                    "children": [
                      {
                        "type": "text",
                        "value": "`Cypress.Commands.add()`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " - use to add a custom command to use when writing tests"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Overwrite-Existing-Commands",
                    "children": [
                      {
                        "type": "text",
                        "value": "`Cypress.Commands.overwrite()`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " - use to override an existing built-in Cypress command or reserved internal function. Caution: this overrides it for Cypress as well and could impact how Cypress behaves."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you want your method to have builtin "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/core-concepts/retry-ability.md",
            "children": [
              {
                "type": "text",
                "value": "retry-ability"
              }
            ]
          },
          {
            "type": "text",
            "value": ", and especially if you return a DOM element for further commands to act on, consider writing a "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/cypress-api/custom-queries.md",
            "children": [
              {
                "type": "text",
                "value": "custom query"
              }
            ]
          },
          {
            "type": "text",
            "value": " instead."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "We recommend defining queries is in your `cypress/support/commands.js` file, since it is loaded before any test files are evaluated via an import statement in the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/core-concepts/writing-and-organizing-tests.md#Support-file",
            "children": [
              {
                "type": "text",
                "value": "supportFile"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Syntax"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add(name, callbackFn)Cypress.Commands.add(name, options, callbackFn)Cypress.Commands.addAll(callbackObj)Cypress.Commands.addAll(options, callbackObj)Cypress.Commands.overwrite(name, callbackFn)"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Usage"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Correct Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('login', (email, pw) => {})Cypress.Commands.addAll({  login(email, pw) {},  visit(orig, url, options) {},})Cypress.Commands.overwrite('visit', (orig, url, options) => {})"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Arguments"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "name (String)"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The name of the command you're either adding or overwriting."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "callbackFn (Function)"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Pass a function that receives the arguments passed to the command."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "callbackObj (Object)"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "An object with `callbackFn`s as properties."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "options (Object)"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Pass in an options object to define the implicit behavior of the custom command."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "`options` is only supported for use in `Cypress.Commands.add()` and not supported for use in `Cypress.Commands.overwrite()`"
          }
        ]
      },
      {
        "type": "table",
        "align": [
          null,
          null,
          null,
          null
        ],
        "children": [
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Option"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Accepts"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Default"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Description"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`prevSubject`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`Boolean`, `String` or `Array`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`false`"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "how to handle the previously yielded subject."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The `prevSubject` accepts the following values:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`false`: ignore any previous subjects: (parent command)"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`true`: receives the previous subject: (child command)"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`optional`: may start a chain, or use an existing chain: (dual command)"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In addition to controlling the command's implicit behavior you can also add declarative subject validations such as:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`element`: requires the previous subject be a DOM element"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`document`: requires the previous subject be the document"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`window`: requires the previous subject be the window"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Examples"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Parent Commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Parent commands always begin a new chain of commands. Even if you've chained it off of a previous command, parent commands will always start a new chain, and ignore previously yielded subjects."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Examples of parent commands:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/visit.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.visit()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/request.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.request()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/exec.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.exec()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/intercept.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.intercept()`"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Click link containing text"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('clickLink', (label) => {  cy.get('a').contains(label).click()})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.clickLink('Buy Now')"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Check a token"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('checkToken', (token) => {  cy.window().its('localStorage.token').should('eq', token)})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.checkToken('abc123')"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Download a file"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Originally used in "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://github.com/Xvier/cypress-downloadfile",
            "children": [
              {
                "type": "text",
                "value": "cypress-downloadfile"
              }
            ]
          },
          {
            "type": "text",
            "value": ", this command calls other Cypress commands."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('downloadFile', (url, directory, fileName) => {  return cy.getCookies().then((cookies) => {    return cy.task('downloadFile', {      url,      directory,      cookies,      fileName,    })  })})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.downloadFile('https://path_to_file.pdf', 'mydownloads', 'demo.pdf')"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Commands to work with `sessionStorage`"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('getSessionStorage', (key) => {  cy.window().then((window) => window.sessionStorage.getItem(key))})Cypress.Commands.add('setSessionStorage', (key, value) => {  cy.window().then((window) => {    window.sessionStorage.setItem(key, value)  })})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.setSessionStorage('token', 'abc123')cy.getSessionStorage('token').should('eq', 'abc123')"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Log in command using UI"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('loginViaUi', (user) => {  cy.session(    user,    () => {      cy.visit('/login')      cy.get('input[name=email]').type(user.email)      cy.get('input[name=password]').type(user.password)      cy.get('button#login').click()      cy.get('h1').contains(`Welcome back ${user.name}!`)    },    {      validate: () => {        cy.getCookie('auth_key').should('exist')      },    }  )})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.loginViaUi({ email: 'fake@email.com', password: '$ecret1', name: 'johndoe' })"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Log in command using request"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('loginViaApi', (userType, options = {}) => {  // this is an example of skipping your UI and logging in programmatically  // setup some basic types  // and user properties  const types = {    admin: {      name: 'Jane Lane',      admin: true,    },    user: {      name: 'Jim Bob',      admin: false,    },  }  // grab the user  const user = types[userType]  // create the user first in the DB  cy.request({    url: '/seed/users', // assuming you've exposed a seeds route    method: 'POST',    body: user,  })    .its('body')    .then((body) => {      // assuming the server sends back the user details      // including a randomly generated password      //      // we can now login as this newly created user      cy.request({        url: '/login',        method: 'POST',        body: {          email: body.email,          password: body.password,        },      })    })})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// can start a chain off of cycy.loginViaApi('admin')// can be chained but will not receive the previous subjectcy.get('button').loginViaApi('user')"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Log out command using UI"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('logout', () => {  cy.contains('Login').should('not.exist')  cy.get('.avatar').click()  cy.contains('Logout').click()  cy.get('h1').contains('Login')  cy.getCookie('auth_key').should('not.exist')})"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Log out command using `localStorage`["
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "End-to-End Only"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "](/llm/markdown/app/core-concepts/testing-types.md#What-is-E2E-Testing)"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('logout', () => {  cy.window().its('localStorage').invoke('removeItem', 'session')  cy.visit('/login')})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.logout()"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Create a user"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('createUser', (user) => {  cy.request({    method: 'POST',    url: 'https://www.example.com/tokens',    body: {      email: 'admin_username',      password: 'admin_password',    },  }).then((resp) => {    cy.request({      method: 'POST',      url: 'https://www.example.com/users',      headers: { Authorization: 'Bearer ' + resp.body.token },      body: user,    })  })})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.createUser({  id: 123,  name: 'Jane Lane',})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Command Log"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Did you know that you can control how your custom commands appear in the Command Log? Read more about "
          },
          {
            "type": "link",
            "title": null,
            "url": "#Command-Logging",
            "children": [
              {
                "type": "text",
                "value": "Command Logging"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Child Commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Child commands are always chained off of a parent command, or another child command."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The previous subject will automatically be yielded to the callback function."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Examples of child commands:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/click.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`.click()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/submit.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`.submit()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/trigger.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`.trigger()`"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Custom `console` command"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// not a super useful custom command// but demonstrates how subject is passed// and how the arguments are shiftedCypress.Commands.add(  'console',  {    prevSubject: true,  },  (subject, method) => {    // the previous subject is automatically received    // and the commands arguments are shifted    // allow us to change the console method used    method = method || 'log'    // log the subject to the console    console[method]('The subject is', subject)    // whatever we return becomes the new subject    //    // we don't want to change the subject so    // we return whatever was passed in    return subject  })"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.get('button')  .console('info')  .then(($button) => {    // subject is still $button  })"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "By setting the `{ prevSubject: true }`, our new `.console()` command will require a subject."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Invoking it like this would error:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.console() // error about how you can't call console without a subject"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Whenever you're using a child command you likely want to use "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/wrap.md",
            "children": [
              {
                "type": "text",
                "value": "cy.wrap()"
              }
            ]
          },
          {
            "type": "text",
            "value": " on the subject. Wrapping it enables you to immediately use more Cypress commands on that subject."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Dual Commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "A dual command can either start a chain of commands or be chained off of an existing one. It is basically the hybrid between both a parent and a child command. You will likely rarely use this, and only a handful of our internal commands use this."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Nevertheless, it is useful if your command can work in multiple ways - either with an existing subject or without one."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Examples of dual commands:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/screenshot.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.screenshot()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/scrollto.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.scrollTo()`"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/wait.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.wait()`"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Custom Dual Command"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.add('dismiss', {  prevSubject: 'optional'}, (subject, arg1, arg2) => {  // subject may be defined or undefined  // so you likely want to branch the logic  // based off of that  if (subject) {    // wrap the existing subject    // and do something with it    cy.wrap(subject)    ...  } else {    ...  }})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.dismiss() // no subjectcy.get('#dialog').dismiss() // with subject"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Overwrite Existing Commands"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can also modify the behavior of existing Cypress commands. This is useful to always set some defaults to avoid creating another command that ends up using the original."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "`Cypress.Commands.overwrite` can only overwrite commands, not queries. If you want to modify the behavior of a query, you'll need to use "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/cypress-api/custom-queries.md",
            "children": [
              {
                "type": "text",
                "value": "`Cypress.Commands.overwriteQuery`"
              }
            ]
          },
          {
            "type": "text",
            "value": " instead."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Overwrite `visit` command"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.overwrite('visit', (originalFn, url, options) => {  const domain = Cypress.expose('BASE_DOMAIN')  if (domain === '...') {    url = '...'  }  if (options.something === 'else') {    url = '...'  }  // originalFn is the existing `visit` command that you need to call  // and it will receive whatever you pass in here.  //  // make sure to add a return here!  return originalFn(url, options)})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "We see many of our users creating their own `visitApp` command. We commonly see that all you're doing is swapping out base urls for `development` vs `production` environments."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This is usually unnecessary because Cypress is already configured to swap out a `baseUrl` that both "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/visit.md",
            "children": [
              {
                "type": "text",
                "value": "cy.visit()"
              }
            ]
          },
          {
            "type": "text",
            "value": " and "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/request.md",
            "children": [
              {
                "type": "text",
                "value": "cy.request()"
              }
            ]
          },
          {
            "type": "text",
            "value": " use. Set the `baseUrl` configuration property in your "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/references/configuration.md",
            "children": [
              {
                "type": "text",
                "value": "Cypress configuration"
              }
            ]
          },
          {
            "type": "text",
            "value": " and override it with the `CYPRESS_BASE_URL` environment variable."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "For more complex use cases feel free to overwrite existing commands."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Overwrite `type` command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you are typing into a password field, the password input is masked automatically within your application. But "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/type.md",
            "children": [
              {
                "type": "text",
                "value": ".type()"
              }
            ]
          },
          {
            "type": "text",
            "value": " automatically logs any typed content into the Cypress Command Log."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.get('#username').type('username@email.com')cy.get('#password').type('superSecret123')"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You may want to mask some values passed to the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/type.md",
            "children": [
              {
                "type": "text",
                "value": ".type()"
              }
            ]
          },
          {
            "type": "text",
            "value": " command so that sensitive data does not display in screenshots or videos of your test run. This example overwrites the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/type.md",
            "children": [
              {
                "type": "text",
                "value": ".type()"
              }
            ]
          },
          {
            "type": "text",
            "value": " command to allow you to mask sensitive data in the Cypress Command Log."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.overwrite('type', (originalFn, element, text, options) => {  if (options && options.sensitive) {    // turn off original log    options.log = false    // create our own log with masked message    Cypress.log({      $el: element,      name: 'type',      message: '*'.repeat(text.length),    })  }  return originalFn(element, text, options)})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.get('#username').type('username@email.com')cy.get('#password').type('superSecret123', { sensitive: true })"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Now our sensitive password is not printed to the Cypress Command Log when `sensitive: true` is passed as an option to "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/type.md",
            "children": [
              {
                "type": "text",
                "value": ".type()"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Overwrite `screenshot` command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This example overwrites "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/screenshot.md",
            "children": [
              {
                "type": "text",
                "value": "cy.screenshot()"
              }
            ]
          },
          {
            "type": "text",
            "value": " to always wait until a certain element is visible."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.overwrite(  'screenshot',  (originalFn, subject, fileName, options) => {    // call another command, no need to return as it is managed    cy.get('.app')      .should('be.visible')      // overwrite the default timeout, because screenshot does that internally      // otherwise the `then` is limited to the default command timeout      .then({ timeout: Cypress.config('responseTimeout') }, () => {        // return the original function so that cypress waits for it        return originalFn(subject, fileName, options)      })  })"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "Overwrite `click` command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This example overwrites "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/click.md",
            "children": [
              {
                "type": "text",
                "value": ".click()"
              }
            ]
          },
          {
            "type": "text",
            "value": " to always have the `waitForAnimations` option set to `false`."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "Cypress.Commands.overwrite(  'click',  (originalFn, subject, positionOrX, y, options = {}) => {    options.waitForAnimations = false    return originalFn(subject, positionOrX, y, options)  })"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Validations"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "As noted in the "
          },
          {
            "type": "link",
            "title": null,
            "url": "#Arguments",
            "children": [
              {
                "type": "text",
                "value": "Arguments"
              }
            ]
          },
          {
            "type": "text",
            "value": " above, you can also set `prevSubject` to one of:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`element`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`document`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`window`"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "When doing so Cypress will automatically validate your subject to ensure it conforms to one of those types."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Adding validations is optional. Passing `{ prevSubject: true }` will require a subject, but not validate its type."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Require Element"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Require subject be of type: `element`."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// this is how .click() is implementedCypress.Commands.add(  'click',  {    prevSubject: 'element',  },  (subject, options) => {    // receives the previous subject and it's    // guaranteed to be an element  })"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Valid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.get('button').click() // has subject, and is `element`"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Invalid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.click() // no subject, will errorcy.wrap([]).click() // has subject, but not `element`, will error"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Allow Multiple Types"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "`.trigger()`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Require subject be one of the following types: `element`, `document` or `window`"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// this is how .trigger() is implementedCypress.Commands.add(  'trigger',  {    prevSubject: ['element', 'document', 'window'],  },  (subject, eventName, options) => {    // receives the previous subject and it's    // guaranteed to be an element, document, or window  })"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Valid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.get('button').trigger() // has subject, and is `element`cy.document().trigger() // has subject, and is `document`cy.window().trigger() // has subject, and is `window`"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Invalid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.trigger() // no subject, will errorcy.wrap(true).trigger() // has subject, but not `element`, will error"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Validations always work as \"or\" not \"and\"."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Optional with Types"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can also mix optional commands with validations."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// this is how .scrollTo() is implementedCypress.Commands.add(  'scrollTo',  {    prevSubject: ['optional', 'element', 'window'],  },  (subject, ...args) => {    // subject could be undefined    // since it's optional.    //    // if it's present then it's and element or window.    // - when window, we'll scroll to a position on the page.    // - when element, we'll scroll to a position related to the element.    if (subject) {      // ...    } else {      // ...    }  })"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Valid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.scrollTo() // no subject, but valid because it's optionalcy.get('#main').scrollTo() // has subject, and is `element`cy.visit().scrollTo() // has subject, and since visit yields `window` it's ok"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Invalid Usage"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.document().scrollTo() // has subject, but it's a `document`, will errorcy.wrap(null).scrollTo() // has subject, but it's `null`, will error"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Notes"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Command Logging"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "When creating your own custom command, you can control how it appears and behaves in the Command Log."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Take advantage of the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/cypress-api/cypress-log.md",
            "children": [
              {
                "type": "text",
                "value": "`Cypress.log()`"
              }
            ]
          },
          {
            "type": "text",
            "value": " API. When you're issuing many internal Cypress commands, consider passing `{ log: false }` to those commands, and programmatically controlling your custom command. This will cleanup the Command Log and be much more visually appealing and understandable."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "`cy.hover()` and `cy.mount()`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Cypress does not have `cy.hover()` or `cy.mount()` commands out-of-the-box. See how to craft your own "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/hover.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.hover()`"
              }
            ]
          },
          {
            "type": "text",
            "value": " and "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/mount.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.mount()`"
              }
            ]
          },
          {
            "type": "text",
            "value": " custom commands."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Best Practices"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "1. Don't make everything a custom command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Custom commands work well when you're needing to describe behavior that's desirable across all of your tests. Examples would be a `cy.setup()` or `cy.login()` or extending your application's behavior like `cy.get('.dropdown').dropdown('Apples')`. These are specific to your application and can be used everywhere."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "However, this pattern can be used and abused. Let's not forget - writing Cypress tests is JavaScript, and it's often more efficient to write a function for repeatable behavior that's specific to only a single spec file."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you're working on a `search.cy.js` file and want to compose several repeatable actions together, you should first ask yourself:"
          }
        ]
      },
      {
        "type": "blockquote",
        "children": [
          {
            "type": "paragraph",
            "children": [
              {
                "type": "text",
                "value": "Can this be written as a function?"
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The answer is usually yes. Here's an example:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// There's no reason to create something like a cy.search() custom// command because this behavior is only applicable to a single spec file//// Use a regular ol' javascript function folks!const search = (term, options = {}) => {  // example massaging to defaults  _.defaults(options, {    headers: {},  })  const { fixture, headers } = options  // return cy chain here so we can  // chain off this function below  return cy    .log(`Searching for: ${term} `)    .intercept('GET', '/search/**', (req) => {      req.reply({        statusCode: 200,        body: `fixture:${fixture}`,        headers: headers,      })    })    .as('getSearchResults')    .get('#search')    .type(term)    .wait('@getSearchResults')}it('displays a list of search results', () => {  cy.visit('/page')    .then(() => {      search('cypress.io', {        fixture: 'list',      }).then((reqRes) => {        // do something with the '@getSearchResults'        // request such as make assertions on the        // request body or url params        // {        //   url: 'http://app.com/search?cypress.io'        //   method: 'GET',        //   duration: 123,        //   request: {...},        //   response: {...},        // }      })    })    .get('#results li')    .should('have.length', 5)    .get('#pagination')    .should('not.exist')})it('displays no search results', () => {  cy.visit('/page')    .then(() => {      search('cypress.io', {        fixture: 'zero',      })    })    .get('#results')    .should('contain', 'No results found')})it('paginates many search results', () => {  cy.visit('/page')    .then(() => {      search('cypress.io', {        fixture: 'list',        headers: {          // trick our app into thinking          // there's a bunch of pages          'x-pagination-total': 3,        },      })    })    .get('#pagination')    .should(($pagination) => {      // should offer to goto next page      expect($pagination).to.contain('Next')      // should have provided 3 page links      expect($pagination.find('li.page')).to.have.length(3)    })})"
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "2. Don't overcomplicate things"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Custom commands you write are generally an abstraction over a series of internal commands. That means you and your team members exert much more mental effort to understand what your custom command does."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "There's no reason to add this level of complexity when you're only wrapping a couple commands."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Don't do things like:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`cy.clickButton(selector)`"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "`.shouldBeVisible()`"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This first custom command is wrapping `cy.get(selector).click()`. Going down this route would lead to creating dozens or even hundreds of custom commands to cover every possible combination of element interactions. It's completely unnecessary."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The `.shouldBeVisible()` custom command isn't worth the trouble or abstraction when you can already use: `.should('be.visible')`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Testing in Cypress is all about readability and simplicity. You don't have to do that much actual programming to get a lot done. You also don't need to worry about keeping your code as DRY as possible. Test code serves a different purpose than app code. Understandability and debuggability should be prioritized above all else."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Try not to overcomplicate things and create too many abstractions. When in doubt, use a regular function for individual spec files."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "3. Don't do too much in a single command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Make your custom commands composable and as unopinionated as possible. Cramming too much into them makes them inflexible and requires more and more options passing to control their behavior."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Try to add either zero or as few assertions as possible in your custom command. Those tend to shape your command into a much more rigid structure. Sometimes this is unavoidable, but a best practice is to let the calling code choose when and how to use assertions."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "4. Skip your UI as much as possible"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Custom commands are a great way to abstract away setup (specific to your app). When doing those kinds of tasks, skip as much of the UI as possible. Use "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/request.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.request()`"
              }
            ]
          },
          {
            "type": "text",
            "value": " to login, set cookies or localStorage directly, stub and mock your applications functions, and / or trigger events programmatically."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Having custom commands repeat the same UI actions over and over again is slow, and unnecessary. Try to take as many shortcuts as possible."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "5. Write TypeScript definitions"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can describe the method signature for your custom command, allowing IntelliSense to show helpful documentation."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "6. Create a function that adds the custom command"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Cypress "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/references/changelog.md#12-17-4",
            "children": [
              {
                "type": "text",
                "value": "12.17.4"
              }
            ]
          },
          {
            "type": "text",
            "value": " includes a Webpack upgrade (v4 to v5), which "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://webpack.js.org/blog/2020-10-10-webpack-5-release/#inner-module-tree-shaking",
            "children": [
              {
                "type": "text",
                "value": "tree shakes out"
              }
            ]
          },
          {
            "type": "text",
            "value": " any side-effects or files that only include side-effects."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you are using TypeScript and have Webpack's `sideEffects:false` set in your package.json, custom Cypress commands will not be registered by the common pattern of writing the commands in one file and having them be run as a side effect of importing the file. For example:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// cypress/support/commands.tsCypress.Commands.add(\"login\", (email, password) => { ... })"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// cypress/support/e2e.tsimport './commands'"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To support `sideEffects:false`, you can wrap the Cypress commands in a function that will be imported by the support file."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// cypress/support/commands.tsexport function registerCommands(){  Cypress.Commands.add(\"login\", (email, password) => { ... })}"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// cypress/support/e2e.tsimport { registerCommands } from './commands'registerCommands()"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "History"
          }
        ]
      },
      {
        "type": "table",
        "align": [
          null,
          null
        ],
        "children": [
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Version"
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "Changes"
                  }
                ]
              }
            ]
          },
          {
            "type": "tableRow",
            "children": [
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/app/references/changelog.md#0-20-0",
                    "children": [
                      {
                        "type": "text",
                        "value": "0.20.0"
                      }
                    ]
                  }
                ]
              },
              {
                "type": "tableCell",
                "children": [
                  {
                    "type": "text",
                    "value": "`Cypress.Commands` API added"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "See also"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "See how to add "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/app/tooling/typescript-support.md#Types-for-Custom-Commands",
                    "children": [
                      {
                        "type": "text",
                        "value": "TypeScript support for custom commands"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": true,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "["
                  }
                ]
              },
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Plugins using custom commands"
                  }
                ]
              },
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "](/llm/markdown/app/plugins/plugins-list.md#custom-commands)"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/cypress-api/cypress-log.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "Cypress.log()"
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/app/references/recipes.md#Logging-In",
                    "children": [
                      {
                        "type": "text",
                        "value": "Recipe: Logging In"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  "token_estimate": 4039
}