Cypress is a browser-automation testing suite that we use for end-to-end tests. See ADR019 - Integration Tests for some additional background on testing goals and decisions.
All tests added into the tdrs-frontend/cypress/e2e/ folder will be run against the newly deployed develop environment as part of our pipeline to help ensure site reliability and that no bugs have been introduced. These are run against the actual deployed environment and so might catch issues connecting with real backend services instead of having everything local.
- Have both the backend and frontend running in separate terminal processes, the app needs to be reachable and usable at
localhost:3000when testing locally - In a new terminal, set up test users by running
cd tdrs-backend docker-compose exec web python manage.py loaddata cypress/users cypress/data_files
- Be sure your
tdrs-backend/.envfile contains the following# testing CYPRESS_TOKEN=local-cypress-token DJANGO_DEBUG=yes - In a new terminal, run the following commands to launch the Cypress runner
cd tdrs-frontend yarn test:e2e - Select "E2E Testing" from the testing type menu

- Select a browser and click "Start E2E Testing"

- Now you can select a spec file from the menu and watch the tests run

It is highly recommended that you check out the Cypress Test Runner feature overview. Here are the main highlights
- Command log - Cypress will show you each
cycommand that executes in the command log, alongside the rendered page.- You can hover over past commands to "time travel" and replay individual steps, and clicking on a command in the log will "pin" the point in time in the runner, and log output information about that command in the console.
- The command log also shows network requests and other dispatched events.
- Dev tools - you can open up chrome's devtools (right click and click "Inspect") and see all the normal DOM information about the page, including for pinned commands which is useful when debugging. The console will also show output from the page as well as the tests.
- Automatic rerun - whenever you save your test code or frontend app code, Cypress automatically reruns the open test.
On top of Cypress, we've layered cypress-cucumber-preprocessor to provide Gherkin syntax for tests. This gives the tests more structure and allows us to easily separate and organize step implementations.
Test files are defined as .feature files within the tdrs-frontend/cypress/e2e directory. Feature "areas" can be grouped into a folder.
Step implementations are defined as .js. The cucumber preprocessor uses the glob pattern cypress/e2e/*/[filepath].js to discover the step files at the same level as and within the feature directory so long as the step file has the same name as the .feature file. The cucumber preprocessor uses the glob pattern cypress/e2e/common-steps/*.js to identify it's common step file(s) which are available to all .feature files.
Here's an example feature file
tdrs-frontend/cypress/e2e/accounts/accounts.feature
Feature: Users can create and manage their accounts
Scenario: A user can log in and request access
When 'new-cypress@teamraft.com' visits the home page
And 'new-cypress@teamraft.com' logs in
Then {string} sees a Request Access formAt its top level, it defines a Feature with multiple Scenarios (this can be likened to a describe and it in jest, respectively). Scenarios belonging to a feature area should be grouped within a Feature. Scenarios should describe a specific task/goal the user is required to perform in the system.
Each line within a Scenario is a "step", which should describe the user interaction with the system in the business context. There are three types of steps, Given, When, and Then
Givensteps define prerequisite state of the system in order for the tests to run. Mostly commonly, we'll use aGiven I am logged in as xyzstep to perform user login and setup prior to the test steps actually executing.Whensteps define user actions needed to perform the task.Thensteps describe validation or confirmationt that the user receives, indicating success of the task.
In general, all Given, When, and Then steps should reflect things the user of the system knows about the system. This is intended to hide both technical and administrator implemenation details and focus on the end-user experience.
Each step defined in a Scenario must have a corresponding "step implementation" (loaded from a .js file). Here is an example step implementation file
tdrs-frontend/cypress/e2e/accounts/accounts.js
/* eslint-disable no-undef */
import { When, Then } from '@badeball/cypress-cucumber-preprocessor'
When('{string} visits the home page', (username) => {
cy.visit('/')
cy.contains('Sign into TANF Data Portal', { timeout: 30000 })
})
When('{string} logs in', (username) => {
cy.login(username)
})
Then('{string} sees a Request Access form', (username) => {
cy.contains('Welcome').should('exist')
cy.get('button').contains('Request Access').should('exist')
})For each step implementation, a good rule of thumb is to perform both an action and an assertion. An action should be something the user can do in the system (click, type, etc.). Assertions help "slow down" the test and limit unexpected behavior when applications run a lot of asynchronous processes. By asserting on something verifiable in each step, we can ensure the test is in a proper state to move forward. This applies to Given, When, and Then steps (though Then steps can often omit an action).
Shared step implementations, which apply to all feature files, can be added as common step definitions in tdrs-frontend/cypress/e2e/common-steps/common-steps.js