Hello, I hope you are doing well. Today I would like to tell you about how to set up an end-to-end test for you front-end application. I used Cypress because it is currently the best tool to run an end-to-end test. The question is, why not unit tests? In my opinion, for a small part of the code, you could run unit tests. But if you want to ensure a happy path always succeeds, end-to-end tests are better to simulate the user’s journey. You can use both, and I encourage it. So, Without further ado, let’s get started.
Install Cypress
First, you need to install Cypress in your project. Run npm i -D cypress
or yarn add -D cypress
. After that, add the following scripts to your package.json
file.
{
// ...the rest of the package.json file
"scripts": {
"e2e": "cypress open",
"e2e:headless": "cypress run"
}
// ...the rest of the package.json file
}
The e2e
script is for running with the browser, and e2e:headless
is for running in headless mode.
Write Your Test
Open Cypress by running npm run e2e
. Cypress UI will pop up, known as Launchpad. Click on E2E Testing
, and it will generate the files you need to run the test.
Once everything is set, add a sample test for you project. Create a file called cypress/e2e/login.cy.ts
.
// cypress/e2e/login.cy.ts
describe("Login page", () => {
it("should open the login page", () => {
// arrange
cy.visit("http://localhost:3000/auth/login");
// act
cy.contains("Username").click().type("Hari");
cy.contains("Password").click().type("random-string");
cy.contains("Log in").click();
// assert
cy.contains("Hi, Hari");
});
});
Here is the sample of my application code:
<!-- ... the rest of the pages/login.tsx -->
<div class="name">
<label for="name">Username</label>
<input name="name" id="name" />
</div>
<div class="password">
<label for="password">Password</label>
<input name="password" id="password" />
</div>
<button>Log in</button>
<!-- ... the rest of the pages/login.tsx -->
You need too learn the Cypress methods from its documentation. The documentation is great, you are not supposeed to have difficulty exploring it. In my opinion, the learning curve is shallow, as you can see in my example. You could read it easily. It basically do the follows these steps:
- Visit
http://localhost:3000/auth/login"
from browser - Find something that contains
Username
(this is a label). Click it. Then, typeHari
- Find something that contains
Password
(this is a label). Click it. Then, typerandom-string
- Find something that contains
Log in
(this is a button). Then click it - Find something that contains
Hi, Hari
A little bit advice: I’ve put three comments - arrange
, act
, assert
. That is a testing pattern where you prepare the test (arrange), perform the test (act), evaluate the test result (assert). I’ve added the comments to make it more readable.
Another piece of advice is that when you write and end-to-end test, make sure you put yourself in the user’s perspective. For instance, you can replace cy.contains("Username").click().type("Hari")
with cy.get("#name").type("Hari")
. However, users won’t find elements by their ID, they will find elements by readable things like Username
then perform the action on that element.
After you’ve added your test, run it. Run npm run e2e
or npm run e2e:headless
.
Run it in GitHub Actions
After everything is okay, you just need to run you test in your CI/CD. Here is what the ./.github/workflows/code-quality.yaml
looks like:
name: 🌟 Code Quality
on: push
jobs:
code-quality:
name: 🌟 Code quality
runs-on: ubuntu-latest
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4
- name: ⎔ Setup node
uses: actions/setup-node@v3
with:
node-version: "18"
cache: "yarn"
- name: 📥 Install deps
run: yarn --immutable
- name: 🛠️ Build the application
run: yarn build
- name: 💻 Run the application
run: yarn start &
- name: ⏳ Wait the application to start
run: npx wait-on http://localhost:3000/auth/login
- name: 🤖 End-to-end test
run: yarn e2e:headless
The pipeline will run the following steps:
- Check out the repository to the CI/CD server.
- Set up Node.js.
- Install the application dependencies.
- Build the application.
- Run the application.
- Wait the application to run.
- Run the end-to-end test.
One more piece of advice here is that you need to build the application in production mode if you can. This ensures that you test the same build as your production.
In the sixth step, I wait for the application to run because the test will fail if the application is not running.
That’s all from me. I hope you can get some ideas on how to set up end-to-end tests and run them in GitHub Actions.
Thank you!