Skip to content

Commit

Permalink
add some rough e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NoxHarmonium committed Oct 11, 2024
1 parent e94c6a9 commit cb50d13
Show file tree
Hide file tree
Showing 18 changed files with 3,619 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"forwardPorts": [8000, 5432],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "uv sync --all-extras && uv run fastapi dev ./openday_scavenger/main.py --reload",
// No hup makes the server run in the background so it doesn't hang the build process
"postCreateCommand": "nohup bash -c 'uv sync --all-extras && uv run fastapi dev ./openday_scavenger/main.py --reload &'",

// Configure tool-specific properties.
"customizations": {
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: E2E Test on PR

on:
push: {} # TODO remove this once tested
pull_request:
branches:
- main
jobs:
cypress-run:
name: e2e
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Start Server
uses: devcontainers/[email protected]
with:
runCmd: uv sync --all-extras && uv run fastapi dev ./openday_scavenger/main.py

- name: Cypress run
uses: cypress-io/github-action@v6
with:
working-directory: ./e2e
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,5 @@ cython_debug/
# direnv
.envrc

# direnv
.envrc
# Dev container output
nohup.out
158 changes: 158 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Created by https://www.toptal.com/developers/gitignore/api/cypressio
# Edit at https://www.toptal.com/developers/gitignore?templates=cypressio

### CypressIO ###
# gitignore template for the CypressIO, browser test framework
# website: https://www.cypress.io/

cypress/results/*
cypress/reports/*
cypress/screenshots/*
cypress/videos/*

# End of https://www.toptal.com/developers/gitignore/api/cypressio

# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

### Node Patch ###
# Serverless Webpack directories
.webpack/

# Optional stylelint cache

# SvelteKit build / generate output
.svelte-kit

# End of https://www.toptal.com/developers/gitignore/api/node
1 change: 1 addition & 0 deletions e2e/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 20.18.0
11 changes: 11 additions & 0 deletions e2e/cypress.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
baseUrl: "http://localhost:8000",
},
env: {
admin_username: "admin",
admin_password: "admin",
},
});
100 changes: 100 additions & 0 deletions e2e/cypress/e2e/full-e2e.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
describe("template spec", () => {
before(() => {
cy.adminSeedPuzzleData();
cy.adminStartSession();
});

describe("for a single session", () => {
it("the demo puzzle should accept incorrect and correct answers", () => {
cy.visit("/puzzles/demo/");

// Incorrect
cy.contains("h1", "Demo Puzzle");
cy.get("input#answer").type("Fhqwhgads");
cy.get("form").submit();
cy.contains("h1", "Oh no!").should("be.visible");
cy.contains("a", "Try This Puzzle Again!").click();

// Correct
cy.get("input#answer").type("3{enter}");
cy.contains("h1", "That's Right!").should("be.visible");
});

it("puzzle responses should be recorded in the admin section", () => {
// Filter by UID
const visitorUid = Cypress.env("VisitorUid");

cy.adminVisit("/admin/responses/");
cy.get("input#visitor_uid").type(visitorUid);

cy.get("#response-table table tbody").within(() => {
cy.get("tr").should("have.length", 2);
cy.contains("tr", "No").within(() => {
cy.get("td").eq(0).should("contain.text", "demo");
cy.get("td").eq(2).should("contain.text", "Fhqwhgads");
});
cy.contains("tr", "Yes").within(() => {
cy.get("td").eq(0).should("contain.text", "demo");
cy.get("td").eq(2).should("contain.text", "3");
});
});

cy.get("input#visitor_uid").clear();

// Filter by Name

cy.get("input#visitor_uid").type("demo");

cy.get("#response-table table tbody").within(() => {
cy.get("tr").should("have.length", 2);
cy.contains("tr", "No").within(() => {
cy.get("td").eq(0).should("contain.text", "demo");
cy.get("td").eq(2).should("contain.text", "Fhqwhgads");
});
cy.contains("tr", "Yes").within(() => {
cy.get("td").eq(0).should("contain.text", "demo");
cy.get("td").eq(2).should("contain.text", "3");
});
});

cy.get("input#visitor_uid").clear();
});

it("visitors can be checked out from the admin page", () => {
// Filter by UID
const visitorUid = Cypress.env("VisitorUid");

cy.adminVisit("/admin/visitors/");
cy.get("input#still_playing").check();

cy.get("input#uid_filter").type(visitorUid);

cy.get("#visitor-table table tbody").within(() => {
cy.get("tr").should("have.length", 1);
cy.contains("tr", visitorUid).within(() => {
cy.get("td").eq(1).should("not.be.empty");
cy.get("td").eq(2).should("be.empty");
cy.get("td").eq(4).should("contain.text", "Playing");
});

cy.contains("button", "Checkout").click();
});

// TODO: Should the filter clear when checkout is pressed?
cy.get("input#uid_filter").type(visitorUid);

cy.get("#visitor-table table tbody tr").should("not.exist");

cy.get("input#still_playing").uncheck();

cy.get("#visitor-table table tbody").within(() => {
cy.get("tr").should("have.length", 1);
cy.contains("tr", visitorUid).within(() => {
cy.get("td").eq(1).should("not.be.empty");
cy.get("td").eq(2).should("not.be.empty");
cy.get("td").eq(4).should("contain.text", "Finished");
});
});
});
});
});
12 changes: 12 additions & 0 deletions e2e/cypress/fixtures/puzzle_seed_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"puzzles": [
{
"name": "demo",
"id": 1,
"active": true,
"notes": "",
"answer": "3",
"location": ""
}
]
}
48 changes: 48 additions & 0 deletions e2e/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//

import "cypress-file-upload";

Cypress.Commands.add("adminVisit", (url) => {
cy.visit(url, {
auth: {
username: "admin",
password: "admin",
},
});
});

Cypress.Commands.add("adminSeedPuzzleData", () => {
cy.intercept("POST", "/admin/puzzles/upload-json").as("uploadRequest");

cy.adminVisit("/admin/puzzles");
cy.contains(".btn", "Upload JSON").click();
cy.get("form#upload-json-form").should("be.visible").as("uploadForm");
cy.get('input#json-file[type="file"]').as("fileInput");
cy.get("@fileInput").attachFile("puzzle_seed_data.json");
cy.get("@uploadForm").submit();
cy.wait("@uploadRequest").its("response.statusCode").should("eq", 200);
});

Cypress.Commands.add("adminStartSession", () => {
cy.adminVisit("/admin/visitors/pool/");
cy.get("input#number_of_entries").type("1");
cy.get("button#create-button").click();
cy.get("#visitor-pool-table table tbody tr:first-child").within(() => {
cy.get("td:first-child").then((cell) => {
Cypress.env("VisitorUid", cell.text());
});
cy.get("td:nth-child(2) a").click();
});

cy.contains(".container", "Scan puzzle lock to unlock the puzzle!");
});
Loading

0 comments on commit cb50d13

Please sign in to comment.