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...

deck

By Cecelia Martinez

deck

  • 1,848