Cecelia Martinez
Technical Account Manager @ Cypress
Install
ng add @cypress/schematicRun in open mode
ng run {project-name}:cypress-openRun headlessly
ng run {project-name}:cypress-runGenerate new e2e spec files
ng generate @cypress/schematic:e2eAdd or update to use to run Cypress in open mode
ng e2eConfiguration Options:
"cypress-open": {
  "builder": "@cypress/schematic:cypress",
  "options": {
    "watch": true,
    "headless": false,
    "browser": "chrome"
  },
},
"cypress-run": {
  "builder": "@cypress/schematic:cypress",
  "options": {
    "devServerTarget": "{project-name}:serve",
    "configFile": "cypress.production.json",
    "parallel": true,
    "record": true,
    "key": "your-cypress-dashboard-recording-key"
  },
}Tip: No need to disable waiting for Angular to be enabled when visiting non-Angular pages
it('visits a page', () => {
  browser.get('/about')
  browser.navigate().forward()
  browser.navigate().back()
})Before: Protractor
it('visits a page', () => {
  cy.visit('/about')
  cy.go('forward')
  cy.go('back')
})After: Cypress
Tip: Cypress enables you to write tests without the need for waiting, so tests are more predictable
element(by.css('button')).click()
browser.waitForAngular()
expect(by.css('.list-item').getText()).toEqual('my text')Before: Protractor
cy.get('button').click()
cy.get('.list-item').contains('my text')After: Cypress
Yes, you CAN use Page Objects with Cypress. You just may not need them!
// Protractor Page Object
const page = {
  login: () => {
    element(by.css('.username')).sendKeys('my username')
    element(by.css('.password')).sendKeys('my password')
    element(by.css('button')).click()
  },
}
it('should display the username of a logged in user', () => {
  page.login()
  expect(by.css('.username').getText()).toEqual('my username')
})Yes, you CAN use Page Objects with Cypress. You just may not need them!
// Cypress Page Object
const page = {
  login: () => {
    cy.get('.username').type('my username')
    cy.get('.password').type('my password')
    cy.get('button').click()
  },
}
it('should display the username of a logged in user', () => {
  page.login()
  cy.get('.username').contains('my username')
})Cypress.Commands.add('login', (username, password) => {
  cy.get('.username').type(username)
  cy.get('.password').type(password)
})Cypress also provides a Custom Command API to enable you to add methods to use globally.
it('should display the username of a logged in user', () => {
  cy.login('Matt', Cypress.env('password'))
  cy.get('.username').contains('Matt')
})Cypress also provides a Custom Command API to enable you to add methods to use globally.
You can also just use regular JavaScript functions!
element(by.tagName('h1'))
element(by.css('.my-class'))
element(by.id('my-id'))
element(by.cssContainingText('.my-class', 'text'))
element.all(by.tagName('li'))Before: Protractor
cy.get('h1')
cy.get('.my-class')
cy.get('#my-id')
cy.get('.my-class').contains('text')
cy.get('li')After: Cypress
element(by.css('button')).click()
element(by.css('input')).sendKeys('my text')
element.all(by.css('[type="checkbox"]')).first().click()Before: Protractor
cy.get('button').click()
cy.get('input').type('my text')
cy.get('[type="checkbox"]').first().check()After: Cypress
const list = element.all(by.css('li.selected'))
expect(list.count()).toBe(3)Before: Protractor
cy.get('li.selected').should('have.length', 3)After: Cypress
Expect vs. Should: Length
expect(element(by.id('user-name')).getText()).toBe('Joe Smith')Before: Protractor
cy.get('#user-name').should('have.text', 'Joe Smith')After: Cypress
Expect vs. Should: Text Content
expect(element(by.tagName('button')).isDisplayed()).toBe(true)Before: Protractor
cy.get('button').should('be.visible')After: Cypress
Expect vs. Should: Visibility
it("gets a list of bank accounts for user", function () {
  const { id: userId } = ctx.authenticatedUser!;
  cy.request("GET", `${apiBankAccounts}`).then((response) => {
    expect(response.status).to.eq(200);
    expect(response.body.results[0].userId).to.eq(userId);
    });
  });
});// Application source code app.component.ts
export class AppComponent {
  constructor(private store: Store<any>) {
    // @ts-ignore
    if(window.Cypress){
      // @ts-ignore
      window.store = this.store;
  }
}
...
  }
}// Application source code actions.ts
export const getAllSuccess = createAction(
  '[Shows API] Get all shows success',
  props<{ shows }>()
);You can assert on the type of action last dispatched in the NgRx store, as well as the value of props.
// test code
it("validates getAllSuccess action", () => {
 // code to trigger action here via UI
  cy.window().then(w => {
// tap into the store to access the last action and its value
    const store = w.store;
    const action = store.actionsObserver._value;
    const shows = store.actionsObserver._value.shows;
            
 // expect action type and length of shows array to match expected values
    expect(action.type).equal("[Shows API] Get all shows success");
    expect(shows.length).equal(4);
  });// application source code effects.ts
favoriteShow$ = createEffect(() =>
  this.actions$.pipe(
    ofType(allShowsActions.favoriteShowClicked, favoriteShowsActions.favoriteShowClicked),
    mergeMap(({ showId }) =>
      this.showsService
        .favoriteShow(showId)
        .pipe(map(() => favoriteShowSuccess({ showId })), catchError(error => of(null)))
...
  );Dispatching favoriteShowClicked causes the favoriteShowSuccess action with the correct showId
// code to trigger the favoriteShowClicked with id 2 action here
cy.window().then(w => {
  // gets most recent action from store
  const store = w.store;
  const action = store.actionsObserver._value
            
  // confirms it is the effect expected after favoriteShowClicked
  expect(action.type).equal("[Shows API] favorite show success");
  expect(action.showId).equal(2)
});Dispatching favoriteShowClicked causes the favoriteShowSuccess action with the correct showId
cy.window()
.its('store')
.invoke('dispatch', { showId: 1, type: '[All Shows] favorite show'});