Skip to content

Latest commit

 

History

History
264 lines (177 loc) · 11.1 KB

cypress-migration-guide.md

File metadata and controls

264 lines (177 loc) · 11.1 KB

We're moving our docs!

Find the latest version of this page on the Platform website.

Still can't find what you're looking for? Reach out to #vfs-platform-support on Slack.


Cypress Migration Guide

The purpose of this guide is to help with converting Nightwatch tests to Cypress.

Table of Contents

  1. Initial setup
  2. Writing tests
    1. Test structure
    2. Visiting a page
    3. Interacting with page elements
    4. Mock API responses
    5. Custom commands
      1. Mock users
      2. Test data (fixtures)
      3. File uploads
      4. Accessibility
    6. Assertions
  3. Running tests
    1. Headless mode
    2. Test runner
  4. Things to note

Initial setup

Cypress tests, like Nightwatch tests, should be written in the tests directory of the application. Cypress tests should also have the file extension .cypress.spec.js (e.g. my-test-name.cypress.spec.js).

The overall folder structure for Cypress in the vets-website repo is as follows:

vets-website
|
└───src/platform/testing/e2e/cypress
│   |
│   └───plugins
│   |   │   index.js
|   |
│   └───support
│       │   commands
│       │   index.js
  • plugins contains custom tasks, which allow you to tap into the Node environment. This is valuable because all Cypress test code is executed in the browser, so plugins allow us to execute code in the Node process running outside of the browser.
  • support contains custom Cypress commands. By default, the custom commands imported in commands/index.js are available to all of our Cypress tests. These commands can be invoked similarly to the built in commands. This feature is similar to Nightwatch's custom commands. This is also where custom commands from Cypress plugins are imported, as can be seen in index.js.

For a list of currently used custom written Cypress helpers and mocks for vets-frontend browse the Cypress Resources Overview & Guide.

Test dependencies

You generally do not need to import any modules for helpers, timeouts, etc. as with Nightwatch.

Writing tests

Test structure

The test structure of Cypress should feel familiar. Cypress uses Mocha's behvior-driven development (BDD) syntax, which is what we use for our unit tests.

Each spec file starts a new browser instance and runs the suite of tests according to the describe() and it() blocks.

  • Note that it() blocks are individual tests, so each it() block should be independent of others in the same test suite.
  • Everything executed in the browser needs to be inside of an it() block.

Visit the Cypress docs for more context.

Form tests

Applications that are built with the VA Forms Library should be tested with the Cypress form tester.

Visiting a page

When visiting a page, you don't need to specify the baseUrl. Cypress's configuration file takes care of this. So rather than grabbing the baseUrl from the helpers in Nightwatch:

client.openUrl(`${E2eHelpers.baseUrl}/health-care/apply/application`);

You can instead visit the page with a relative path:

cy.visit("health-care/apply/application");

Interacting with page elements

See Interacting with Page Elements in the Cypress Resources Overview & Guide for a detailed use case guide.

The most common interactions you will probably need to convert from Nightwatch tests are clicking, selecting elements from dropdowns, and entering text inputs. Below are examples of these interactions in Cypress.

Clicking

Nightwatch:

client.click(".form-panel .usa-button-primary");

Cypress:

cy.get(".form-panel .usa-button-primary").click();

Selecting from dropdown

Nightwatch:

client.selectDropdown(
  "root_veteranAddress_country",
  data.veteranAddress.country
);

Cypress:

cy.findByLabelText(/country/i).select(testData.veteranAddress.country);

Entering data

Nightwatch:

client.fill('input[name="root_firstName"]', data.veteranFullName.first);

Cypress:

cy.findByLabelText(/first name/i).type(testData.veteranFullName.first);

For more information about how Cypress interactions behave, visit the Cypress guide for interacting with elements.

For additional ways to interact with the DOM, we have also included the Cypress Testing Library as a dependency.

Mock API responses

Cypress allows you to stub responses, avoiding the need for mock API helpers.

// Start a server to begin routing responses
cy.server();

// Stub request to get enrollment status
cy.route({
  method: "GET",
  url: "/v0/health_care_applications/enrollment_status*",
  status: 404,
  response: {
    errors: [
      {
        title: "Record not found",
        detail: "The record identified by  could not be found",
        code: "404",
        status: "404",
      },
    ],
  },
}).as("getApplication");

// Stub request to submit application
cy.route("POST", "/v0/health_care_applications", {
  formSubmissionId: "123fake-submission-id-567",
  timestamp: "2016-05-16",
}).as("submitApplication");

Based on the above stubs, whenever the browser makes a GET /v0/health_care_applications/enrollment_status* or a POST /v0/health_care_applications request, Cypress will automatically handle the request with the stubbed response.

You can consider cy.route() to be the built-in Cypress equivalent of the Nightwatch custom command mockData().

Our Cypress setup that's shared across all tests will automatically set up some default stubbed responses.

  • This involves starting cy.server() and stubbing the /v0/maintenance_windows and /v0/feature_toggles endpoints to return empty arrays, so you don't have to explicitly do any of this in your tests.
  • Why is this necessary? Most apps make these requests to determine the availability of the app and render a loading indicator while waiting for the responses. Thus, without stubbing these endpoints, your tests will likely time out and fail before the app finishes loading due to the requests taking the same amount of time to time out. With a defined response, the requests are able to resolve immediately, and the test can proceed without delay.
  • If you want different responses from these requests, you can override the default stubs by calling cy.route() on the same endpoints.

Custom commands

Some custom Nightwatch commands located in src/platform/testing/e2e/nightwatch-commands were converted to custom Cypress commands, and can be found in src/platform/testing/e2e/cypress/support/commands.

Many of them did not need to be converted, either because Cypress supports their functionality natively or the functionality was too simple to abstract into a command.

We have custom commands to acccomplish some common tasks, which are highlighted below. Visit here to see a directory of all commands.

Mock users

For simulating a signed-in session with a mock user, use cy.login().

See Mock Users in Cypress Best Practices for detailed usage.

Test data (fixtures)

To use fixtures (which get access to data in tests) see usage in Test Data in Cypress Best Practices.

File uploads

To see custom commands for uploading files using Cypress see File Uploads in Cypress Best Practices.

Accessibility

Like most E2E tools, Cypress has its own axe-core plugin to test accessibility.

To add aXe checks to your tests use the custom cy.axeCheck() command based off the cy.checkA11y() command.

As documented by the plugin, be sure to call cy.injectAxe() after each cy.visit(), after which cy.axeCheck() can be invoked any number of times.

Nightwatch:

client.axeCheck(".main");

Cypress:

cy.visit(PAGE_URL);
cy.injectAxeThenAxeCheck();

Depending on the content of a page, it may be more thorough to test accessibility after expanding all accordions (and expandable content in general) on the page. We have a custom command for that purpose:

cy.expandAccordions();
cy.axeCheck(); // Run the aXe check after expanding everything.

Tests written with the form tester automatically check for accessibility, so this command does not need to be used explicitly in such tests.

Assertions

Cypress uses chai to handle assertions. Currently in the nightwatch-assertions folder, we have 4 custom assertions, which can all be replaced:

hasFocusableCount / hasTabbableCount

Checks the count of focusable and tabbable elements.

These can be converted by making custom chai assertions.

isActiveElement

Checks if the given element is focused on the page.

// BDD assertion
cy.findByLabelText("First name").should("have.focus");

isDisabledElement

Checks if the given element is disabled on the page.

// BDD assertion
cy.findByRole("button", { name: "Submit" }).should("be.disabled");