Skip to content

Commit

Permalink
test: setup unit testing with jest and e2e testing with cypress
Browse files Browse the repository at this point in the history
* refactor(formats): handled the checkbox disabling logic in the Format component

* test(e2e): adapted the tests to pass on all browsers

* test(e2e): added e2e tests for error cases when loading the download page

* test(e2e): started the e2e tests for the download page

* refactor(tests): grouped results page e2e tests to remove code duplication

* test(e2e): added a test that goes to the download page

* test(e2e): added e2e tests for the results page

* refactor(results): manually caught the API errors instead of relying on an error.tsx file

* feat: added ids to some elements to better target them with cypress

* test(unit): added tests for the SearchParams class

* test(unit): added tests for the Istex API functions

* test(unit): added tests for the format functions

* test: changed the github action to run both unit and e2e tests

* test: setup unit testing with jest

* build: called the build script through npm instead of calling it manually

* test: added a github action to run cypress

* build: made custom build and start scripts for production to start a prod server outside of docker

* test: added npm scripts for cypress

* test: disabled video generation when running cypress run

* test: added another test that tries to search with an empty input and removed unused files

* test: added a basic test that does a regular search

* style: added the cypress eslint plugin

* test: setup cypress
  • Loading branch information
ClementDreptin committed Aug 10, 2023
1 parent 959268f commit bce7b8a
Show file tree
Hide file tree
Showing 28 changed files with 8,199 additions and 2,760 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ const path = require("path");

/** @type {import('eslint').Linter.Config} */
module.exports = {
plugins: ["cypress"],
extends: [
"standard-with-typescript",
"standard-jsx",
"standard-react",
"next/core-web-vitals",
"prettier",
"plugin:cypress/recommended",
],
parserOptions: {
project: path.join(__dirname, "tsconfig.json"),
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Tests

on:
push:
branches: [nextjs]

jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- name: Check out files
uses: actions/checkout@v3

- name: Install Node
uses: actions/setup-node@v3
with:
node-version: latest

- name: Install dependencies and run tests
run: npm ci && npm run test:unit

e2e-tests:
runs-on: ubuntu-latest
container: cypress/browsers:latest
steps:
- name: Check out files
uses: actions/checkout@v3

- name: Cypress run
uses: cypress-io/github-action@v5
with:
build: npm run build
start: npm start
8 changes: 8 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
baseUrl: "http://localhost:3000",
video: false,
},
});
153 changes: 153 additions & 0 deletions cypress/e2e/download-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* eslint-disable cypress/no-unnecessary-waiting */

describe("The Download Page", () => {
describe("Valid", () => {
const queryString = "hello";
const size = 2;

it("Default state of the page", () => {
cy.visit("/download", { qs: { q: queryString, size } });

cy.get("button[data-testid=custom")
.invoke("attr", "aria-selected")
.should("equal", "true");

cy.get("input[type=checkbox]").should("not.be.checked");
cy.get("button#download-button").should("be.disabled");

cy.get("p[data-testid=custom-usage-description]").should("exist");
cy.get("p[data-testid=query-string]").should("have.text", queryString);
cy.get("a[data-testid=raw-request]").should(
"include.text",
`q=${queryString}`
);
});

it("Selecting formats", () => {
// Adding arbitrary wait times to wait until the page is fully loaded is the only
// way I found to make this test pass on all browsers. The initial page load is a
// little bit long and each click on a checkbox re-renders the whole view and it
// takes some time.

cy.visit("/download", { qs: { q: queryString, size } });
cy.wait(2000);

cy.get('input[name="fulltext.pdf"]').check();
cy.wait(1000);
cy.get('input[name="metadata.json"]').check();
cy.wait(1000);
cy.get('input[name="metadata.xml"]').check();
cy.wait(1000);
cy.get('input[name="metadata.mods"]').check();
cy.wait(1000);

cy.get('input[name="fulltext.category"]')
.invoke("attr", "data-indeterminate")
.should("equal", "true");
cy.get('input[name="metadata.category"]').should("be.checked");

cy.get("button#download-button").should("be.enabled");
cy.url().should("include", "extract=fulltext%5Bpdf%5D");
});

it("Custom usage", () => {
const usage = "custom";
const extract = "fulltext[pdf]";
cy.visit("/download", { qs: { q: queryString, size, usage, extract } });

cy.get(`button[data-testid=${usage}`)
.invoke("attr", "aria-selected")
.should("equal", "true");

cy.get("input[type=checkbox]:disabled").should("not.exist");
cy.get('input[name="fulltext.pdf"]').should("be.checked");
cy.get(`p[data-testid=${usage}-usage-description]`).should("exist");
cy.url().should("include", `extract=${encodeURIComponent(extract)}`);
});

it("Lodex usage", () => {
const usage = "lodex";
const extract = "metadata[json]";
cy.visit("/download", { qs: { q: queryString, size, usage, extract } });

cy.get(`button[data-testid=${usage}`)
.invoke("attr", "aria-selected")
.should("equal", "true");

cy.get("input[type=checkbox]").should("be.disabled");
cy.get('input[name="metadata.json"]').should("be.checked");
cy.get(`p[data-testid=${usage}-usage-description]`).should("exist");
cy.url().should("include", `extract=${encodeURIComponent(extract)}`);
});

it("CorTexT usage", () => {
const usage = "cortext";
const extract = "fulltext[tei,cleaned];enrichments[teeft]";
cy.visit("/download", { qs: { q: queryString, size, usage, extract } });

cy.get(`button[data-testid=${usage}`)
.invoke("attr", "aria-selected")
.should("equal", "true");

cy.get("input[type=checkbox]").should("be.disabled");
cy.get('input[name="fulltext.tei"]').should("be.checked");
cy.get('input[name="fulltext.cleaned"]').should("be.checked");
cy.get('input[name="enrichments.teeft"]').should("be.checked");
cy.get(`p[data-testid=${usage}-usage-description]`).should("exist");
cy.url().should("include", `extract=${encodeURIComponent(extract)}`);
});

it("GarganText usage", () => {
const usage = "gargantext";
const extract = "metadata[json]";
cy.visit("/download", { qs: { q: queryString, size, usage, extract } });

cy.get(`button[data-testid=${usage}`)
.invoke("attr", "aria-selected")
.should("equal", "true");

cy.get("input[type=checkbox]").should("be.disabled");
cy.get('input[name="metadata.json"]').should("be.checked");
cy.get(`p[data-testid=${usage}-usage-description]`).should("exist");
cy.url().should("include", `extract=${encodeURIComponent(extract)}`);
});
});

describe("Invalid", () => {
it("Empty query string", () => {
cy.visit("/download", { qs: { q: "", size: 2 } });

// A NEXT_REDIRECT error is expected when the query string is empty
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/download");
});

it("No query string", () => {
cy.visit("/download", { qs: { size: 2 } });

// A NEXT_REDIRECT error is expected when there is no query string
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/download");
});

it("Size set to 0", () => {
cy.visit("/download", { qs: { q: "hello", size: 0 } });

// A NEXT_REDIRECT error is expected when the size is 0
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/download");
});

it("No size", () => {
cy.visit("/download", { qs: { q: "hello" } });

// A NEXT_REDIRECT error is expected when there is no size
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/download");
});
});
});
18 changes: 18 additions & 0 deletions cypress/e2e/home-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
describe("The Home Page", () => {
beforeEach(() => {
cy.visit("/");
});

it("Valid search", () => {
const queryString = "hello";

cy.get("input#regular-search-input").type(`${queryString}{enter}`);
cy.url().should("include", `/results?q=${queryString}`);
});

it("Search with empty input", () => {
cy.get("button[type=submit]").click();
cy.url().should("not.include", "/results");
cy.get("p#regular-search-input-helper-text").should("exist");
});
});
51 changes: 51 additions & 0 deletions cypress/e2e/results-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe("The Results Page", () => {
describe("Valid", () => {
const queryString = "hello";

beforeEach(() => {
cy.visit("/results", { qs: { q: queryString } });
});

it("Search", () => {
cy.get("a#download-button").should("exist");
cy.get("input#regular-search-input").should("have.value", queryString);
cy.get("#results-grid").children().should("have.length.gt", 0);
});

it("Going to download page", () => {
cy.get("a#download-button").click();
cy.url().should("include", "/download");
});

it("Going back to home page", () => {
cy.get("a#home-link").click();
cy.url().should("not.include", "/results");
});
});

describe("Invalid", () => {
it("Syntax error", () => {
cy.visit("/results", { qs: { q: "hello:" } });

cy.get("[role=alert]").should("exist");
});

it("Empty query string", () => {
cy.visit("/results", { qs: { q: "" } });

// A NEXT_REDIRECT error is expected when the query string is empty
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/results");
});

it("No query string", () => {
cy.visit("/results");

// A NEXT_REDIRECT error is expected when there is no query string
cy.on("uncaught:exception", () => false);

cy.url().should("not.include", "/results");
});
});
});
Empty file added cypress/support/e2e.ts
Empty file.
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const nextJest = require("next/jest");

const createJestConfig = nextJest({ dir: "./" });

/** @type {import('jest').Config} */
module.exports = createJestConfig({
testEnvironment: "jest-environment-jsdom",
});
Loading

0 comments on commit bce7b8a

Please sign in to comment.