End-to-end Testing Mobile Apps with Ionic and Cypress

Hello! 👋🏽

Cecelia Martinez

  • Success Engineer @ Cypress.io 🌲
  • Atlanta, GA 🍑
  • Women Who Code Track Evangelist 👩🏽‍💻
  • Out in Tech Leadership Team 🌈
  • @ceceliacreates 🐦
  • github.com/ceceliacreates
  • github.com/CypressCecelia

 

About Cypress

  • Free and open-source Test Runner 🌲
  • Runs in a browser alongside your application code 💻
  • Mocha, Chai, and jQuery selectors built in ☕️
  • Built-in waits and retries
  • Time Travel, Error Reporting, Screenshots,  and Videos for improved debugging 🐞
  • Set viewport, access application state, simulate swipe motions 📱

Mobile testing in a browser?

When mobile development happens in a browser, why not?

Benefits

  • No device-specific configuration required
  • Access and manipulate app state
  • Develop and test in JavaScript

Limitations

  • Can't directly test native device functionality
  • Mimics mobile experience by setting viewport

Getting started...

Ionic Conference App

github.com/ionic-team/ionic-conference-app

Built with Ionic + Angular

Getting started...

Install Cypress in root of project directory

git clone https://github.com/ionic-team/ionic-conference-app.git
cd ionic-conference-app
npm install cypress --save-dev

Getting started...

/cypress
  /fixtures
    - example.json

  /integration
    - tests.spec.js

  /plugins
    - index.js

  /support
    - commands.js
    - index.js

Getting started

Set baseUrl in cypress.json

/// cypress.json

{
  "baseUrl": "http://localhost:8100"
}

Getting started...

// launch app

ionic serve

// THEN launch Cypress (separate terminal)

npx cypress open

Running the tests

Diving in...

/// tests.spec.js

describe("critical path", () => {
  
  beforeEach(() => {
   
      cy.viewport('iphone-5')
  })

  it('visits site', () => {
    
      cy.visit('/')
  })
})

Diving in...

/// tests.spec.js cont.

it("swipes through tutorial", () => {

        cy.enableTutorial();
        cy.swipeLeft();
        cy.swipeLeft();
        cy.swipeLeft();
        cy.contains("Continue").click();
    })

    it("adds a session from list to favorites", () => {
        cy.disableTutorial();
        cy.contains('University of Ionic').click({force: true});
        cy.get('[data-cy=favorite]').click();
        cy.get('[data-cy=back').click();
        cy.contains('Favorites').click();
        cy.contains('University of Ionic');
    })

Critical path user flow

Diving in...

/// tests.spec.js

it("enables dark mode", () => {

        cy.disableTutorial();
        cy.get('[data-cy=menu]').click();
        cy.get('[data-cy=dark-mode]').click();
        cy.contains("Dark Mode").should('have.css', 'color', 'rgb(255, 255, 255)')
    })

    it("searches for a session and adds to favorites", () => {

        cy.disableTutorial();
        cy.get('[data-cy=search]').click();
        cy.get('[data-cy=searchbar]').click().type("Angular{enter}")
        cy.contains("Angular Directives").click();
        cy.get('[data-cy=favorite]').click();
        cy.get('[data-cy=back]').click();
        cy.contains("Favorites").click();
        cy.contains("Angular Directives");
    })

Critical path user flow

Diving in...

Setting viewport

/// cypress.json

{
"viewportHeight": 320,
"viewportWidth": 568
}

/// spec.js

cy.viewport(320, 568)
cy.viewport('iphone-5')
cy.viewport('iphone-5', 'landscape')

/// mobile-ui.spec.js

describe('mobile-tests', () => {

beforeEach(() => {

cy.viewport('iphone-5')

})
/// Tests here
})

Setting viewport

App Storage

import { Storage } from '@ionic/storage';

const storage = new Storage;


storage.set('name', 'Cecelia');


storage.get('age').then((val) => {

console.log('Your age is', val);

});

Ionic Storage is a free and open-source storage option built in to Ionic

/// cypress/support/commands.js

import { Storage } from '@ionic/storage';

const storage = new Storage;

Cypress.Commands.add('enableTutorial', () => {
  cy.visit('/', {
    onBeforeLoad () {
      storage.set('ion_did_tutorial', false)
    }
  })
})

App Storage

Custom Commands in Cypress

/// tests.spec.js

it("swipes through tutorial", () => {

        cy.enableTutorial();
        cy.swipeLeft();
        cy.swipeLeft();
        cy.swipeLeft();
        cy.contains("Continue").click();
    })

App Storage

Custom Commands in Cypress

App Storage

Swipe like a user

Cypress Custom Command + drag-and-drop example

/// cypress/support/commands.js

Cypress.Commands.add('swipeLeft', () => {
    cy.get('.swiper-slide-active')
    .trigger('mousedown', {position: "right"})
    .trigger('mousemove', {clientX: 100, clientY: 275})
    .trigger('mouseup', {force: true})
})

Swipe like a user

Use Custom Command in our test

/// cypress/support/commands.js

it("swipes through tutorial", () => {

    cy.enableTutorial()
    cy.swipeLeft()
    cy.swipeLeft()
    cy.swipeLeft()
    cy.contains("Continue").click()
    
})

Swipe like a user

Putting it all together...

Installing Cypress

File structure

cypress.json baseUrl

Setting viewport

Cypress Custom Commands

Accessing app storage

simulate "swipe" functionality

The critical user path

Putting it all together...