{
  "doc": {
    "id": "api/cypress-api/custom-queries",
    "title": "Custom Queries in Cypress",
    "description": "Learn how to create custom queries in Cypress, which are synchronous, retriable, and idempotent.",
    "section": "api",
    "source_path": "/llm/markdown/api/cypress-api/custom-queries.md",
    "version": "e6988a974973e9090ce70406c38cb2b9e0eac9fa",
    "updated_at": "2026-05-15T15:50:22.536Z",
    "headings": [
      {
        "id": "api/cypress-api/custom-queries#custom-queries",
        "text": "Custom Queries",
        "level": 1
      },
      {
        "id": "api/cypress-api/custom-queries#syntax",
        "text": "Syntax",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-queries#usage",
        "text": "Usage",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-queries#arguments",
        "text": "Arguments",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-queries#examples",
        "text": "Examples",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-queries#focused",
        "text": ".focused()",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-queries#the-outer-function",
        "text": "The outer function",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-queries#the-inner-function",
        "text": "The inner function",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-queries#overwriting-existing-queries",
        "text": "Overwriting Existing Queries",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-queries#adding-alias-support-to-contains",
        "text": "Adding alias support to .contains()",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-queries#validation",
        "text": "Validation",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-queries#notes",
        "text": "Notes",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-queries#best-practices",
        "text": "Best Practices",
        "level": 3
      },
      {
        "id": "api/cypress-api/custom-queries#1-dont-make-everything-a-custom-query",
        "text": "1. Don't make everything a custom query",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-queries#2-dont-overcomplicate-things",
        "text": "2. Don't overcomplicate things",
        "level": 4
      },
      {
        "id": "api/cypress-api/custom-queries#history",
        "text": "History",
        "level": 2
      },
      {
        "id": "api/cypress-api/custom-queries#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "api/cypress-api/custom-queries#syntax",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Syntax",
      "heading_level": 2,
      "content_markdown": "## Syntax\n\n```\nCypress.Commands.addQuery(name, callbackFn)Cypress.Commands.overwriteQuery(name, callbackFn)\n```\n\n### Usage\n\n**Correct Usage**\n\n```\nCypress.Commands.addQuery('getById', function (id) {  return (subject) => newSubject})Cypress.Commands.overwriteQuery('get', function (originalFn, ...args) {  return originalFn.apply(this, args)})\n```\n\n### Arguments\n\n**name _(String)_**\n\nThe name of the query you're adding.\n\n**callbackFn _(Function)_**\n\nPass a function that receives the arguments passed to the query.\n\nThis outer function is invoked once. It should return a function that takes a subject and returns a new subject; this inner function might be called multiple times.\n\nThe query API relies on `this` to set timeouts, which means that `callbackFn` should always use `function () {}` and not be an arrow function (`() => {}`).\n",
      "section": "api",
      "anchors": [
        "syntax"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 144
    },
    {
      "id": "api/cypress-api/custom-queries#arguments",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Arguments",
      "heading_level": 3,
      "content_markdown": "### Arguments\n\n**name _(String)_**\n\nThe name of the query you're adding.\n\n**callbackFn _(Function)_**\n\nPass a function that receives the arguments passed to the query.\n\nThis outer function is invoked once. It should return a function that takes a subject and returns a new subject; this inner function might be called multiple times.\n\nThe query API relies on `this` to set timeouts, which means that `callbackFn` should always use `function () {}` and not be an arrow function (`() => {}`).\n",
      "section": "api",
      "anchors": [
        "arguments"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 107
    },
    {
      "id": "api/cypress-api/custom-queries#examples",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Examples",
      "heading_level": 2,
      "content_markdown": "## Examples\n\n### `.focused()`\n\nThe callback function can be thought of as two separate parts. The _outer function_, which is invoked once, where you perform setup and state management, and the _query function_, which might be called repeatedly.\n\nLet's look at an example. This is actual Cypress code - how `.focused()` is implemented internally, with some small adjustments to make it work from a support file. The only thing omitted here for simplicity is the TypeScript definitions.\n\n```\nCypress.Commands.addQuery('focused2', function focused2(options = {}) {  const log = options.log !== false && Cypress.log({ timeout: options.timeout })  this.set('timeout', options.timeout)  return () => {    let $el = cy.getFocused()    log &&      cy.state('current') === this &&      log.set({        $el,        consoleProps: () => {          return {            Yielded: $el?.length ? $el[0] : '--nothing--',            Elements: $el != null ? $el.length : 0,          }        },      })    if (!$el) {      $el = cy.$$(null)      $el.selector = 'focused'    }    return $el  }})\n```\n\n#### The outer function\n\nThe outer function is called once each time test uses the query. It performs setup and state management:\n\n```\nfunction focused2(options = {}) {  const log = options.log !== false && Cypress.log({ timeout: options.timeout })  this.set('timeout', options.timeout)  return () => { ... } // Inner function}\n```\n\nLet's look at this piece by piece.\n\n```\nfunction focused2(options = {}) { ... }\n```\n\nCypress passes the outer function whatever arguments the user calls it with; no processing or validation is done on the user's arguments. In our case, `.focused2()` accepts one optional argument, `options`.\n\nIf you wanted to validate the incoming arguments, you might add something like:\n\n```\nif (options === null || !_.isPlainObject(options)) {  const err = `cy.root() requires an \\`options\\` object. You passed in: \\`{options}\\``  throw new TypeError(err)}\n```\n\nThis is a general pattern: when something goes wrong, queries just throw an error. Cypress will handle displaying the error in the Command Log.\n\n```\nconst log = options.log !== false && Cypress.log({ timeout: options.timeout })\n```\n\nIf the user has not set `{ log: false }`, we create a new `Cypress.log()` instance. See [`Cypress.log()`](/llm/markdown/api/cypress-api/cypress-log.md) for more information.\n\nThis line is setup code, so it lives in the outer function - we only want it to run once, creating the log message when Cypress first begins executing this query. We hold onto a reference to `Log` instance. We'll update it later with additional details when the inner function executes.\n\n```\nthis.set('timeout', options.timeout)\n```\n\nWhen defining `focused2()`, it's important to note that we used `function`, rather than an arrow function. This gives us access to `this`, where we can set the `timeout`. If you don't call `this.set('timeout')`, or call it with `null` or `undefined`, your query will use the [default timeout](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Timeouts).\n\n```\n  return () => { ... }\n```\n\n#### The inner function\n\nThe outer function's return value is the inner function.\n\nThe inner function is called any number of times. It's first invoked repeatedly until it passes or the query times out; it can then be invoked again later to determine the subject of future commands, or when the user retrieves an alias.\n\nThe inner function is called with one argument: the previous subject. Cypress performs no validation on this - it could be any type, including `null` or `undefined`.\n\n`.focused2()` ignores any previous subject, but many queries do not - for example, `.contains()` accepts only certain types of subjects. You can use Cypress' builtin `ensures` functions, as `.contains()` does: `cy.ensureSubjectByType(subject, ['optional', 'element', 'window', 'document'], this)`\n\nor you can perform your own validation and simply throw an error: `if (!_.isString(subject)) { throw new Error('MyCustomCommand only accepts strings as a subject!') }`\n\nIf the inner function throws an error, Cypress will retry it after a short delay until it either passes or the query times out. This is the core of Cypress' retry-ability, and the guarantees it provides that your tests interact with the page as a user would.\n\nLooking back to our `.focused2()` example:\n\n```\nreturn () => {  let $el = cy.getFocused()  log &&    cy.state('current') === this &&    log.set({      $el,      consoleProps: () => {        return {          Yielded: $el?.length ? $el[0] : '--nothing--',          Elements: $el != null ? $el.length : 0,        }      },    })  if (!$el) {    $el = cy.$$(null)    $el.selector = 'focused'  }  return $el}\n```\n\nPiece by piece again:\n\n```\nlet $el = cy.getFocused()\n```\n\nThis is the 'business end' of `.focused2()` - finding the element on the page that's currently focused.\n\n```\n    log && cy.state('current') === this && log.set({...})\n```\n\nIf `log` is defined (ie, the user did not pass in `{ log: false }`), and this query is the current command, we update the log message with new information, such as `$el` (the subject we're about to yield from this query), and the `consoleProps`, a function that [returns console output](/llm/markdown/app/core-concepts/open-mode.md#Console-output) for the user.\n\n```\nif (!$el) {  $el = cy.$$(null)  $el.selector = 'focused'}\n```\n\nIf there's no focused element on the page, we create an empty jquery object.\n\n```\nreturn $el\n```\n\nThe return value of the inner function becomes the new subject for the next command.\n\nWith this return value in hand, Cypress verifies any upcoming assertions, such as user's `.should()` commands, or if there are none, the default implicit assertions that the subject should exist.\n\n### Overwriting Existing Queries\n\nYou can also modify the behavior of existing Cypress queries. This is useful to extend the functionality of builtin commands.\n\n`Cypress.Commands.overwriteQuery` can only overwrite queries, not other commands. If you want to modify the behavior of a non-query command, you'll need to use [`Cypress.Commands.overwrite`](/llm/markdown/api/cypress-api/custom-commands.md) instead.\n\nRemember that query functions rely on `this` - when you invoke `originalFn`, be sure to use `.call` or `.apply`.\n\n```\nCypress.Commands.overwriteQuery('get', function (originalFn, ...args) {  console.log('get called with args:', args)  const innerFn = originalFn.apply(this, args)  return (subject) => {    console.log('get inner function called with subject:', subject)    return innerFn(subject)  }})\n```\n\nThe `originalFn` is the function originally passed to `Cypress.Commands.addQuery` - it is a **function that returns a function.** This gives you access to both the outer arguments (before you call `originalFn`) and the inner function (the return value of `originalFn`), giving you a great deal of control over how the query executes.\n\n#### Adding alias support to `.contains()`\n\nIn this example, `cy.contains()` is extended to support querying for aliased subjects, like `cy.contains('@foo')`.\n\n```\nCypress.Commands.overwriteQuery(  'contains',  function (originalFn, filter, text, userOptions) {    if (_.isString(filter) && filter[0] === '@') {      let alias = cy.state('aliases')[filter.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      filter = subject    }    if (_.isString(text) && text[0] === '@') {      let alias = cy.state('aliases')[text.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      text = subject    }    return originalFn.call(this, filter, text, userOptions)  })cy.wrap('li').as('element')cy.wrap('asdf 1').as('content')cy.contains('@element', '@content')\n```\n",
      "section": "api",
      "anchors": [
        "examples"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 1457
    },
    {
      "id": "api/cypress-api/custom-queries#focused",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": ".focused()",
      "heading_level": 3,
      "content_markdown": "### `.focused()`\n\nThe callback function can be thought of as two separate parts. The _outer function_, which is invoked once, where you perform setup and state management, and the _query function_, which might be called repeatedly.\n\nLet's look at an example. This is actual Cypress code - how `.focused()` is implemented internally, with some small adjustments to make it work from a support file. The only thing omitted here for simplicity is the TypeScript definitions.\n\n```\nCypress.Commands.addQuery('focused2', function focused2(options = {}) {  const log = options.log !== false && Cypress.log({ timeout: options.timeout })  this.set('timeout', options.timeout)  return () => {    let $el = cy.getFocused()    log &&      cy.state('current') === this &&      log.set({        $el,        consoleProps: () => {          return {            Yielded: $el?.length ? $el[0] : '--nothing--',            Elements: $el != null ? $el.length : 0,          }        },      })    if (!$el) {      $el = cy.$$(null)      $el.selector = 'focused'    }    return $el  }})\n```\n\n#### The outer function\n\nThe outer function is called once each time test uses the query. It performs setup and state management:\n\n```\nfunction focused2(options = {}) {  const log = options.log !== false && Cypress.log({ timeout: options.timeout })  this.set('timeout', options.timeout)  return () => { ... } // Inner function}\n```\n\nLet's look at this piece by piece.\n\n```\nfunction focused2(options = {}) { ... }\n```\n\nCypress passes the outer function whatever arguments the user calls it with; no processing or validation is done on the user's arguments. In our case, `.focused2()` accepts one optional argument, `options`.\n\nIf you wanted to validate the incoming arguments, you might add something like:\n\n```\nif (options === null || !_.isPlainObject(options)) {  const err = `cy.root() requires an \\`options\\` object. You passed in: \\`{options}\\``  throw new TypeError(err)}\n```\n\nThis is a general pattern: when something goes wrong, queries just throw an error. Cypress will handle displaying the error in the Command Log.\n\n```\nconst log = options.log !== false && Cypress.log({ timeout: options.timeout })\n```\n\nIf the user has not set `{ log: false }`, we create a new `Cypress.log()` instance. See [`Cypress.log()`](/llm/markdown/api/cypress-api/cypress-log.md) for more information.\n\nThis line is setup code, so it lives in the outer function - we only want it to run once, creating the log message when Cypress first begins executing this query. We hold onto a reference to `Log` instance. We'll update it later with additional details when the inner function executes.\n\n```\nthis.set('timeout', options.timeout)\n```\n\nWhen defining `focused2()`, it's important to note that we used `function`, rather than an arrow function. This gives us access to `this`, where we can set the `timeout`. If you don't call `this.set('timeout')`, or call it with `null` or `undefined`, your query will use the [default timeout](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Timeouts).\n\n```\n  return () => { ... }\n```\n\n#### The inner function\n\nThe outer function's return value is the inner function.\n\nThe inner function is called any number of times. It's first invoked repeatedly until it passes or the query times out; it can then be invoked again later to determine the subject of future commands, or when the user retrieves an alias.\n\nThe inner function is called with one argument: the previous subject. Cypress performs no validation on this - it could be any type, including `null` or `undefined`.\n\n`.focused2()` ignores any previous subject, but many queries do not - for example, `.contains()` accepts only certain types of subjects. You can use Cypress' builtin `ensures` functions, as `.contains()` does: `cy.ensureSubjectByType(subject, ['optional', 'element', 'window', 'document'], this)`\n\nor you can perform your own validation and simply throw an error: `if (!_.isString(subject)) { throw new Error('MyCustomCommand only accepts strings as a subject!') }`\n\nIf the inner function throws an error, Cypress will retry it after a short delay until it either passes or the query times out. This is the core of Cypress' retry-ability, and the guarantees it provides that your tests interact with the page as a user would.\n\nLooking back to our `.focused2()` example:\n\n```\nreturn () => {  let $el = cy.getFocused()  log &&    cy.state('current') === this &&    log.set({      $el,      consoleProps: () => {        return {          Yielded: $el?.length ? $el[0] : '--nothing--',          Elements: $el != null ? $el.length : 0,        }      },    })  if (!$el) {    $el = cy.$$(null)    $el.selector = 'focused'  }  return $el}\n```\n\nPiece by piece again:\n\n```\nlet $el = cy.getFocused()\n```\n\nThis is the 'business end' of `.focused2()` - finding the element on the page that's currently focused.\n\n```\n    log && cy.state('current') === this && log.set({...})\n```\n\nIf `log` is defined (ie, the user did not pass in `{ log: false }`), and this query is the current command, we update the log message with new information, such as `$el` (the subject we're about to yield from this query), and the `consoleProps`, a function that [returns console output](/llm/markdown/app/core-concepts/open-mode.md#Console-output) for the user.\n\n```\nif (!$el) {  $el = cy.$$(null)  $el.selector = 'focused'}\n```\n\nIf there's no focused element on the page, we create an empty jquery object.\n\n```\nreturn $el\n```\n\nThe return value of the inner function becomes the new subject for the next command.\n\nWith this return value in hand, Cypress verifies any upcoming assertions, such as user's `.should()` commands, or if there are none, the default implicit assertions that the subject should exist.\n",
      "section": "api",
      "anchors": [
        "focused"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 1152
    },
    {
      "id": "api/cypress-api/custom-queries#the-outer-function",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "The outer function",
      "heading_level": 4,
      "content_markdown": "#### The outer function\n\nThe outer function is called once each time test uses the query. It performs setup and state management:\n\n```\nfunction focused2(options = {}) {  const log = options.log !== false && Cypress.log({ timeout: options.timeout })  this.set('timeout', options.timeout)  return () => { ... } // Inner function}\n```\n\nLet's look at this piece by piece.\n\n```\nfunction focused2(options = {}) { ... }\n```\n\nCypress passes the outer function whatever arguments the user calls it with; no processing or validation is done on the user's arguments. In our case, `.focused2()` accepts one optional argument, `options`.\n\nIf you wanted to validate the incoming arguments, you might add something like:\n\n```\nif (options === null || !_.isPlainObject(options)) {  const err = `cy.root() requires an \\`options\\` object. You passed in: \\`{options}\\``  throw new TypeError(err)}\n```\n\nThis is a general pattern: when something goes wrong, queries just throw an error. Cypress will handle displaying the error in the Command Log.\n\n```\nconst log = options.log !== false && Cypress.log({ timeout: options.timeout })\n```\n\nIf the user has not set `{ log: false }`, we create a new `Cypress.log()` instance. See [`Cypress.log()`](/llm/markdown/api/cypress-api/cypress-log.md) for more information.\n\nThis line is setup code, so it lives in the outer function - we only want it to run once, creating the log message when Cypress first begins executing this query. We hold onto a reference to `Log` instance. We'll update it later with additional details when the inner function executes.\n\n```\nthis.set('timeout', options.timeout)\n```\n\nWhen defining `focused2()`, it's important to note that we used `function`, rather than an arrow function. This gives us access to `this`, where we can set the `timeout`. If you don't call `this.set('timeout')`, or call it with `null` or `undefined`, your query will use the [default timeout](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Timeouts).\n\n```\n  return () => { ... }\n```\n",
      "section": "api",
      "anchors": [
        "the-outer-function"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 404
    },
    {
      "id": "api/cypress-api/custom-queries#the-inner-function",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "The inner function",
      "heading_level": 4,
      "content_markdown": "#### The inner function\n\nThe outer function's return value is the inner function.\n\nThe inner function is called any number of times. It's first invoked repeatedly until it passes or the query times out; it can then be invoked again later to determine the subject of future commands, or when the user retrieves an alias.\n\nThe inner function is called with one argument: the previous subject. Cypress performs no validation on this - it could be any type, including `null` or `undefined`.\n\n`.focused2()` ignores any previous subject, but many queries do not - for example, `.contains()` accepts only certain types of subjects. You can use Cypress' builtin `ensures` functions, as `.contains()` does: `cy.ensureSubjectByType(subject, ['optional', 'element', 'window', 'document'], this)`\n\nor you can perform your own validation and simply throw an error: `if (!_.isString(subject)) { throw new Error('MyCustomCommand only accepts strings as a subject!') }`\n\nIf the inner function throws an error, Cypress will retry it after a short delay until it either passes or the query times out. This is the core of Cypress' retry-ability, and the guarantees it provides that your tests interact with the page as a user would.\n\nLooking back to our `.focused2()` example:\n\n```\nreturn () => {  let $el = cy.getFocused()  log &&    cy.state('current') === this &&    log.set({      $el,      consoleProps: () => {        return {          Yielded: $el?.length ? $el[0] : '--nothing--',          Elements: $el != null ? $el.length : 0,        }      },    })  if (!$el) {    $el = cy.$$(null)    $el.selector = 'focused'  }  return $el}\n```\n\nPiece by piece again:\n\n```\nlet $el = cy.getFocused()\n```\n\nThis is the 'business end' of `.focused2()` - finding the element on the page that's currently focused.\n\n```\n    log && cy.state('current') === this && log.set({...})\n```\n\nIf `log` is defined (ie, the user did not pass in `{ log: false }`), and this query is the current command, we update the log message with new information, such as `$el` (the subject we're about to yield from this query), and the `consoleProps`, a function that [returns console output](/llm/markdown/app/core-concepts/open-mode.md#Console-output) for the user.\n\n```\nif (!$el) {  $el = cy.$$(null)  $el.selector = 'focused'}\n```\n\nIf there's no focused element on the page, we create an empty jquery object.\n\n```\nreturn $el\n```\n\nThe return value of the inner function becomes the new subject for the next command.\n\nWith this return value in hand, Cypress verifies any upcoming assertions, such as user's `.should()` commands, or if there are none, the default implicit assertions that the subject should exist.\n",
      "section": "api",
      "anchors": [
        "the-inner-function"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 551
    },
    {
      "id": "api/cypress-api/custom-queries#overwriting-existing-queries",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Overwriting Existing Queries",
      "heading_level": 3,
      "content_markdown": "### Overwriting Existing Queries\n\nYou can also modify the behavior of existing Cypress queries. This is useful to extend the functionality of builtin commands.\n\n`Cypress.Commands.overwriteQuery` can only overwrite queries, not other commands. If you want to modify the behavior of a non-query command, you'll need to use [`Cypress.Commands.overwrite`](/llm/markdown/api/cypress-api/custom-commands.md) instead.\n\nRemember that query functions rely on `this` - when you invoke `originalFn`, be sure to use `.call` or `.apply`.\n\n```\nCypress.Commands.overwriteQuery('get', function (originalFn, ...args) {  console.log('get called with args:', args)  const innerFn = originalFn.apply(this, args)  return (subject) => {    console.log('get inner function called with subject:', subject)    return innerFn(subject)  }})\n```\n\nThe `originalFn` is the function originally passed to `Cypress.Commands.addQuery` - it is a **function that returns a function.** This gives you access to both the outer arguments (before you call `originalFn`) and the inner function (the return value of `originalFn`), giving you a great deal of control over how the query executes.\n\n#### Adding alias support to `.contains()`\n\nIn this example, `cy.contains()` is extended to support querying for aliased subjects, like `cy.contains('@foo')`.\n\n```\nCypress.Commands.overwriteQuery(  'contains',  function (originalFn, filter, text, userOptions) {    if (_.isString(filter) && filter[0] === '@') {      let alias = cy.state('aliases')[filter.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      filter = subject    }    if (_.isString(text) && text[0] === '@') {      let alias = cy.state('aliases')[text.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      text = subject    }    return originalFn.call(this, filter, text, userOptions)  })cy.wrap('li').as('element')cy.wrap('asdf 1').as('content')cy.contains('@element', '@content')\n```\n",
      "section": "api",
      "anchors": [
        "overwriting-existing-queries"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 303
    },
    {
      "id": "api/cypress-api/custom-queries#adding-alias-support-to-contains",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Adding alias support to .contains()",
      "heading_level": 4,
      "content_markdown": "#### Adding alias support to `.contains()`\n\nIn this example, `cy.contains()` is extended to support querying for aliased subjects, like `cy.contains('@foo')`.\n\n```\nCypress.Commands.overwriteQuery(  'contains',  function (originalFn, filter, text, userOptions) {    if (_.isString(filter) && filter[0] === '@') {      let alias = cy.state('aliases')[filter.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      filter = subject    }    if (_.isString(text) && text[0] === '@') {      let alias = cy.state('aliases')[text.slice(1)]      let subject = cy.getSubjectFromChain(alias?.subjectChain)      text = subject    }    return originalFn.call(this, filter, text, userOptions)  })cy.wrap('li').as('element')cy.wrap('asdf 1').as('content')cy.contains('@element', '@content')\n```\n",
      "section": "api",
      "anchors": [
        "adding-alias-support-to-contains"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 101
    },
    {
      "id": "api/cypress-api/custom-queries#validation",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Validation",
      "heading_level": 2,
      "content_markdown": "## Validation\n\nAs noted in the examples above, Cypress performs very little validation around queries - it is the responsibility of each implementation to ensure that its arguments and subject are of the correct type.\n\nCypress has several builtin 'ensures' which can be helpful in this regard:\n\n*   `cy.ensureSubjectByType(subject, types, this)`: Accepts an array with any of the strings `optional`, `element`, `document`, or `window`. `ensureSubjectByType` is how [`prevSubject` validation](/llm/markdown/api/cypress-api/custom-commands.md#Validations) is implemented for commmands.\n*   `cy.ensureElement(subject, queryName)`: Ensure that the passed in `subject` is one or more DOM elements.\n*   `cy.ensureWindow(subject)`: Ensure that the passed in `subject` is a `window`.\n*   `cy.ensureDocument(subject)`: Ensure that the passed in `subject` is a `document`.\n*   `cy.ensureAttached(subject, queryName)`: Ensure that DOM element(s) are attached to the page.\n*   `cy.ensureNotDisabled(subject)`: Ensure that form elements aren't disabled.\n*   `cy.ensureVisibility(subject)`: Ensure that a DOM element is visible on the page.\n\nThere's nothing special about these functions - they simply validate their argument and throw an error if the check fails. You can throw errors of any type at any time inside your queries - Cypress will catch and handle it appropriately.\n",
      "section": "api",
      "anchors": [
        "validation"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 244
    },
    {
      "id": "api/cypress-api/custom-queries#notes",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Notes",
      "heading_level": 2,
      "content_markdown": "## Notes\n\n### Best Practices\n\n#### 1\\. Don't make everything a custom query\n\nCustom queries work well when you're needing to describe behavior that's desirable across **all of your tests**. Examples would be `cy.findBreadcrumbs()` or `cy.getLoginForm()`. These are specific to your application and can be used everywhere.\n\nHowever, 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 than it is to implement a custom query.\n\n#### 2\\. Don't overcomplicate things\n\nEvery custom query you write is generally an abstraction for locating elements on the page. That means you and your team members exert much more mental effort to understand what your custom command does.\n\nThere's no reason to add this level of complexity when the builtin queries are already quite expressive and powerful.\n\nDon't do things like:\n\n*   `cy.getButton()`\n*   `.getFirstTableRow()`\n\nBoth of these are wrapping `cy.get(selector)`. It's completely unnecessary. Just call `.get('button')` or `.get('tr:first')`.\n\nTesting 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.\n\nTry not to overcomplicate things and create too many abstractions.\n",
      "section": "api",
      "anchors": [
        "notes"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 305
    },
    {
      "id": "api/cypress-api/custom-queries#best-practices",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "Best Practices",
      "heading_level": 3,
      "content_markdown": "### Best Practices\n\n#### 1\\. Don't make everything a custom query\n\nCustom queries work well when you're needing to describe behavior that's desirable across **all of your tests**. Examples would be `cy.findBreadcrumbs()` or `cy.getLoginForm()`. These are specific to your application and can be used everywhere.\n\nHowever, 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 than it is to implement a custom query.\n\n#### 2\\. Don't overcomplicate things\n\nEvery custom query you write is generally an abstraction for locating elements on the page. That means you and your team members exert much more mental effort to understand what your custom command does.\n\nThere's no reason to add this level of complexity when the builtin queries are already quite expressive and powerful.\n\nDon't do things like:\n\n*   `cy.getButton()`\n*   `.getFirstTableRow()`\n\nBoth of these are wrapping `cy.get(selector)`. It's completely unnecessary. Just call `.get('button')` or `.get('tr:first')`.\n\nTesting 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.\n\nTry not to overcomplicate things and create too many abstractions.\n",
      "section": "api",
      "anchors": [
        "best-practices"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 303
    },
    {
      "id": "api/cypress-api/custom-queries#1-dont-make-everything-a-custom-query",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "1. Don't make everything a custom query",
      "heading_level": 4,
      "content_markdown": "#### 1\\. Don't make everything a custom query\n\nCustom queries work well when you're needing to describe behavior that's desirable across **all of your tests**. Examples would be `cy.findBreadcrumbs()` or `cy.getLoginForm()`. These are specific to your application and can be used everywhere.\n\nHowever, 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 than it is to implement a custom query.\n",
      "section": "api",
      "anchors": [
        "1-dont-make-everything-a-custom-query"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 105
    },
    {
      "id": "api/cypress-api/custom-queries#2-dont-overcomplicate-things",
      "doc_id": "api/cypress-api/custom-queries",
      "heading": "2. Don't overcomplicate things",
      "heading_level": 4,
      "content_markdown": "#### 2\\. Don't overcomplicate things\n\nEvery custom query you write is generally an abstraction for locating elements on the page. That means you and your team members exert much more mental effort to understand what your custom command does.\n\nThere's no reason to add this level of complexity when the builtin queries are already quite expressive and powerful.\n\nDon't do things like:\n\n*   `cy.getButton()`\n*   `.getFirstTableRow()`\n\nBoth of these are wrapping `cy.get(selector)`. It's completely unnecessary. Just call `.get('button')` or `.get('tr:first')`.\n\nTesting 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.\n\nTry not to overcomplicate things and create too many abstractions.\n",
      "section": "api",
      "anchors": [
        "2-dont-overcomplicate-things"
      ],
      "path": "/llm/json/chunked/api/cypress-api/custom-queries.json",
      "token_estimate": 193
    }
  ]
}