{
  "doc": {
    "id": "app/write-tests/component-testing/vue/examples",
    "title": "Vue examples",
    "description": "Examples for testing Vue components with Cypress.",
    "section": "app",
    "source_path": "/llm/markdown/app/write-tests/component-testing/vue/examples.md",
    "version": "e4f4d57da4cfedd520d3d465137d387fd54b532f",
    "updated_at": "2026-09-08T17:49:12.214Z",
    "headings": [
      {
        "id": "app/write-tests/component-testing/vue/examples#vue-examples",
        "text": "Vue Examples",
        "level": 1
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#passing-data-to-a-component",
        "text": "Passing Data to a Component",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#testing-event-handlers",
        "text": "Testing Event Handlers",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#using-jsx",
        "text": "Using JSX",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#testing-error-states",
        "text": "Testing Error States",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#testing-with-onerrorcaptured",
        "text": "Testing with onErrorCaptured",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#using-slots",
        "text": "Using Slots",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#default-slot",
        "text": "Default Slot",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#named-slot",
        "text": "Named Slot",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#using-vue-test-utils",
        "text": "Using Vue Test Utils",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#custom-mount-commands",
        "text": "Custom Mount Commands",
        "level": 2
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#customizing-cy-mount",
        "text": "Customizing cy.mount()",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#replicating-plugins",
        "text": "Replicating Plugins",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#replicating-the-expected-component-hierarchy",
        "text": "Replicating the expected Component Hierarchy",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#vue-router",
        "text": "Vue Router",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#vuex",
        "text": "Vuex",
        "level": 3
      },
      {
        "id": "app/write-tests/component-testing/vue/examples#global-components",
        "text": "Global Components",
        "level": 3
      }
    ]
  },
  "content": {
    "type": "root",
    "children": [
      {
        "type": "heading",
        "depth": 1,
        "children": [
          {
            "type": "text",
            "value": "Vue Examples"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To mount a component with `cy.mount()`, import the component and pass it to the method:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { Stepper } from './Stepper.vue'\n\nit('mounts', () => {\n  cy.mount(Stepper)\n})"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Passing Data to a Component"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can pass props and events to a component by setting `props` in the options:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.mount(Stepper, {\n  props: {\n    initial: 100,\n  },\n})"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Testing Event Handlers"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Pass a Cypress "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/guides/stubs-spies-and-clocks.md#Spies",
            "children": [
              {
                "type": "text",
                "value": "spy"
              }
            ]
          },
          {
            "type": "text",
            "value": " to an event prop and validate it was called:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "it('clicking + fires a change event with the incremented value', () => {\n  const onChangeSpy = cy.spy().as('onChangeSpy')\n  cy.mount(Stepper, { props: { onChange: onChangeSpy } })\n  cy.get('[data-cy=increment]').click()\n  cy.get('@onChangeSpy').should('have.been.calledWith', 1)\n})"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Using JSX"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The mount command also supports JSX syntax (provided that you've configured your bundler to support transpiling JSX or TSX files). Some might find using JSX syntax beneficial when writing tests."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Sample with JSX:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "it('clicking + fires a change event with the incremented value', () => {\n  const onChangeSpy = cy.spy().as('onChangeSpy')\n  cy.mount(<Stepper initial={100} onChange={onChangeSpy} />)\n  cy.get('[data-cy=increment]').click()\n  cy.get('@onChangeSpy').should('have.been.calledWith', 101)\n})"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Testing Error States"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "`cy.mount()` is an asynchronous Cypress command: it enqueues the mount and returns immediately, before the component ever renders. When a component throws during render, the error surfaces as an uncaught exception rather than as a synchronous throw."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "By default Cypress fails the test on any uncaught exception, so to assert on a render error you listen for it with "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/cypress-api/catalog-of-events.md#Uncaught-Exceptions",
            "children": [
              {
                "type": "text",
                "value": "`cy.on('uncaught:exception')`"
              }
            ]
          },
          {
            "type": "text",
            "value": " and return `false` to prevent Cypress from failing the test:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import UserProfile from './UserProfile.vue'\n\nit('surfaces a render error', () => {\n  cy.on('uncaught:exception', (err) => {\n    // Assert on the error thrown during render...\n    expect(err.message).to.include('user is required')\n\n    // ...and return false so Cypress does not fail the test.\n    return false\n  })\n\n  cy.mount(UserProfile)\n})"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Testing with `onErrorCaptured`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The recommended way to render fallback UI in Vue is a wrapper component that uses the "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured",
            "children": [
              {
                "type": "text",
                "value": "`onErrorCaptured`"
              }
            ]
          },
          {
            "type": "text",
            "value": " lifecycle hook to catch errors from its descendants. You can mount a component inside such a wrapper and assert that the fallback UI is displayed:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// ErrorBoundary.cy.js\nimport { h } from 'vue'\nimport ErrorBoundary from './ErrorBoundary.vue'\nimport ChildWithError from './ChildWithError.vue'\n\nit('displays the fallback UI on error', () => {\n  // onErrorCaptured renders fallback UI, but it does NOT stop the error from\n  // propagating to Cypress as an uncaught exception. Cypress fails on uncaught\n  // exceptions by default, so we must still suppress that behavior.\n  cy.on('uncaught:exception', (err) => {\n    // Only suppress the specific error we expect.\n    expect(err.message).to.include('I crashed!')\n\n    return false\n  })\n\n  cy.mount(() => h(ErrorBoundary, () => h(ChildWithError)))\n\n  cy.get('[data-cy=fallback]').should('contain', 'Something went wrong.')\n})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Where `ErrorBoundary` captures the error with `onErrorCaptured` and renders fallback UI:"
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "<!-- ErrorBoundary.vue -->\n<template>\n  <div v-if=\"error\" data-cy=\"fallback\">Something went wrong.</div>\n  <slot v-else />\n</template>\n\n<script setup>\n  import { ref, onErrorCaptured } from 'vue'\n\n  const error = ref(null)\n\n  onErrorCaptured((err) => {\n    error.value = err\n\n    // Returning false stops the error from propagating further up the Vue tree,\n    // but it still reaches Cypress as an uncaught exception.\n    return false\n  })\n</script>"
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Using Slots"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Default Slot"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "DefaultSlot.cy.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "DefaultSlot.cy.jsx (JSX)"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "DefaultSlot.vue"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import DefaultSlot from './DefaultSlot.vue'\n\ndescribe('<DefaultSlot />', () => {\n  it('renders', () => {\n    cy.mount(DefaultSlot, {\n      slots: {\n        default: 'Hello there!',\n      },\n    })\n    cy.get('div.content').should('have.text', 'Hello there!')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import DefaultSlot from './DefaultSlot.vue'\n\ndescribe('<DefaultSlot />', () => {\n  it('renders', () => {\n    cy.mount(<DefaultSlot>Hello there!</DefaultSlot>)\n    cy.get('div.content').should('have.text', 'Hello there!')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "<template>\n  <div>\n    <div class=\"content\">\n      <slot />\n    </div>\n  </div>\n</template>\n\n<script setup></script>"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Named Slot"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "NamedSlot.cy.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "NamedSlot.cy.jsx (JSX)"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "NamedSlot.vue"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import NamedSlot from './NamedSlot.vue'\n\ndescribe('<NamedSlot />', () => {\n  it('renders', () => {\n    const slots = {\n      header: 'my header',\n      footer: 'my footer',\n    }\n    cy.mount(NamedSlot, {\n      slots,\n    })\n    cy.get('header').should('have.text', 'my header')\n    cy.get('footer').should('have.text', 'my footer')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import NamedSlot from './NamedSlot.vue'\n\ndescribe('<NamedSlot />', () => {\n  it('renders', () => {\n    const slots = {\n      header: 'my header',\n      footer: 'my footer',\n    }\n    cy.mount(<NamedSlot>{{ ...slots }}</NamedSlot>)\n    cy.get('header').should('have.text', 'my header')\n    cy.get('footer').should('have.text', 'my footer')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "<template>\n  <div>\n    <header>\n      <slot name=\"header\" />\n    </header>\n    <footer>\n      <slot name=\"footer\" />\n    </footer>\n  </div>\n</template>\n\n<script setup></script>"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "For more info on testing Vue components with slots, refer to the "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://test-utils.vuejs.org/guide/advanced/slots.html",
            "children": [
              {
                "type": "text",
                "value": "Vue Test Utils Slots guide"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Using Vue Test Utils"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In order to encourage interoperability between your existing component tests and Cypress, we support using Vue Test Utils' API."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.mount(Stepper).then(({ wrapper, component }) => {\n  // `wrapper` is the Vue Test Utils wrapper\n  // `component` is the component instance itself\n})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you intend to use the `wrapper` frequently and use Vue Test Util's API, we recommend you write a "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/mount.md",
            "children": [
              {
                "type": "text",
                "value": "custom mount command"
              }
            ]
          },
          {
            "type": "text",
            "value": " and create a Cypress alias to get back at the `wrapper`."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\n\nCypress.Commands.add('mount', (...args) => {\n  return mount(...args).then(({ wrapper }) => {\n    return cy.wrap(wrapper).as('vue')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\n\ntype MountParams = Parameters<typeof mount>\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Mounts a Vue component and aliases the Vue Test Utils wrapper as `@vue`\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(...args: MountParams): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (...args) => {\n  return mount(...args).then(({ wrapper }) => {\n    return cy.wrap(wrapper).as('vue')\n  })\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "// the \"@vue\" alias will now work anywhere\n// after you've mounted your component\ncy.mount(Stepper).doStuff().get('@vue') // The subject is now the Vue Wrapper"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This means that you are able to get to the resulting `wrapper` returned from the `mount` command and use `wrapper.emitted()` in order to gain access to Native DOM events that were fired, as well as custom events that were emitted by your component under test."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Because `wrapper.emitted()` is only data, and NOT spy-based you will have to unpack its results to write assertions."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Your test failure messages will not be as helpful because you're not able to use the Sinon-Chai library that Cypress ships, which comes with methods such as `to.have.been.called` and `to.have.been.calledWith`."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Usage of the `cy.get('@vue')` alias may look something like the below code snippet."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Notice that we're using the `'should'` function signature in order to take advantage of Cypress's "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/guides/test-retries.md",
            "children": [
              {
                "type": "text",
                "value": "retryability"
              }
            ]
          },
          {
            "type": "text",
            "value": ". If we chained using `cy.then` instead of `cy.should`, we may run into the kinds of issues you have in Vue Test Utils tests where you have to use `await` frequently in order to make sure the DOM has updated or any reactive events have fired."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "With emitted"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "With spies"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "cy.mount(Stepper, { props: { initial: 100 } })\ncy.get(incrementSelector).click()\ncy.get('@vue').should(({ wrapper }) => {\n  expect(wrapper.emitted('change')).to.have.length\n  expect(wrapper.emitted('change')[0][0]).to.equal('101')\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "const onChangeSpy = cy.spy().as('onChangeSpy')\n\ncy.mount(Stepper, { props: { initial: 100, onChange: onChangeSpy } })\n\ncy.get(incrementSelector).click()\ncy.get('@onChangeSpy').should('have.been.calledWith', '101')"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Regardless of our recommendation to use spies instead of the internal Vue Test Utils API, you may decide to continue using `emitted` as it automatically records every single event emitted from the component, and so you won't have to create a spy for every event emitted."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This auto-spying behavior could be useful for components that emit many custom events."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Custom Mount Commands"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Customizing `cy.mount()`"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "While you can use the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/app/component-testing/vue/api.md#mount",
            "children": [
              {
                "type": "text",
                "value": "mount()"
              }
            ]
          },
          {
            "type": "text",
            "value": " function in your tests, we recommend using "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/mount.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.mount()`"
              }
            ]
          },
          {
            "type": "text",
            "value": ", which is a "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/cypress-api/custom-commands.md",
            "children": [
              {
                "type": "text",
                "value": "custom command"
              }
            ]
          },
          {
            "type": "text",
            "value": " that is defined in the cypress/support/component.js file:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\n\nCypress.Commands.add('mount', mount)"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      mount: typeof mount\n    }\n  }\n}\n\nCypress.Commands.add('mount', mount)"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This allows you to use `cy.mount()` in any test without having to import the `mount()` function in each and every spec file."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "By default, `cy.mount()` is a simple passthrough to `mount()`, however, you can customize `cy.mount()` to fit your needs. For instance, if you are using plugins or other global app-level setups in your Vue app, you can configure them here."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Below are a few examples that demonstrate using a custom mount command. These examples can be adjusted for most other providers that you will need to support."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Replicating Plugins"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Most applications will have state management or routing. Both of these are Vue plugins."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "With JSX"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { createPinia } from 'pinia' // or Vuex\nimport { createI18n } from 'vue-i18n'\nimport { mount } from 'cypress/vue'\nimport { h } from 'vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst i18nOptions = {\n  locale: 'en',\n  messages: {\n    en: {\n      hello: 'hello!',\n    },\n    ja: {\n      hello: 'こんにちは！',\n    },\n  },\n}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(createPinia())\n  args.global.plugins.push(createI18n())\n\n  return mount(\n    () => {\n      return h(VApp, {}, component)\n    },\n    ...args\n  )\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { createPinia } from 'pinia' // or Vuex\nimport { createI18n } from 'vue-i18n'\nimport { mount } from 'cypress/vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst i18nOptions = {\n  locale: 'en',\n  messages: {\n    en: {\n      hello: 'hello!',\n    },\n    ja: {\n      hello: 'こんにちは！',\n    },\n  },\n}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(createPinia())\n  args.global.plugins.push(createI18n())\n\n  // <component> is a built-in component that comes with Vue\n  return mount(\n    () => (\n      <VApp>\n        <component is={component} />\n      </VApp>\n    ),\n    ...args\n  )\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { createPinia } from 'pinia' // or Vuex\nimport { createI18n } from 'vue-i18n'\nimport { mount } from 'cypress/vue'\nimport { h } from 'vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst i18nOptions = {\n  locale: 'en',\n  messages: {\n    en: {\n      hello: 'hello!',\n    },\n    ja: {\n      hello: 'こんにちは！',\n    },\n  },\n}\n\ntype MountParams = Parameters<typeof mount>\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Helper mount function for Vue Components\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(...args: MountParams): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(createPinia())\n  args.global.plugins.push(createI18n())\n\n  return mount(\n    () => {\n      return h(VApp, {}, component)\n    },\n    ...args\n  )\n})"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Replicating the expected Component Hierarchy"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Some Vue applications, most famously Vue apps built on top of Vuetify, require certain components to be structured in a specific hierarchy."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "All Vuetify applications require that you wrap your app in a `VApp` component when you build it. This is an implementation detail of Vuetify, but once users try to test components that depend on Vuetify, they get Vuetify-specific compilation errors and quickly find out that they need to replicate that component hierarchy any time they need to mount a component that uses a Vuetify component!"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Custom `cy.mount` commands to the rescue! You may find the JSX syntax to be more straightforward."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You'll also need to replicate the plugin setup steps from the Vuetify docs for everything to compile."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "With JSX"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import Vuetify from 'vuetify/lib'\nimport { VApp } from 'vuetify'\nimport { mount } from 'cypress/vue'\nimport { h } from 'vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst vuetifyOptions = {}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(new Vuetify(vuetifyOptions))\n\n  return mount(\n    () => {\n      return h(VApp, {}, component)\n    },\n    ...args\n  )\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import Vuetify from 'vuetify/lib'\nimport { VApp } from 'vuetify'\nimport { mount } from 'cypress/vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst vuetifyOptions = {}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(new Vuetify(vuetifyOptions))\n\n  // <component> is a built-in component that comes with Vue\n  return mount(\n    () => (\n      <VApp>\n        <component is={component} />\n      </VApp>\n    ),\n    ...args\n  )\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import Vuetify from 'vuetify/lib'\nimport { VApp } from 'vuetify'\nimport { mount } from 'cypress/vue'\nimport { h } from 'vue'\n\n// We recommend that you pull this out\n// into a constants file that you share with\n// your main.js file.\nconst vuetifyOptions = {}\n\ntype MountParams = Parameters<typeof mount>\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Helper mount function for Vue Components\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(...args: MountParams): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (component, ...args) => {\n  args.global = args.global || {}\n  args.global.plugins = args.global.plugins || []\n  args.global.plugins.push(new Vuetify(vuetifyOptions))\n\n  return mount(\n    () => {\n      return h(VApp, {}, component)\n    },\n    ...args\n  )\n})"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Vue Router"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To use Vue Router, create a command to register the plugin and pass in a custom implementation of the router via the options param."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Spec Usage"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport { createMemoryHistory, createRouter } from 'vue-router'\nimport { routes } from '../../src/router'\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.plugins = options.global.plugins || []\n\n  // create router if one is not provided\n  if (!options.router) {\n    options.router = createRouter({\n      routes: routes,\n      history: createMemoryHistory(),\n    })\n  }\n\n  // Add router plugin\n  options.global.plugins.push({\n    install(app) {\n      app.use(options.router)\n    },\n  })\n\n  return mount(component, options)\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport { createMemoryHistory, createRouter, Router } from 'vue-router'\nimport { routes } from '../../src/router'\n\ntype MountParams = Parameters<typeof mount>\ntype OptionsParam = MountParams[1] & { router?: Router }\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Helper mount function for Vue Components\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(component: any, options?: OptionsParam): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.plugins = options.global.plugins || []\n\n  // create router if one is not provided\n  if (!options.router) {\n    options.router = createRouter({\n      routes: routes,\n      history: createMemoryHistory(),\n    })\n  }\n\n  // Add router plugin\n  options.global.plugins.push({\n    install(app) {\n      app.use(options.router)\n    },\n  })\n\n  return mount(component, options)\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import Navigation from './Navigation.vue'\nimport { routes } from '../router'\nimport { createMemoryHistory, createRouter } from 'vue-router'\n\nit('home link should be active when url is \"/\"', () => {\n  // No need to pass in custom router as default url is '/'\n  cy.mount(<Navigation />)\n\n  cy.get('a').contains('Home').should('have.class', 'router-link-active')\n})\n\nit('login link should be active when url is \"/login\"', () => {\n  // Create a new router instance for each test\n  const router = createRouter({\n    routes: routes,\n    history: createMemoryHistory(),\n  })\n\n  // Change location to `/login`,\n  // and await on the promise with cy.wrap\n  cy.wrap(router.push('/login'))\n\n  // Pass the already initialized router for use\n  cy.mount(<Navigation />, { router })\n\n  cy.get('a').contains('Login').should('have.class', 'router-link-active')\n})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Calling `router.push()` in the router for Vue 3 is an asynchronous operation. Use the "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/wrap.md",
            "children": [
              {
                "type": "text",
                "value": "cy.wrap"
              }
            ]
          },
          {
            "type": "text",
            "value": " command to have Cypress await the promise's resolve before it continues with other commands:"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Vuex"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To use a component that uses "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://vuex.vuejs.org/",
            "children": [
              {
                "type": "text",
                "value": "Vuex"
              }
            ]
          },
          {
            "type": "text",
            "value": ", create a `mount` command that configures a Vuex store for your component."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Spec Usage"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport { getStore } from '../../src/plugins/store'\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.stubs = options.global.stubs || {}\n  options.global.stubs['transition'] = false\n  options.global.components = options.global.components || {}\n  options.global.plugins = options.global.plugins || []\n\n  // Use store passed in from options, or initialize a new one\n  const { store = getStore(), ...mountOptions } = options\n\n  // Add Vuex plugin\n  options.global.plugins.push({\n    install(app) {\n      app.use(store)\n    },\n  })\n\n  return mount(component, mountOptions)\n})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The `getStore` method is a factory method that initializes Vuex and creates a new store. It is important that the store be initialized with each new test to ensure changes to the store don't affect other tests."
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport { getStore } from '../../src/plugins/store'\nimport { Store } from 'vuex'\n\ntype MountParams = Parameters<typeof mount>\ntype OptionsParam = MountParams[1]\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Helper mount function for Vue Components\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(\n        component: any,\n        options?: OptionsParam & { store?: Store }\n      ): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.stubs = options.global.stubs || {}\n  options.global.stubs['transition'] = false\n  options.global.components = options.global.components || {}\n  options.global.plugins = options.global.plugins || []\n\n  // Use store passed in from options, or initialize a new one\n  const { store = getStore(), ...mountOptions } = options\n\n  // Add Vuex plugin\n  options.global.plugins.push({\n    install(app) {\n      app.use(store)\n    },\n  })\n\n  return mount(component, mountOptions)\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { getStore } from '@/plugins/store'\nimport UserProfile from './UserProfile.vue'\n\nit.only('User profile should display user name', () => {\n  const user = { name: 'test person' }\n\n  // getStore is a factory method that creates a new store\n  const store = getStore()\n\n  // mutate the store with user\n  store.commit('setUser', user)\n\n  cy.mount(UserProfile, {\n    store,\n  })\n\n  cy.get('div.name').should('have.text', user.name)\n})"
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Global Components"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "If you have components that are registered globally in the main application file, set them up in your mount command so your component will render them properly:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.js"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "cypress/support/component.ts"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport Button from '../../src/components/Button.vue'\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.components = options.global.components || {}\n\n  // Register global components\n  options.global.components['Button'] = Button\n\n  return mount(component, options)\n})"
      },
      {
        "type": "code",
        "lang": null,
        "meta": null,
        "value": "import { mount } from 'cypress/vue'\nimport Button from '../../src/components/Button.vue'\n\ntype MountParams = Parameters<typeof mount>\ntype OptionsParam = MountParams[1]\n\ndeclare global {\n  namespace Cypress {\n    interface Chainable {\n      /**\n       * Helper mount function for Vue Components\n       * @param component Vue Component or JSX Element to mount\n       * @param options Options passed to Vue Test Utils\n       */\n      mount(component: any, options?: OptionsParam): Chainable<any>\n    }\n  }\n}\n\nCypress.Commands.add('mount', (component, options = {}) => {\n  // Setup options object\n  options.global = options.global || {}\n  options.global.components = options.global.components || {}\n\n  // Register global components\n  options.global.components['Button'] = Button\n\n  return mount(component, options)\n})"
      }
    ]
  },
  "token_estimate": 4072
}