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

July 8, 2020

•

By The Cypress Team

What is Ionic?

Ionic Framework is a free, open-source framework for developing native mobile applications using web development frameworks like Angular, React, and (coming soon!) Vue. Mobile applications developed in Ionic Framework run in a browser during development. Because of this, we can use Cypress to perform tests on the functionality of mobile applications before they are built for a native device.

Pros & Cons

There are some nice benefits when pairing Ionic and Cypress for testing mobile devices. Because Ionic apps run like any modern web app, no device-specific configuration is required to get a running version of your application for testing. No need to install Android Studio or set up an emulator. Also, because Cypress runs right alongside your application code, you can also directly access and manipulate the state of your application for more efficient testing.

There are some limitations, as well. Because we are mocking a mobile environment within a browser, you can't directly test native device functionality like camera or fingerprint authentication. If there are specific limitations of your application related to running a mobile device (performance, security, etc.) you would need to use workarounds to replicate these in your testing environment as well.

Demo: Ionic Conference App

For this blog, I am using a fork of the Ionic Conference App. Built with Angular and Ionic Framework, it is a demo app for the framework and showcases a wide variety of functionality provided by Ionic. The app is like one you would use when attending a large conference, with speaker information and a session schedule.

You can add Cypress to your Ionic app the same way you install in any other project. Once you have installed Cypress, you'll want to configure the baseUrl in your cypress.json file and create a spec file for your tests in your integration folder.

Setting the viewport

In order to make these tests mobile, you need to configure the viewport setting in Cypress. If you plan to use the same viewport for all your tests, you can set this in your cypress.json file with the viewportHeight and viewportWidth options.

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

For more granular control of the viewport, you can use the cy.viewport() command and pass through height and width values or provide a device from the pre-set options. In our example, we are using cy.viewport() in a beforeEach() hook so all the tests in our spec file are run in the viewport size for an iPhone 5 device.

// tests.spec.js
describe('mobile-tests', () => {
  beforeEach(() => {
    cy.viewport('iphone-5')
  })
  // Tests here
})

In this video, you can see the difference between a normal test run of our application and a second run with the viewport set to a mobile device.

Desktop vs Mobile Test Run

Accessing App Storage

The app has functionality for swiping through an onboarding tutorial, adding sessions to a favorites list, searching for a session, and enabling dark mode. This is the functionality we will test in this example.

Like many mobile apps, the Ionic Conference App starts with a tutorial the first time you install the app. It is helpful for us to control when we see this tutorial in our tests. To do this, we can access and set the state of our application with a Cypress custom command.

The Ionic Conference App uses Ionic Storage. It is a free, open-source storage option and is built in to Ionic Framework. Within the app, the display of the tutorial is controlled by the ion_did_tutorial field in storage. We can import Ionic Storage into our Cypress commands.js file and use it to set this field.

// 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)
    }
  })
})

In this example, we are writing a command that visits the baseUrl, but before the page loads, it sets the ion_did_tutorial field to false. This will ensure the tutorial is always displayed when we use this command.

We can then use this custom command within our test to control when we display the tutorial and when we programmatically bypass it to progress to the actual application. Check out the video here.

Enabling and disabling tutorial

Simulating Mobile Behavior

When the tutorial is displayed, the user needs to swipe left on their device three times before clicking "Continue" to progress to the app. How do we perform this behavior in a browser? We can leverage this drag-and-drop example and trigger the same mouse events to simulate a user swiping across a screen.

/// 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})
})

In this custom command, we are first selecting the element to be swiped, then triggering three events:

  • a mousedown event on the right-hand side of the element
  • a mousemove event to the left side of the "screen"
  • a mouseup event that releases the element

These events simulate a user swiping their finger across the screen of a mobile device. Once this command is written, we can then use it in our test to move through the tutorial.

/// cypress/integration/spec.js

it("swipes through tutorial", () => {
  cy.enableTutorial()
  cy.swipeLeft()
  cy.swipeLeft()
  cy.swipeLeft()
  cy.contains("Continue").click()  
})

In this video, you can see the user swiping through the tutorial in the test.

Swiping through tutorial

Putting it all together

Now that we understand some of the tools within Cypress for testing a mobile application, we can create a test of our user's critical path.

/// tests.spec.js

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');
})

In the first test, we are swiping through the tutorial. In the second test, we are bypassing the tutorial directly to the application. The user then selects a session from the list, adds it to their favorites, and then verifies that the session exists on the favorites list.

/// 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");
})

The first test in this section bypasses the tutorial and then clicks on the element to enable dark mode. It then asserts that dark mode has been enabled by checking the CSS color property.

Finally, the last test runs through the user searching for a term, finding the matching session, adding it to their favorites, and then verifying it is on the favorites list.

Here is the video of the full test run.

Full test run in Cypress

Next Steps

This is a starting point to show the flexibility of Cypress when testing mobile applications built with Ionic. There are many example recipes to simulate a variety of behaviors, many of which could be leveraged to test additional functionalities of mobile applications.

The repository for this blog post with all the test code and custom commands is available at https://github.com/ceceliacreates/ionic-cypress. The slides for a talk on this topic given at Ioniconf 2020 are available here. The video of this talk can be found here.