diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e6287f3a0..842f9d496 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,17 +1,34 @@ name: E2E Tests on: - workflow_dispatch # disabled pending db seeding - # push: - # branches: [ main ] - # pull_request: - # branches: [ main ] + push: + branches: [ main ] + pull_request: + branches: [ main ] jobs: - build: + e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: build containers and run cypress e2e tests - # TODO: seed the database with a sparse(r) dump so there are pages to test.. + + - name: deploy services in e2e mode run: make e2e + + - name: run e2e tests + uses: cypress-io/github-action@v6 + with: + working-directory: e2e + wait-on: "http://localhost:8000" + wait-on-timeout: 300 + + - name: display service logs + if: failure() + run: docker compose -f docker-compose.yml -f e2e.yml logs + + - name: upload cypress videos + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cypress-videos + path: e2e/cypress/videos diff --git a/Makefile b/Makefile index 4e584b73c..4aebccd17 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ release-version: .env .PHONY: docker-compose.yml docker-compose.yml: base.yml dev.yml staging.yml prod.yml config.mk $(PGPASS_PATH) release-version case "$(DEPLOY_ENVIRONMENT)" in \ - dev|staging|e2e) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ + dev|staging) docker compose -f base.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ prod) docker compose -f base.yml -f staging.yml -f $(DEPLOY_ENVIRONMENT).yml config > docker-compose.yml;; \ *) echo "invalid environment. must be either dev, staging or prod" 1>&2; exit 1;; \ esac @@ -138,9 +138,21 @@ clean_deploy: clean test: build docker compose run --rm server /code/deploy/test.sh +# e2e testing setup + +E2E_SHARED_DIR=${DOCKER_SHARED_DIR}/e2e +E2E_BACKUPS_PATH=${E2E_SHARED_DIR}/backups +E2E_REPO_PATH=${E2E_BACKUPS_PATH}/repo + +$(E2E_REPO_PATH): + mkdir -p $(E2E_BACKUPS_PATH) + wget -c ${BORG_REPO_URL} -P $(E2E_BACKUPS_PATH) + tar -Jxf $(E2E_BACKUPS_PATH)/repo.tar.xz -C $(E2E_BACKUPS_PATH) + .PHONY: e2e -e2e: DEPLOY_ENVIRONMENT=e2e -e2e: build - docker compose run server inv collectstatic - docker compose run --rm e2e npm run test - docker compose down +e2e: docker-compose.yml secrets $(DOCKER_SHARED_DIR) $(E2E_REPO_PATH) + docker compose -f docker-compose.yml -f e2e.yml up -d --build + docker compose -f docker-compose.yml -f e2e.yml exec server bash -c "\ + inv borg.restore --force && \ + ./manage.py migrate && \ + inv prepare" diff --git a/django/core/settings/e2e.py b/django/core/settings/e2e.py index 6ba62d430..a7bc69622 100644 --- a/django/core/settings/e2e.py +++ b/django/core/settings/e2e.py @@ -1,5 +1,25 @@ -from .dev import * +from .test import * -DEPLOY_ENVIRONMENT = Environment.TEST +DEBUG = True DJANGO_VITE_DEV_MODE = False + +SHARE_DIR = path.realpath("/shared/e2e") +LIBRARY_ROOT = path.join(SHARE_DIR, "library") +LIBRARY_PREVIOUS_ROOT = path.join(SHARE_DIR, ".latest") +REPOSITORY_ROOT = path.join(BASE_DIR, "repository") +BACKUP_ROOT = path.join(SHARE_DIR, "backups") +BORG_ROOT = path.join(BACKUP_ROOT, "repo") +EXTRACT_ROOT = path.join(SHARE_DIR, "extract") +MEDIA_ROOT = path.join(SHARE_DIR, "media") + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.getenv("DB_NAME"), + "USER": os.getenv("DB_USER"), + "PASSWORD": read_secret("db_password", os.getenv("DB_PASSWORD")), + "HOST": "e2edb", + "PORT": os.getenv("DB_PORT"), + } +} diff --git a/django/curator/invoke_tasks/borg.py b/django/curator/invoke_tasks/borg.py index 56408c564..2db1922ad 100644 --- a/django/curator/invoke_tasks/borg.py +++ b/django/curator/invoke_tasks/borg.py @@ -203,14 +203,19 @@ def restore_database( @task() def restore( - ctx, repo=settings.BORG_ROOT, archive=None, target_database=db._DEFAULT_DATABASE + ctx, + repo=settings.BORG_ROOT, + archive=None, + target_database=db._DEFAULT_DATABASE, + force=False, ): """Restore the library files, media files and database to the state given in the borg repo at path REPO using archive ARCHIVE. The target_database argument is for testing so a different database can be used to make sure the database is getting restored properly""" - confirm( - "Are you sure you want to restore the database and all file content (y/n)? " - ) + if not force: + confirm( + "Are you sure you want to restore the database and all file content (y/n)? " + ) with tempfile.TemporaryDirectory(dir=settings.SHARE_DIR) as working_directory: _restore( ctx, diff --git a/django/curator/invoke_tasks/database.py b/django/curator/invoke_tasks/database.py index b2427073c..43035704e 100644 --- a/django/curator/invoke_tasks/database.py +++ b/django/curator/invoke_tasks/database.py @@ -42,7 +42,7 @@ def create_pgpass_file(ctx, db_key=_DEFAULT_DATABASE, force=False): if os.path.isfile(pgpass_path) and not force: return with open(pgpass_path, "w+") as pgpass: - pgpass.write("db:*:*:{db_user}:{db_password}\n".format(**db_config)) + pgpass.write("{db_host}:*:*:{db_user}:{db_password}\n".format(**db_config)) ctx.run("chmod 0600 ~/.pgpass") @@ -132,7 +132,7 @@ def restore_from_dump( cat_cmd = "zcat" drop(ctx, database=target_database, create=True) ctx.run( - "{cat_cmd} {dumpfile} | psql -w -q -o restore-from-dump-log.txt -h db {db_name} {db_user}".format( + "{cat_cmd} {dumpfile} | psql -w -q -o restore-from-dump-log.txt -h {db_host} {db_name} {db_user}".format( cat_cmd=cat_cmd, dumpfile=dumpfile, **db_config ), echo=True, diff --git a/e2e.yml b/e2e.yml index 436b47f05..8b406f2f1 100644 --- a/e2e.yml +++ b/e2e.yml @@ -1,27 +1,43 @@ services: - e2e: - image: comses/cypress - build: e2e - environment: - - CYPRESS_baseUrl=http://server:8000 + db: + image: alpine # disable the normal db container + command: tail -f /dev/null + healthcheck: null + e2edb: + image: postgis/postgis:15-3.4 + secrets: + - db_password # re-using the same db password volumes: - - ./e2e/cypress:/code/cypress - - ./e2e/cypress.config.js:/code/cypress.config.js - - ./django/deploy/wait-for-it.sh:/code/wait-for-it.sh - depends_on: - - server + - ./build/secrets/db_password:/run/secrets/db_password + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] + interval: 30s + timeout: 5s + retries: 5 + environment: + POSTGRES_USER: "${DB_USER}" + POSTGRES_DB: "${DB_NAME}" + POSTGRES_PASSWORD_FILE: /run/secrets/db_password vite: - volumes: - - ./frontend:/code command: ["npm", "run", "build"] environment: NODE_ENV: "e2e" server: - image: comses/server:dev - volumes: - - ./django:/code - - ./docs:/docs + depends_on: + db: + condition: service_started + e2edb: + condition: service_healthy + elasticsearch: + condition: service_started + redis: + condition: service_started + vite: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000"] + interval: 30s + timeout: 10s + retries: 5 environment: DJANGO_SETTINGS_MODULE: "core.settings.e2e" - ports: - - "127.0.0.1:8000:8000" diff --git a/e2e/Dockerfile b/e2e/Dockerfile deleted file mode 100644 index ec2e8c569..000000000 --- a/e2e/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM cypress/base:18.14.1 -WORKDIR /code - -# only install dependencies if package.json or package-lock.json has changed -COPY package.json . -COPY package-lock.json . - -# suppress most of the cypress messages -ENV CI=1 - -RUN npm install - -# verify that cypress is installed correctly -RUN npm run cy:run -- verify diff --git a/e2e/cypress.config.js b/e2e/cypress.config.js index cf0cec0be..6906d32df 100644 --- a/e2e/cypress.config.js +++ b/e2e/cypress.config.js @@ -3,9 +3,9 @@ const { defineConfig } = require("cypress"); module.exports = defineConfig({ e2e: { baseUrl: "http://localhost:8000", - specPattern: ["cypress/e2e/**/*.spec.ts"], + specPattern: ["cypress/tests/**/*.spec.ts"], supportFile: false, screenshotOnRunFailure: false, - video: false, + video: true, } }); diff --git a/e2e/cypress/fixtures/codebase/codebasetestimage.png b/e2e/cypress/fixtures/codebase/codebasetestimage.png new file mode 100644 index 000000000..96a5268e8 Binary files /dev/null and b/e2e/cypress/fixtures/codebase/codebasetestimage.png differ diff --git a/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt b/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt new file mode 100644 index 000000000..bff092200 --- /dev/null +++ b/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt @@ -0,0 +1 @@ +//test narative documentation diff --git a/e2e/cypress/fixtures/codebase/testSimulationOutput.txt b/e2e/cypress/fixtures/codebase/testSimulationOutput.txt new file mode 100644 index 000000000..9f1541be1 --- /dev/null +++ b/e2e/cypress/fixtures/codebase/testSimulationOutput.txt @@ -0,0 +1 @@ +//testSimulationOutput diff --git a/e2e/cypress/fixtures/codebase/testSourceCode.txt b/e2e/cypress/fixtures/codebase/testSourceCode.txt new file mode 100644 index 000000000..003a0b85d --- /dev/null +++ b/e2e/cypress/fixtures/codebase/testSourceCode.txt @@ -0,0 +1,2 @@ +//test source code + diff --git a/e2e/cypress/fixtures/codebase/testUploadData.txt b/e2e/cypress/fixtures/codebase/testUploadData.txt new file mode 100644 index 000000000..89786372e --- /dev/null +++ b/e2e/cypress/fixtures/codebase/testUploadData.txt @@ -0,0 +1 @@ +//test upload data diff --git a/e2e/cypress/tests/beforeLoginTests.spec.ts b/e2e/cypress/tests/beforeLoginTests.spec.ts new file mode 100644 index 000000000..44ec659aa --- /dev/null +++ b/e2e/cypress/tests/beforeLoginTests.spec.ts @@ -0,0 +1,19 @@ +//test ability to browse/download codebases +describe("Browse Codebases", () => { + it("should browse the model library and be able to enter a search query", () => { + cy.visit("/"); // visit homepage + cy.visit("/codebases"); + assert(cy.get("h1").contains("Computational Model Library")); + }); +}); + +//download codebase +describe("Download Codebases", () => { + it("should download a codebase", () => { + // Navigate to the first 'search-result', find the 'a' tag within the 'title' div, and click it + cy.get(".search-result", { timeout: 10000 }) + .first() + .find(".title a") + .click(); + }); +}); diff --git a/e2e/cypress/tests/codebase.spec.ts b/e2e/cypress/tests/codebase.spec.ts new file mode 100644 index 000000000..4cf706cac --- /dev/null +++ b/e2e/cypress/tests/codebase.spec.ts @@ -0,0 +1,121 @@ +import { loginBeforeEach } from "./setup.spec"; +import "cypress-file-upload"; + +//login +describe("Login", () => { + it("should log into comses homepage with test user", () => { + loginBeforeEach("test_user", "123456"); + }); +}); + + +describe("Visit codebases page", () => { + //codebases PAGE + + it("should visit the codebases page", () => { + cy.visit("/codebases"); + assert(cy.get("h1").contains("Computational Model Library")); + }); + + it("should be able to download a codebase", () => { + cy.visit("/codebases"); + cy.get(".search-result").first().find("a").first().click(); + cy.get("#djHideToolBarButton").click(); + cy.get('button.btn.btn-primary.my-1.w-100[rel="nofollow"]').click(); + cy.get("#form-field-industry").select("College/University"); + cy.get("#form-field-affiliation").type("Arizona State University{enter}", { + force: true, + }); + cy.get("#form-field-reason").select("Research"); + cy.get( + 'button[type="submit"][form="download-request-form"].btn.btn-success' + ).click(); + }); + + it("should be able to upload a codebase", () => { + loginBeforeEach("test_user", "123456"); + cy.visit("/codebases"); + assert(cy.get("h1").contains("Computational Model Library")); + cy.get("#djHideToolBarButton").click(); + cy.contains("Publish a model").click(); + cy.get('[data-cy="codebase title"]').type("Codebase Title"); + cy.get('[data-cy="codebase description"]').type("Codebase Description"); + cy.get('[data-cy="codebase replication-text"]').type( + "Codebase Replication Text" + ); + cy.get('[data-cy="codebase associated publications"]').type( + "Codebase Associated Publications" + ); + cy.get('[data-cy="codebase references"]').type("Codebase References"); + cy.get('[data-cy="next"]').click(); + //add images + cy.get('[data-cy="add image"]').click(); //add image + cy.get('[data-cy="upload-image"]') + .first() + .selectFile("cypress/fixtures/codebase/codebasetestimage.png", { + force: true, + }); + cy.get('button.btn.btn-outline-gray[data-bs-dismiss="modal"]') + .get("button") + .first() + .click({ force: true }); + cy.get('button.btn.btn-outline-gray[data-bs-dismiss="modal"]') + .should("be.visible") + .and("not.be.disabled") + .first() + .click({ force: true }); + cy.get("body").click(0, 0); + cy.get("body").click(0, 0); //FIX: find a more precise way of closing the image upload modal + + //add source code files + cy.get('[data-cy="upload-code"]') + .first() + .selectFile("cypress/fixtures/codebase/testSourceCode.txt", { + force: true, + }); + cy.get('[data-cy="upload-docs"]') + .first() + .selectFile("cypress/fixtures/codebase/testNarrativeDocumentation.txt", { + force: true, + }); + cy.get('[data-cy="upload-data"]') + .first() + .selectFile("cypress/fixtures/codebase/testUploadData.txt", { + force: true, + }); + cy.get('[data-cy="upload-results"]') + .first() + .selectFile("cypress/fixtures/codebase/testSimulationOutput.txt", { + force: true, + }); + cy.get('[data-cy="add metadata"]').click(); + cy.get('[data-cy="release-notes"] textarea').type("Release notes"); + cy.get('[data-cy="embargo-end-date"]').click(); + cy.get('[data-cy="embargo-end-date"]').contains("29").click(); + cy.get('[data-cy="operating-system"] select').select( + "Operating System Independent" + ); + cy.get('[data-cy="software-frameworks"]').type("NetLogo {enter} "); + cy.get("body").click(0, 0); + cy.get('[data-cy="programming-languages"').type("Net Logo {enter}"); + cy.get("body").click(0, 0); + cy.get('[data-cy="license"] .multiselect__select').click(); + cy.get('[data-cy="license"] .multiselect__element') + .contains("GPL-2.0") + .click(); + cy.get('[data-cy="save-and-continue"]').click(); + cy.get('button.btn.btn-danger[rel="nofollow"]').click(); + cy.get('button[type="submit"].btn.btn-danger[form="publish-form"]').click(); + }); + + it("should verify that the codebase was uploaded correctly", () => { + cy.visit("/codebases"); + cy.get(".search-result").first().find("a").first().click(); + assert(cy.get("h1").contains("Codebase Title")); + assert(cy.get("p").contains("Codebase Description")); + assert(cy.get("p").contains("Codebase Replication Text")); + assert(cy.get("p").contains("Codebase Associated Publications")); + assert(cy.get("p").contains("Codebase References")); + + }) +}); \ No newline at end of file diff --git a/e2e/cypress/tests/event.spec.ts b/e2e/cypress/tests/event.spec.ts new file mode 100644 index 000000000..a9798bd96 --- /dev/null +++ b/e2e/cypress/tests/event.spec.ts @@ -0,0 +1,55 @@ +import { loginBeforeEach } from "./setup.spec"; +import "cypress-file-upload"; + + +describe("Visit events page", () => { + //EVENTS PAGE + + it("should visit the events page", () => { + cy.visit("/events"); + assert(cy.get("h1").contains("Community Events")); + cy.get(".card-body").first().find("a").first().click(); + assert(cy.get("h1").contains("Title")); + }); + + it("should be able to search for a specific event", () => { + cy.visit("/events"); + cy.get('input[class="form-control"]').type( + "Call for applications to organize a 2022 CECAM-Lorentz funded workshop on modeling" + ); + cy.get("button.btn.btn-primary").click(); + }); + + it("should be able to submit an event", () => { + loginBeforeEach("test_user", "123456"); + cy.visit("/events"); + cy.get("#djHideToolBarButton").click(); + cy.get(".text-white").first().click({ force: true }); + cy.get('[data-cy="event title"]').type("Title"); + cy.get('[data-cy="event location"]').type("Location"); + cy.get('[data-cy="event start date"]').first().click(); //event start date + cy.get('[data-cy="event start date"]').contains("22").click(); + cy.get('[data-cy="event end date"]').first().click(); //event end date + cy.get('[data-cy="event end date"]').contains("27").click(); + cy.get('[data-cy="early registration deadline"]').first().click(); //early registration deadline + cy.get('[data-cy="early registration deadline"]').contains("22").click(); + cy.get('[data-cy="registration deadline"]').first().click(); //registration deadline + cy.get('[data-cy="registration deadline"]').contains("25").click(); + cy.get('[data-cy="submission deadline"]').first().click(); //submission deadline + cy.get('[data-cy="submission deadline"]').contains("22").click(); + cy.get('[data-cy="description"]').type("Description"); + cy.get('[data-cy="summary"]').type("Summary"); + cy.get('[data-cy="external url"]').type("https://www.comses.net/"); + cy.get('[data-cy="create button"]').click(); + }); + + it("should be able to verify event was submitted correctly", () => { + cy.visit("/events"); + cy.get("#djHideToolBarButton").click(); + assert(cy.get("h1").contains("Community Events")); + cy.get(".card-body").first().find("a").first().click(); + assert(cy.get("h1").contains("Title")); + assert(cy.get("p").contains("Description")); + assert(cy.get("p").contains("Location")); + }); + }); \ No newline at end of file diff --git a/e2e/cypress/tests/job.spec.ts b/e2e/cypress/tests/job.spec.ts new file mode 100644 index 000000000..6382a2851 --- /dev/null +++ b/e2e/cypress/tests/job.spec.ts @@ -0,0 +1,35 @@ +import { loginBeforeEach } from "./setup.spec"; +import "cypress-file-upload"; + +describe("Visit jobs page", () => { + //JOBS PAGE + + it("should visit the jobs page", () => { + cy.visit("/jobs"); + assert(cy.get("h1").contains("Jobs & Appointments")); + }); + + it("should be able to submit a job posting", () => { + loginBeforeEach("test_user", "123456"); + cy.visit("/jobs"); + cy.get("#djHideToolBarButton").click(); + cy.get(".text-white").first().click({ force: true }); + cy.get('[data-cy="job title"]').type("Job Title"); + cy.get('[data-cy="job description"]').type("Job Description"); + cy.get('[data-cy="job summary"]').type("Job Summary"); + cy.get('[data-cy="external url"]').type("https://www.comses.net/"); + cy.get('[data-cy="application deadline"]').first().click(); + cy.get('[data-cy="application deadline"]').contains("29").click(); + cy.get('[data-cy="create button"]').click(); + }); + + it("should be able to verify job was submitted correctly", () => { + cy.visit("/jobs"); + cy.get("#djHideToolBarButton").click(); + assert(cy.get("h1").contains("Jobs & Appointments")); + cy.get(".card-body").first().find("a").first().click(); + assert(cy.get("h1").contains("Job Title")); + assert(cy.get("p").contains("Job Description")); + }); + }); + \ No newline at end of file diff --git a/e2e/cypress/tests/setup.spec.ts b/e2e/cypress/tests/setup.spec.ts new file mode 100644 index 000000000..4876a3eb3 --- /dev/null +++ b/e2e/cypress/tests/setup.spec.ts @@ -0,0 +1,8 @@ +//contains setup function for smoke tests +export const loginBeforeEach = (username, password) => { + cy.visit("/accounts/login/?next=/"); + assert(cy.get("h1").contains("Sign In")); //simply goes to sign in page + cy.get('input[name="login"]').type(username); + cy.get('input[name="password"]').type(password); + cy.contains("button", "Sign In").click(); +}; diff --git a/e2e/cypress/tests/user.spec.ts b/e2e/cypress/tests/user.spec.ts new file mode 100644 index 000000000..6b3cdcf37 --- /dev/null +++ b/e2e/cypress/tests/user.spec.ts @@ -0,0 +1,20 @@ +import { loginBeforeEach } from "./setup.spec"; +import "cypress-file-upload"; + +//login +describe("User tests", () => { + it("should visit the users page", () => { + loginBeforeEach("admin_user", "123456"); + cy.visit("/users"); + cy.get("#djHideToolBarButton").click(); + cy.contains('div', 'My profile').click(); + cy.contains('a', 'Edit Profile').click(); + cy.get('#form-field-givenName').clear() + cy.get('[data-cy="first name"]').type("New first name"); + cy.get('#form-field-familyName').clear() + cy.get('[data-cy="last name"]').type("New last name"); + cy.get('[data-cy="submit"]').click(); + assert(cy.get("h4").contains("New first name New last name")); + + }); + }); \ No newline at end of file diff --git a/e2e/package-lock.json b/e2e/package-lock.json index fa61ddfdf..f70c54e0b 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "devDependencies": { "cypress": "^13.6.2", + "cypress-file-upload": "^5.0.8", "typescript": "~4.8.4" }, "engines": { @@ -609,6 +610,18 @@ "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, + "node_modules/cypress-file-upload": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", + "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", + "dev": true, + "engines": { + "node": ">=8.2.1" + }, + "peerDependencies": { + "cypress": ">3.0.0" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/e2e/package.json b/e2e/package.json index 4709473de..8b3ce8ea6 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -4,11 +4,11 @@ "description": "CoMSES Net e2e/integration tests", "main": "index.ts", "scripts": { - "cy:run": "cypress run", - "test": "./wait-for-it.sh server:8000 -t 120 -- cypress run" + "test": "cypress run" }, "devDependencies": { "cypress": "^13.6.2", + "cypress-file-upload": "^5.0.8", "typescript": "~4.8.4" }, "engines": { diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts new file mode 100644 index 000000000..8959a4f25 --- /dev/null +++ b/frontend/cypress.config.ts @@ -0,0 +1,7 @@ +export default { + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + }, +}; diff --git a/frontend/cypress/fixtures/example.json b/frontend/cypress/fixtures/example.json new file mode 100644 index 000000000..02e425437 --- /dev/null +++ b/frontend/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts new file mode 100644 index 000000000..698b01a42 --- /dev/null +++ b/frontend/cypress/support/commands.ts @@ -0,0 +1,37 @@ +/// +// *********************************************** +// This example commands.ts 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 +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +// +// declare global { +// namespace Cypress { +// interface Chainable { +// login(email: string, password: string): Chainable +// drag(subject: string, options?: Partial): Chainable +// dismiss(subject: string, options?: Partial): Chainable +// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable +// } +// } +// } \ No newline at end of file diff --git a/frontend/cypress/support/e2e.ts b/frontend/cypress/support/e2e.ts new file mode 100644 index 000000000..f80f74f8e --- /dev/null +++ b/frontend/cypress/support/e2e.ts @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') \ No newline at end of file diff --git a/frontend/src/components/CodebaseEditForm.vue b/frontend/src/components/CodebaseEditForm.vue index e4605a0ab..b617a8918 100644 --- a/frontend/src/components/CodebaseEditForm.vue +++ b/frontend/src/components/CodebaseEditForm.vue @@ -5,6 +5,7 @@ name="title" label="Title" help="A short title describing this computational model, limited to 300 characters" + data-cy="codebase title" required /> @@ -13,6 +14,7 @@ name="description" label="Description" help="A summary description of your model similar to an abstract. There is no limit on length but it should be kept as succinct as possible." + data-cy="codebase description" required /> - diff --git a/frontend/src/components/CodebaseListSidebar.vue b/frontend/src/components/CodebaseListSidebar.vue index 8601924c3..909e12a27 100644 --- a/frontend/src/components/CodebaseListSidebar.vue +++ b/frontend/src/components/CodebaseListSidebar.vue @@ -3,6 +3,7 @@ create-label="Publish a model" create-url="/codebases/add/" search-label="Apply Filters" + data-cy="publish" :search-url="query" >