Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cypress testing #17

Merged
merged 12 commits into from
Feb 28, 2024
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
SUPABASE_URL=
SUPABASE_ANON_KEY=
SUPABASE_ANON_KEY=
40 changes: 40 additions & 0 deletions .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Run Cypress testing suite
on:
workflow_dispatch:
push:
branches:
- development
pull_request:

jobs:
cypress-run:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 20.10.0
- name: Checkout
uses: actions/checkout@v4
- name: Cypress run
uses: cypress-io/github-action@v6
with:
build: yarn run build
start: yarn start
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Values are set because the code requires them, but they are not actually used.
SUPABASE_URL: "https://wfzpewmlyiozupulbuur.supabase.co"
SUPABASE_ANON_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6IndmenBld21seWlvenVwdWxidXVyIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTU2NzQzMzksImV4cCI6MjAxMTI1MDMzOX0.SKIL3Q0NOBaMehH0ekFspwgcu3afp3Dl9EDzPqs1nKs"
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-videos
path: cypress/videos
if-no-files-found: ignore
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ node_modules
.pnp.cjs
.pnp.loader.mjs
static/dist
.env
.env

cypress/screenshots
14 changes: 14 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
setupNodeEvents() {
// implement node event listeners here
},

baseUrl: "http://localhost:8080",
experimentalStudio: true,
},
viewportHeight: 900,
viewportWidth: 1440,
});
146 changes: 146 additions & 0 deletions cypress/e2e/devpool.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
describe("DevPool", () => {
let issue1: Record<string, unknown>;
let issue2: Record<string, unknown>;
let loginToken: Record<string, unknown>;
let githubUser: Record<string, unknown>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do. I didn't bother because they are populated from a string, so type checking is not relevant as I don't manipulate them. Will change it

before(() => {
cy.fixture("issue-1.json").then((content) => {
issue1 = content;
});
cy.fixture("issue-2.json").then((content) => {
issue2 = content;
});
cy.fixture("user-token.json").then((content) => {
loginToken = content;
});
cy.fixture("user-github.json").then((content) => {
githubUser = content;
});
cy.intercept("https://api.github.com/repos/*/*/issues/**", (req) => {
req.reply({
statusCode: 200,
body: issue1,
});
}).as("getIssueDetails");
cy.intercept("https://api.github.com/orgs/*", (req) => {
req.reply({
statusCode: 200,
body: issue1,
});
}).as("orgs");
});

beforeEach(() => {
// Very important to make sure we don't store data between tests
cy.clearLocalStorage();
});

it.only("Main page displays issues", () => {
// Should display one new task
cy.log("Should display one new task");
cy.intercept("https://api.github.com/repos/*/*/issues**", (req) => {
req.reply({
statusCode: 200,
body: [issue1],
});
}).as("getIssues");
cy.visit("/");
cy.get('div[id="issues-container"]').children().should("have.length", 1);
cy.get("#issues-container > :nth-child(1)").should("have.class", "new-task");

// needed to make sure data is written to the local storage
cy.wait(3000);

// Should display still one old task
cy.log("Should display still one old task");
cy.intercept("https://api.github.com/repos/*/*/issues**", (req) => {
req.reply({
statusCode: 200,
body: [issue1, issue2],
});
}).as("getIssues");
cy.visit("/");
cy.get('div[id="issues-container"]').children().should("have.length", 1);
cy.get("#issues-container > :nth-child(1)").should("not.have.class", "new-task");

// needed to make sure data is written to the local storage
cy.wait(3000);

cy.log("Should display two new tasks");
cy.clock(Date.now() + 95000000);
cy.visit("/");
const fakeNow = new Date("2022-04-10");
// Needed due to a bug
cy.clock(fakeNow).then((clock) => {
// @ts-expect-error https://github.com/cypress-io/cypress/issues/7577
return clock.bind(window);
});
cy.get('div[id="issues-container"]').children().should("have.length", 2);
});

it("Display a message on rate limited", () => {
cy.intercept("https://api.github.com/repos/*/*/issues**", (req) => {
req.reply({
statusCode: 403,
});
}).as("getIssues");
cy.visit("/");
cy.get(".preview-header").should("exist");
});

it("Items can be sorted", () => {
cy.intercept("https://api.github.com/repos/*/*/issues**", (req) => {
req.reply({
statusCode: 200,
body: [issue1, issue2],
});
}).as("getIssues");
cy.visit("/");
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="price"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="price"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="time"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="time"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="priority"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="priority"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="activity"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get('[for="activity"]').click();
cy.get('div[id="issues-container"]').children().should("have.length", 2);
cy.get("#filter").type("draft");
cy.get('div[id="issues-container"]').children().should("have.length", 2);
});

it("User can log in", () => {
cy.intercept("https://api.github.com/repos/*/*/issues**", (req) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are intercepting everything doesn't that kill the point of the test? Why don't we do real end-to-end tests? What if Supabase is broken and we can't oauth etc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't develop the other end (eg the backend) I didn't think it was relevant to test it. If GitHub backend breaks there is nothing we can do. The proper way to handle that would be to have BE error test cases, and to have a health check running on the production instance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay sounds smart I'm in. You can file some issues for this with sufficient details so we can get around to it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean an issue mentioning the creation of e2e testing with the real endpoint correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper way as you say

req.reply({
statusCode: 200,
body: [issue1, issue2],
});
}).as("getIssues");
cy.intercept("https://api.github.com/user", (req) => {
req.reply({
statusCode: 200,
body: githubUser,
});
}).as("getUser");
cy.intercept("https://iyybhhiflwbsjopsgaow.supabase.co/auth/v1/authorize?provider=github", (req) => {
req.reply({
statusCode: 200,
});
// Simulate login token
window.localStorage.setItem("sb-wfzpewmlyiozupulbuur-auth-token", JSON.stringify(loginToken));
}).as("githubLogin");
cy.visit("/");
cy.get("#github-login-button").click();
// Manually come back to home page after "login"
cy.visit("/");
cy.get("#authenticated").should("exist");
});
});
115 changes: 115 additions & 0 deletions cypress/fixtures/issue-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085",
"repository_url": "https://api.github.com/repos/ubiquity/devpool-directory",
"labels_url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085/labels{/name}",
"comments_url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085/comments",
"events_url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085/events",
"html_url": "https://github.com/ubiquity/devpool-directory/issues/1085",
"id": 2152486458,
"node_id": "I_kwDOJWyCVM6ATFY6",
"number": 1085,
"title": "Secure Secrets for Generalized Continuous Deploys",
"user": {
"login": "ubiquibot[bot]",
"id": 113181824,
"node_id": "BOT_kgDOBr8EgA",
"avatar_url": "https://avatars.githubusercontent.com/in/236521?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ubiquibot%5Bbot%5D",
"html_url": "https://github.com/apps/ubiquibot",
"followers_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/followers",
"following_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/following{/other_user}",
"gists_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/subscriptions",
"organizations_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/orgs",
"repos_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/repos",
"events_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/events{/privacy}",
"received_events_url": "https://api.github.com/users/ubiquibot%5Bbot%5D/received_events",
"type": "Bot",
"site_admin": false
},
"labels": [
{
"id": 5389720203,
"node_id": "LA_kwDOJWyCVM8AAAABQUCaiw",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/Time:%20%3C1%20Day",
"name": "Time: <1 Day",
"color": "ededed",
"default": false,
"description": null
},
{
"id": 5390246739,
"node_id": "LA_kwDOJWyCVM8AAAABQUijUw",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/Pricing:%20600%20USD",
"name": "Pricing: 600 USD",
"color": "ededed",
"default": false,
"description": null
},
{
"id": 5639152271,
"node_id": "LA_kwDOJWyCVM8AAAABUB6ijw",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/Unavailable",
"name": "Unavailable",
"color": "ededed",
"default": false,
"description": ""
},
{
"id": 5906091134,
"node_id": "LA_kwDOJWyCVM8AAAABYAfMfg",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/Priority:%203%20(High)",
"name": "Priority: 3 (High)",
"color": "ededed",
"default": false,
"description": null
},
{
"id": 6607857261,
"node_id": "LA_kwDOJWyCVM8AAAABidvmbQ",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/Partner:%20ubiquity/cloudflare-deploy-action",
"name": "Partner: ubiquity/cloudflare-deploy-action",
"color": "ededed",
"default": false,
"description": null
},
{
"id": 6607857262,
"node_id": "LA_kwDOJWyCVM8AAAABidvmbg",
"url": "https://api.github.com/repos/ubiquity/devpool-directory/labels/id:%20I_kwDOLGaFrM6ATBS4",
"name": "id: I_kwDOLGaFrM6ATBS4",
"color": "ededed",
"default": false,
"description": null
}
],
"state": "open",
"locked": false,
"assignee": null,
"assignees": [],
"milestone": null,
"comments": 0,
"created_at": "2024-02-24T21:18:55Z",
"updated_at": "2024-02-25T06:40:46Z",
"closed_at": null,
"author_association": "CONTRIBUTOR",
"active_lock_reason": null,
"body": "https://github.com/ubiquity/cloudflare-deploy-action/issues/1",
"reactions": {
"url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085/reactions",
"total_count": 0,
"+1": 0,
"-1": 0,
"laugh": 0,
"hooray": 0,
"confused": 0,
"heart": 0,
"rocket": 0,
"eyes": 0
},
"timeline_url": "https://api.github.com/repos/ubiquity/devpool-directory/issues/1085/timeline",
"performed_via_github_app": null,
"state_reason": null
}
Loading