In this article, we are going to explain how to setup a continue integration job on GitHub Actions to automatically trigger end-to-end testing by using Cypress e2e framework.
TLDR, the working code can be found here.
Not Familiar with Cypress?
Cypress is an open sourced modern e2e testing framework. It provides out of box solution to test website functionalities with cross browsers support. Cypress leverages the most popular testing libraries, like Sinon, Chai, etc, if you are familiar with these libraries, you can start to write Cypress e2e tests with zero learning curve. Cypress also super easy to setup, you can literally setup an Cypress testing server in 5 minutes by following its official documents. In this article, we are going to use Cypress to test our production website.
Why GitHub Actions?
GitHub Actions is the latest CI/DI platform introduced by GitHub. It deeply integrated with GitHub source control, you can easily build, test, deploy your code right from the your GitHub code repository. It supports most major platforms, like popular Linux distros, Windows and MacOS. GitHub also offers 2,000 Actions minutes per month for free account, which it is enough for most side/small projects. You can simply define GitHub Action jobs by using a yaml files and commit it into source control.
Build Website
First we are going to build the testing website using nodejs and express, our website has one page and one API handler. When users visit the website, they will see a login form, and login request will be handled by /submit
api.
import express from "express";
import * as http from "http";
import * as path from "path";
const app = express();
app.get("/", (req, res) => {
res.sendFile(path.resolve("public/index.html"));
});
app.post("/submit", (req, res) => {
res.end("submit success!");
});
const server = http.createServer(app);
server.listen("4040");
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cypress E2E Test Demo Page</title>
</head>
<body>
<!-- simple login form -->
<form>
<label>
username
<input type="text" id="username" />
</label>
<label>
password
<input type="password" id="password" />
</label>
<button type="submit">Submit</button>
</form>
<!-- we use axios as restful request client -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>
const form = document.querySelector('form');
form.onsubmit = (e) => {
e.preventDefault();
const username = form.querySelector('#username').value;
const password = form.querySelector('#password').value;
axios.post("/submit", { username, password });
}
</script>
</body>
</html>
Setup E2E Test
First install Cypress through npm or yarn
npm install cypress -D
Then create cypress.json
to config cypress server
{
"baseUrl": "http://localhost:4040",
"fixturesFolder": "e2e/fixtures",
"integrationFolder": "e2e/tests",
"screenshotsFolder": "e2e/screenshots",
"videosFolder": "e2e/videos",
"supportFile": false,
"pluginsFile": false
}
We config the baseUrl
to localhost
because we are going to start the website in local CI environment. It also support remote url like using a deployed website url, in that case, e2e tests will be run against the production website. integrationFolder
defined the path to test files, and fixturesFolder
and screenshotsFolder
will be used to locate fixture data and screenshot images.
The last thing is to write the test, for demonstrate purpose, we only have one test file which contains two tests to cover the login flow. You can check more usage details on cypress website.
// e2e/tests/index.test.js
describe("Index Page", () => {
before(() => {
cy.visit("localhost:4040");
});
it("should contain a form element", () => {
const form = cy.get("form");
form.should("exist");
form.get('input#username').should('exist');
form.get('input#password').should('exist');
form.get('button[type="submit"]').should('exist');
});
it("should send http request with login payload when submit button clicked", () => {
cy.server();
cy.route("POST", "/submit").as("submit");
cy.get('input#username').type("test");
cy.get('input#password').type("123abc");
cy.get('button[type="submit"]').click();
cy.wait("@submit").should("have.property", "status", 200);
cy.get("@submit").should((xhr) => {
expect(xhr.request.body).to.deep.equal({ username: "test", password: "123abc" });
expect(xhr.response.body).to.equal("submit success!");
});
});
});
Test Run
That's it, our Cypress server is ready to run, to start the server locally, execute npx cypress open
, and the Cypress dashboard will show up
In dashboard, it lists the test we just wrote, we can select the testing browser and check previous running results. Now click 'Run all specs', Cypress will start to execute our tests (make sure our website is started before running tests!). We will see the target browser pop up and all tests executed successfully!
Define CI Pipeline
The last step is to setup GitHub Action workflow, workflow is a custom automated processes that we can set up in repository to build, test, package, release, or deploy any project on GitHub. We want the workflow can run e2e test automatically when there are new commits pushed to master branch, the workflow has several jobs, first it will checkout source code to local, then install nodejs and dependencies, finally start e2e testing.
To define workflow, we should create a yaml file and put it in .github/workflows
folder, and commit it into source control. More workflow sytax detail can be found on here.
name: E2E Test
on:
push:
branches:
- master
jobs:
e2e:
runs-on: ubuntu-16.04
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: "14"
- name: Install Dependencies
run: npm i
- name: Run Cypress
uses: cypress-io/github-action@v2
with:
start: npm run start
Here we use the official cypress-io/github-action
plugin to help us start cypress testing, but it's same as easy as setting up by ourself. Once this file committed and pushed, we will see workflow can be triggered by following commits.
Top comments (0)