Angular Examples

Mounting Components

Using cy.mount()

To mount a component with cy.mount(), import the component and pass it to the method:

import { StepperComponent } from './stepper.component'

it('mounts', () => {
  cy.mount(StepperComponent)
})

Passing Data to a Component

You can pass inputs and outputs to a component by setting componentProperties in the options:

cy.mount(StepperComponent, {
  componentProperties: {
    count: 100,
    change: new EventEmitter(),
  },
})

Testing Event Handlers

Pass a Cypress spy to an event prop and validate it was called:

it('clicking + fires a change event with the incremented value', () => {
  cy.mount(StepperComponent, {
    componentProperties: {
      change: createOutputSpy('changeSpy'),
    },
  })
  cy.get('[data-cy=increment]').click()
  cy.get('@changeSpy').should('have.been.calledWith', 1)
})

Imports/Declarations/Providers

If you need to set up any additional imports, declarations, or providers for your component to mount successfully, you can set them in the options (similar to setting them up in ngModule in a app):

cy.mount(ComponentThatFetchesData, {
  imports: [HttpClientModule],
  declarations: [ButtonComponent],
  providers: [DataService],
})

See Default Declarations, Providers, or Imports to set up common options in a custom cy.mount() command to avoid having to repeat this boilerplate for each test.

Using Angular Template Syntax

The cy.mount() method also supports the Angular template syntax when mounting a component. Some developers might prefer this approach to the object based mount style:

cy.mount(`<app-stepper [count]="100"></app-stepper>`, {
  declarations: [StepperComponent],
})

When using template syntax, the component needs to added to the declarations in the options parameter.

Using with event emitter spy:

cy.mount('<app-button (click)="onClick.emit($event)">Click me</app-button>', {
  declarations: [ButtonComponent]
  componentProperties: {
    onClick: createOutputSpy('onClickSpy'),
  },
})
cy.get('button').click();
cy.get('@onClickSpy').should('have.been.called');

Accessing the Component Instance

There might be times when you might want to access the component instance directly in your tests. To do so, use .then(), which enables us to work with the subject that was yielded from the cy.mount() command. In this case, mount yields an object that contains the rendered component and the fixture.

In the below example, we use the component to spy directly on the change event emitter.

it('clicking + fires a change event with the incremented value', () => {
  cy.mount(
    '<app-stepper count="100" (change)="change.emit($event)"></app-stepper>',
    {
      componentProperties: { change: new EventEmitter() },
      declarations: [StepperComponent],
    }
  ).then((wrapper) => {
    console.log({ wrapper })
    cy.spy(wrapper.component.change, 'emit').as('changeSpy')
    return cy.wrap(wrapper).as('angular')
  })
  cy.get(incrementSelector).click()
  cy.get('@changeSpy').should('have.been.calledWith', 101)
})

Using createOutputSpy()

To make spying on event emitters easier, there is a utility function called createOutputSpy() which can be used to automatically create an EventEmitter and setup the spy on it's .emit() method. It can be used like the following:

import { createOutputSpy } from 'cypress/angular'

it('clicking + fires a change event with the incremented value', () => {
  // Arrange
  cy.mount('<app-stepper (change)="change.emit($event)"></app-stepper>', {
    declarations: [StepperComponent],
    componentProperties: {
      change: createOutputSpy<boolean>('changeSpy'),
    },
  })
  cy.get(incrementSelector).click()
  cy.get('@changeSpy').should('have.been.called')
})

Using autoSpyOutputs

You might find yourself repeatedly creating a cy.spy() for each of your component outputs. Because of this, we created an easy mechanism to handle this for you. This feature can be turned on by passing the autoSpyOutputs flag into MountConfig. After the component has been mounted you can then access each of the generated spies using the @Output() property name + Spy. So our change property can be accessed via its alias of cy.get('@changeSpy')

it('clicking + fires a change event with the incremented value', () => {
  cy.mount(StepperComponent, {
    autoSpyOutputs: true,
    componentProperties: {
      count: 100,
    },
  })
  cy.get(incrementSelector).click()
  cy.get('@changeSpy').should('have.been.calledWith', 101)
})

Custom Mount Commands

Customizing cy.mount()

By default, cy.mount() is a simple passthrough to mount(), however, you can customize cy.mount() to fit your needs. For instance, you may find yourself doing repetitive work during mounting. In order to reduce boilerplate you may find it useful to create a custom mount command.

Default Declarations, Providers, or Imports

If you find yourself registering a bunch of declarations, providers, or imports in your individual tests, we recommend doing them all within a custom cy.mount() command. The overhead is usually minimal for all your tests and it helps keep your spec code clean.

Below is a sample that registers several default component declarations while still allowing additional ones to be passed in via the config param. The same pattern can also be applied to providers and module imports.

import { Type } from '@angular/core'
import { mount, MountConfig } from 'cypress/angular'
import { ButtonComponent } from 'src/app/button/button.component'
import { CardComponent } from 'src/app/card/card.component'

declare global {
  namespace Cypress {
    interface Chainable {
      mount: typeof customMount
    }
  }
}

const declarations = [ButtonComponent, CardComponent]

function customMount<T>(component: string | Type<T>, config?: MountConfig<T>) {
  if (!config) {
    config = { declarations }
  } else {
    config.declarations = [...(config?.declarations || []), ...declarations]
  }
  return mount<T>(component, config)
}

Cypress.Commands.add('mount', customMount)

This custom mount command will allow you to skip manually passing in the ButtonComponent and CardComponent as declarations into each cy.mount() call.

autoSpyOutputs

Here is an example of defaulting autoSpyOutputs for every mounted component:

declare global {
  namespace Cypress {
    interface Chainable {
      mount: typeof mount
    }
  }
}

Cypress.Commands.add(
  'mount',
  (component: Type<unknown> | string, config: MountConfig<T>) => {
    return mount(component, {
      ...config,
      autoSpyOutputs: true,
    })
  }
)