diff --git a/django/library/jinja2/library/codebases/releases/retrieve.jinja b/django/library/jinja2/library/codebases/releases/retrieve.jinja
index 6a87a6d55..85c62ab23 100644
--- a/django/library/jinja2/library/codebases/releases/retrieve.jinja
+++ b/django/library/jinja2/library/codebases/releases/retrieve.jinja
@@ -262,18 +262,18 @@
{% if release.live %}
{% if invite %}
{# direct release download #}
-
+
Download Version {{ release.version_number }}
{% else %}
{# use survey form #}
-
{% endif %}
{% else %}
{# direct review archive download #}
-
+
Download for Review
{% endif %}
diff --git a/e2e.yml b/e2e.yml
index 436b47f05..59ff00f68 100644
--- a/e2e.yml
+++ b/e2e.yml
@@ -1,27 +1,44 @@
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:
+ disable: true
+ 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_healthy
+ redis:
+ condition: service_started
+ vite:
+ condition: service_completed_successfully
+ 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/.prettierrc b/e2e/.prettierrc
new file mode 100644
index 000000000..34d141f48
--- /dev/null
+++ b/e2e/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": true,
+ "tabWidth": 2,
+ "useTabs": false,
+ "singleQuote": false,
+ "printWidth": 100,
+ "trailingComma": "es5",
+ "bracketSpacing": true,
+ "bracketSameLine": false,
+ "arrowParens": "avoid"
+}
\ No newline at end of file
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/testCodebase.zip b/e2e/cypress/fixtures/codebase/testCodebase.zip
new file mode 100644
index 000000000..5b048f722
Binary files /dev/null and b/e2e/cypress/fixtures/codebase/testCodebase.zip differ
diff --git a/e2e/cypress/fixtures/codebase/testCodebase/pyproject.toml b/e2e/cypress/fixtures/codebase/testCodebase/pyproject.toml
new file mode 100644
index 000000000..4c91fc03a
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testCodebase/pyproject.toml
@@ -0,0 +1,22 @@
+#generated from chatgpt
+
+[tool.poetry]
+name = "logistic-growth-model"
+version = "0.1.0"
+description = "A simple logistic growth model simulation"
+authors = ["Your Name
"]
+license = "MIT"
+readme = "README.md"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+numpy = "^1.21.0"
+matplotlib = "^3.4.2"
+
+[tool.poetry.dev-dependencies]
+pytest = "^6.2.4"
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
+
diff --git a/e2e/cypress/fixtures/codebase/testCodebase/src/main.py b/e2e/cypress/fixtures/codebase/testCodebase/src/main.py
new file mode 100644
index 000000000..4ad8582d6
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testCodebase/src/main.py
@@ -0,0 +1,33 @@
+#generated from chatgpt
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Logistic growth model parameters
+r = 0.1 # Growth rate
+K = 1000 # Carrying capacity
+P0 = 10 # Initial population
+t_max = 100 # Time duration
+dt = 1 # Time step
+
+# Time array
+time = np.arange(0, t_max, dt)
+
+# Population array
+population = np.zeros_like(time)
+population[0] = P0
+
+# Logistic growth model calculation
+for t in range(1, len(time)):
+ population[t] = population[t-1] + r * population[t-1] * (1 - population[t-1] / K) * dt
+
+# Plot the results
+plt.figure(figsize=(10, 6))
+plt.plot(time, population, label='Population')
+plt.xlabel('Time')
+plt.ylabel('Population')
+plt.title('Logistic Growth Model')
+plt.legend()
+plt.grid(True)
+plt.show()
+
diff --git a/e2e/cypress/fixtures/codebase/testCodebase/src/util.py b/e2e/cypress/fixtures/codebase/testCodebase/src/util.py
new file mode 100644
index 000000000..16cddf704
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testCodebase/src/util.py
@@ -0,0 +1,48 @@
+#generated from chatgpt
+
+# util.py
+import numpy as np
+import matplotlib.pyplot as plt
+
+def logistic_growth(r, K, P0, t_max, dt):
+ """
+ Calculate the logistic growth of a population.
+
+ Parameters:
+ r (float): Growth rate
+ K (int): Carrying capacity
+ P0 (int): Initial population
+ t_max (int): Time duration
+ dt (float): Time step
+
+ Returns:
+ time (ndarray): Array of time points
+ population (ndarray): Array of population values
+ """
+ time = np.arange(0, t_max, dt)
+ population = np.zeros_like(time)
+ population[0] = P0
+
+ for t in range(1, len(time)):
+ population[t] = population[t-1] + r * population[t-1] * (1 - population[t-1] / K) * dt
+
+ return time, population
+
+def plot_population_growth(time, population):
+ """
+ Plot the population growth over time.
+
+ Parameters:
+ time (ndarray): Array of time points
+ population (ndarray): Array of population values
+ """
+ plt.figure(figsize=(10, 6))
+ plt.plot(time, population, label='Population')
+ plt.xlabel('Time')
+ plt.ylabel('Population')
+ plt.title('Logistic Growth Model')
+ plt.legend()
+ plt.grid(True)
+ plt.show()
+
+
diff --git a/e2e/cypress/fixtures/codebase/testCodebase/test/test.py b/e2e/cypress/fixtures/codebase/testCodebase/test/test.py
new file mode 100644
index 000000000..4e54c521e
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testCodebase/test/test.py
@@ -0,0 +1,55 @@
+#generated from chatgpt
+
+# test.py
+import unittest
+import numpy as np
+from util import logistic_growth
+
+class TestLogisticGrowth(unittest.TestCase):
+ def test_logistic_growth_initial_population(self):
+ """
+ Test that the initial population is set correctly.
+ """
+ r = 0.1
+ K = 1000
+ P0 = 10
+ t_max = 100
+ dt = 1
+
+ time, population = logistic_growth(r, K, P0, t_max, dt)
+
+ self.assertEqual(population[0], P0, "Initial population should be equal to P0")
+
+ def test_logistic_growth_shape(self):
+ """
+ Test that the shape of the time and population arrays are correct.
+ """
+ r = 0.1
+ K = 1000
+ P0 = 10
+ t_max = 100
+ dt = 1
+
+ time, population = logistic_growth(r, K, P0, t_max, dt)
+
+ expected_length = int(t_max / dt)
+ self.assertEqual(len(time), expected_length, "Time array length should match expected length")
+ self.assertEqual(len(population), expected_length, "Population array length should match expected length")
+
+ def test_logistic_growth_population_limit(self):
+ """
+ Test that the population approaches the carrying capacity K.
+ """
+ r = 0.1
+ K = 1000
+ P0 = 10
+ t_max = 1000
+ dt = 1
+
+ time, population = logistic_growth(r, K, P0, t_max, dt)
+
+ self.assertAlmostEqual(population[-1], K, delta=K*0.01, msg="Final population should approach the carrying capacity K")
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt b/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt
new file mode 100644
index 000000000..f47a2513e
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testNarrativeDocumentation.txt
@@ -0,0 +1 @@
+//test narrative 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..4eca7d733
--- /dev/null
+++ b/e2e/cypress/fixtures/codebase/testSourceCode.txt
@@ -0,0 +1,113 @@
+//test source code from chatgpt
+
+globals [
+ grass ; the total amount of grass
+]
+
+turtles-own [
+ energy ; energy of the turtles
+]
+
+patches-own [
+ grass? ; whether or not there is grass on this patch
+ countdown ; number of ticks until grass grows back
+]
+
+to setup
+ clear-all
+ setup-patches
+ setup-turtles
+ reset-ticks
+end
+
+to setup-patches
+ ask patches [
+ set pcolor green
+ set grass? true
+ set countdown random 30
+ ]
+end
+
+to setup-turtles
+ create-turtles 50 [
+ ifelse random 2 = 0
+ [ set breed sheep set color white ]
+ [ set breed wolves set color black ]
+ set size 1.5
+ setxy random-xcor random-ycor
+ set energy random 10
+ ]
+end
+
+to go
+ if not any? turtles [ stop ]
+ ask patches [ grow-grass ]
+ ask turtles [
+ move
+ if breed = sheep [
+ set energy energy - 1
+ eat-grass
+ reproduce-sheep
+ ]
+ if breed = wolves [
+ set energy energy - 2
+ hunt-sheep
+ reproduce-wolves
+ ]
+ if energy <= 0 [ die ]
+ ]
+ tick
+end
+
+to grow-grass
+ if not grass? [
+ set countdown countdown - 1
+ if countdown <= 0 [
+ set grass? true
+ set pcolor green
+ set countdown random 30
+ ]
+ ]
+end
+
+to move
+ rt random 50
+ lt random 50
+ fd 1
+end
+
+to eat-grass
+ if grass? [
+ set grass? false
+ set pcolor brown
+ set energy energy + 4
+ ]
+end
+
+to hunt-sheep
+ let prey one-of sheep-here
+ if prey != nobody [
+ ask prey [ die ]
+ set energy energy + 10
+ ]
+end
+
+to reproduce-sheep
+ if energy > 20 [
+ set energy energy / 2
+ hatch 1 [
+ rt random-float 360
+ fd 1
+ ]
+ ]
+end
+
+to reproduce-wolves
+ if energy > 30 [
+ set energy energy / 2
+ hatch 1 [
+ rt random-float 360
+ fd 1
+ ]
+ ]
+end
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/fixtures/data.json b/e2e/cypress/fixtures/data.json
new file mode 100644
index 000000000..572f46206
--- /dev/null
+++ b/e2e/cypress/fixtures/data.json
@@ -0,0 +1,48 @@
+{
+ "codebases": [
+ {
+ "title": "Codebase Title",
+ "description": "Codebase Description",
+ "replication-text": "Codebase Replication Text",
+ "associated-publications": "Codebase Associated Publications",
+ "references": "Codebase References"
+ }
+ ],
+ "users": [
+ {
+ "username": "test_user",
+ "password": "123456"
+ },
+ {
+ "username": "admin_user",
+ "password": "123456"
+ },
+ {
+ "first-name": "New first name",
+ "last-name": "New last name"
+ }
+ ],
+ "events": [
+ {
+ "title": "Title",
+ "location": "Location",
+ "start-date": "22",
+ "end-date": "27",
+ "early-registration-deadline": "20",
+ "registration-deadline": "21",
+ "submission-deadline": "22",
+ "description": "Description",
+ "summary": "Summary",
+ "external-url": "https://www.comses.net/"
+ }
+ ],
+ "jobs": [
+ {
+ "title": "Job Title",
+ "description": "Job Description",
+ "summary": "Job Summary",
+ "external-url": "https://www.comses.net/",
+ "application-deadline": "29"
+ }
+ ]
+}
diff --git a/e2e/cypress/fixtures/default_data.json b/e2e/cypress/fixtures/default_data.json
new file mode 100644
index 000000000..593ef9597
--- /dev/null
+++ b/e2e/cypress/fixtures/default_data.json
@@ -0,0 +1,70 @@
+{
+ "codebases": [
+ {
+ "title": "Artificial Anasazi",
+ "description": "Replication of the well known Artificial Anasazi model that simulates the population dynamics between 800 and 1350 in the Long House Valley in Arizona. See also Marco A. Janssen (2009) Understanding Artificial Anasazi, Journal of Artificial Societies and Social Simulation 12 (4) 13",
+ "replication-text": "Axtell, Epstein, Dean, Gumerman, Swedlund, Harburger, Chakravarty, Hammond, Parker and Parker (2002) Population Growth and Collapse in a Multi-Agent Model of the Kayenta Anasazi in Long House Valley. PNAS 99(3): 7275-7279",
+ "associated-publications": "Marco A. Janssen (2009) Understanding Artificial Anasazi, Journal of Artificial Societies and Social Simulation 12 (4) 13 http://jasss.soc.surrey.ac.uk",
+ "references": "Codebase References",
+ "model-version": "1.1.0",
+ "license": "GPL-2.0",
+ "programming-language": "Netlogo",
+ "software-framework": "Netlogo",
+ "operating-system": "Operating System Independant",
+ "publish-date": "Wednesday, June 05, 2024",
+ "last-updated": "Wednesday, June 05, 2024"
+ }
+ ],
+ "users": [
+ {
+ "username": "test_user",
+ "password": "123456"
+ },
+ {
+ "username": "admin_user",
+ "password": "123456"
+ }
+ ],
+ "events": [
+ {
+ "title": "25th International Congress on Environmental Modelling and Software",
+ "location": "Michigan, USA",
+ "start-date": "Monday, June 19, 2045",
+ "end-date": "Sunday, June 25, 2045",
+ "early-registration-deadline": "None",
+ "registration-deadline": "Saturday, April 01, 2045",
+ "submission-deadline": "Monday, May 01, 2045",
+ "description": "The College of Agriculture and Natural Resources at Michigan State University is excited and honored to host the International Congress on Environmental Modelling & Software in 2045. Every day, Spartans work to solve the most pressing global challenges in our post-apocalyptic world. Michigan State University is beautiful during the summer and offers special access to cutting edge labs, affordable housing for student attendees and real-life examples of environments being modelled by researchers attending the 25th International Congress on Environmental Modelling & Software.",
+ "summary": "Michigan State University is proud to host the 25th International Congress on Environmental Modelling and Software June 18-25, 2045 in East Lansing on our beautiful campus.",
+ "external-url": "https://conference2045.iemss.org/ "
+ },
+ {
+ "title": "Epistemology of Modeling and Simulation",
+ "location": "Pittsburgh, USA",
+ "start-date": "Friday, April 01, 2011",
+ "end-date": "Sunday, April 03, 2011",
+ "early-registration-deadline": "Friday, January 07, 2011",
+ "registration-deadline": "Tuesday, March 01, 2011",
+ "submission-deadline": "None",
+ "description": "The conference will focus on philosophical issues that arise within the practice and application of contemporary research using modeling and simulation. The goal of this event is to bring together sophisticated work in philosophy of science and on-going efforts in modeling in order to build more effective collaboration between philosophers of science and those who build and employ models in a range of disciplines and applications.\n\nTopics will include:\n\n- The scientific status of computational techniques\n- Does simulation require a new epistemology?\n- The role of theory, experiment, model, and simulation\n- Varieties and purposes of scientific simulation\n- Analytic modeling versus computer simulation\n- Problems of juggling multiple and competing models\n- How do models fail?\n- Validation and verification of models and simulations\n- Promises and pitfalls of large, detailed, and realistic models",
+ "summary": "The conference will focus on philosophical issues that arise within the practice and application of contemporary research using modeling and simulation",
+ "external-url": "http://www.modelingepistemology.pitt.edu/ "
+ }
+ ],
+ "jobs": [
+ {
+ "title": "Post Doc Agent Based Modeler",
+ "description": "Seeking a post-doctoral scholar to help with creating models",
+ "summary": "Seeking a post-doctoral scholar to help with creating models",
+ "external-url": "https://university.edu/jobs/19823823908298",
+ "application-deadline": "Sunday, June 30, 2024"
+ },
+ {
+ "title": "Synergistic Solutions Consultant VI",
+ "description": "Are you ready to join a dynamic team of synergistic thought leaders and leverage your disruptive ideation skills? We are seeking a forward-thinking Solutions Consultant to spearhead our transformative paradigm shift. In this role, you will harness cutting-edge methodologies to facilitate cross-functional synergies and drive holistic stakeholder engagement.",
+ "summary": "Are you ready to join a dynamic team of synergistic thought leaders and leverage your disruptive ideation skills? We are seeking a forward-thinking Solutions Consultant to spearhead our transformative paradigm shift. In this role, you will harness cutting-edge methodologies to facilitate cross-functional synergies and drive holistic stakeholder engagement.",
+ "external-url": "https://quantumweb3solutions.com",
+ "application-deadline": "Saturday, November 01, 2025"
+ }
+ ]
+}
diff --git a/e2e/cypress/support/setup.ts b/e2e/cypress/support/setup.ts
new file mode 100644
index 000000000..6713e2750
--- /dev/null
+++ b/e2e/cypress/support/setup.ts
@@ -0,0 +1,10 @@
+import { getDataCy } from "./util";
+
+//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/support/util.ts b/e2e/cypress/support/util.ts
new file mode 100644
index 000000000..f642e5f32
--- /dev/null
+++ b/e2e/cypress/support/util.ts
@@ -0,0 +1,7 @@
+/**
+ * Get an element by its data-cy attribute
+ * @param {string} value - value of the data-cy attribute
+ */
+export function getDataCy(value: string): Cypress.Chainable {
+ return cy.get(`[data-cy="${value}"]`);
+}
diff --git a/e2e/cypress/tests/codebase.spec.ts b/e2e/cypress/tests/codebase.spec.ts
new file mode 100644
index 000000000..fa418d0b1
--- /dev/null
+++ b/e2e/cypress/tests/codebase.spec.ts
@@ -0,0 +1,107 @@
+import { loginBeforeEach } from "../support/setup";
+import { getDataCy } from "../support/util";
+import "cypress-file-upload";
+
+//login
+describe("Login", () => {
+ it("should log into comses homepage with test user", function () {
+ cy.fixture("data.json").then(data => {
+ const user = data.users[0];
+ loginBeforeEach(user.username, user.password);
+ });
+ });
+});
+
+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");
+ getDataCy("codebase-search-result").first().find("a").first().click();
+ getDataCy("release-version").click();
+ getDataCy("industry").find("select").select("College/University");
+ cy.get('[data-cy="affiliation"] input').first().type("Arizona State University {enter}", {
+ force: true,
+ });
+ cy.get('[data-cy="reason"] select').select("Research", { force: true });
+ getDataCy("submit-download").click();
+ cy.wait(1000);
+ });
+
+ it("should be able to upload a codebase", function () {
+ cy.fixture("data.json").then(data => {
+ const codebase = data.codebases[0];
+ const user = data.users[0];
+
+ loginBeforeEach(user.username, user.password);
+ cy.visit("/codebases");
+ assert(cy.get("h1").contains("Computational Model Library"));
+ cy.contains("Publish a model").click();
+ getDataCy("codebase-title").type(codebase.title);
+ getDataCy("codebase-description").type(codebase.description);
+ getDataCy("codebase-replication-text").type(codebase["replication-text"]);
+ getDataCy("codebase-associated-publications").type(codebase["associated-publications"]);
+ getDataCy("codebase-references").type(codebase.references);
+ getDataCy("next").click();
+
+ //add images
+ getDataCy("add-image").click();
+ getDataCy("upload-image")
+ .first()
+ .selectFile("cypress/fixtures/codebase/codebasetestimage.png", { force: true });
+ cy.wait(1000);
+ cy.get("body").click(0, 0);
+ cy.get("body").click(0, 0);
+
+ //upload files
+ getDataCy("upload-code")
+ .first()
+ .selectFile("cypress/fixtures/codebase/testCodebase.zip", { force: true });
+ getDataCy("upload-docs")
+ .first()
+ .selectFile("cypress/fixtures/codebase/testNarrativeDocumentation.txt", { force: true });
+ getDataCy("upload-data")
+ .first()
+ .selectFile("cypress/fixtures/codebase/testUploadData.txt", { force: true });
+ getDataCy("upload-results")
+ .first()
+ .selectFile("cypress/fixtures/codebase/testSimulationOutput.txt", { force: true });
+
+ getDataCy("add-metadata").click();
+ getDataCy("release-notes").type("Release notes");
+ getDataCy("embargo-end-date").click();
+ getDataCy("embargo-end-date").contains("29").click();
+ getDataCy("operating-system").find("select").select("Operating System Independent");
+ getDataCy("software-frameworks").type("NetLogo {enter}");
+ cy.get("body").click(0, 0);
+ getDataCy("programming-languages").type("Net Logo {enter}");
+ cy.get("body").click(0, 0);
+ getDataCy("license").click();
+ getDataCy("license").within(() => {
+ cy.contains("GPL-2.0").click();
+ });
+ getDataCy("save-and-continue").click();
+ cy.contains("button", "Publish").click();
+ getDataCy("publish").click();
+ cy.wait(2000);
+ });
+ });
+
+ it("should verify that the codebase was uploaded correctly", function () {
+ cy.fixture("data.json").then(data => {
+ const codebase = data.codebases[0];
+ cy.visit("/codebases");
+ getDataCy("codebase-search-result").first().find("a").first().click();
+ cy.contains(codebase.title).should("exist");
+ cy.contains(codebase.description).should("exist");
+ cy.contains(codebase["replication-text"]).should("exist");
+ cy.contains(codebase["associated-publications"]).should("exist");
+ cy.contains(codebase.references).should("exist");
+ });
+ });
+});
diff --git a/e2e/cypress/tests/event.spec.ts b/e2e/cypress/tests/event.spec.ts
new file mode 100644
index 000000000..42001e751
--- /dev/null
+++ b/e2e/cypress/tests/event.spec.ts
@@ -0,0 +1,65 @@
+import { loginBeforeEach } from "../support/setup";
+import { getDataCy } from "../support/util";
+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"));
+ getDataCy("event-result").first().find("a").first().click();
+ assert(
+ cy.get("h1").contains("25th International Congress on Environmental Modelling and Software")
+ );
+ });
+
+ it("should be able to search for a specific event", () => {
+ cy.visit("/events");
+ getDataCy("search-bar").type(
+ "Call for applications to organize a 2022 CECAM-Lorentz funded workshop on modeling"
+ );
+ getDataCy("search-button").click();
+ });
+
+ it("should be able to submit an event", function () {
+ cy.fixture("data.json").then(data => {
+ const user = data.users[0];
+ const event = data.events[0];
+ loginBeforeEach(user.username, user.password);
+ cy.visit("/events");
+ cy.contains("Submit an event").click();
+ getDataCy("event-title").type(event.title);
+ getDataCy("event-location").type(event.location);
+ getDataCy("event-start-date").first().click();
+ getDataCy("event-start-date").contains(event["start-date"]).click();
+ getDataCy("event-end-date").first().click();
+ getDataCy("event-end-date").contains(event["end-date"]).click();
+ getDataCy("early-registration-deadline").first().click();
+ getDataCy("early-registration-deadline")
+ .contains(event["early-registration-deadline"])
+ .click();
+ getDataCy("registration-deadline").first().click();
+ getDataCy("registration-deadline").contains(event["registration-deadline"]).click();
+ getDataCy("submission-deadline").first().click();
+ getDataCy("submission-deadline").contains(event["submission-deadline"]).click();
+ getDataCy("description").type(event.description);
+ getDataCy("summary").type(event.summary);
+ getDataCy("external-url").type(event["external-url"]);
+ getDataCy("create-button").click();
+ cy.wait(2000);
+ });
+ });
+
+ it("should be able to verify event was submitted correctly", function () {
+ cy.fixture("data.json").then(data => {
+ const event = data.events[0];
+ cy.visit("/events");
+ assert(cy.get("h1").contains("Community Events"));
+ getDataCy("event-result").first().find("a").first().click();
+ assert(cy.get("h1").contains(event.title));
+ assert(cy.get("p").contains(event.description));
+ assert(cy.get("p").contains(event.location));
+ });
+ });
+});
diff --git a/e2e/cypress/tests/job.spec.ts b/e2e/cypress/tests/job.spec.ts
new file mode 100644
index 000000000..b10766ade
--- /dev/null
+++ b/e2e/cypress/tests/job.spec.ts
@@ -0,0 +1,41 @@
+import { loginBeforeEach } from "../support/setup";
+import { getDataCy } from "../support/util";
+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", function () {
+ cy.fixture("data.json").then(data => {
+ const user = data.users[0];
+ const job = data.jobs[0];
+ loginBeforeEach(user.username, user.password);
+ cy.visit("/jobs");
+ cy.contains("Post a job").click();
+ getDataCy("job-title").type(job.title);
+ getDataCy("job-description").type(job.description);
+ getDataCy("job-summary").type(job.summary);
+ getDataCy("external-url").type(job["external-url"]);
+ getDataCy("application-deadline").first().click();
+ getDataCy("application-deadline").contains(job["application-deadline"]).click();
+ getDataCy("create-button").click();
+ cy.wait(2000);
+ });
+ });
+
+ it("should be able to verify job was submitted correctly", function () {
+ cy.fixture("data.json").then(data => {
+ const job = data.jobs[0];
+ cy.visit("/jobs");
+ assert(cy.get("h1").contains("Jobs & Appointments"));
+ getDataCy("job-result").first().find("a").first().click();
+ assert(cy.get("h1").contains(job.title));
+ assert(cy.get("p").contains(job.description));
+ });
+ });
+});
diff --git a/e2e/cypress/tests/user.spec.ts b/e2e/cypress/tests/user.spec.ts
new file mode 100644
index 000000000..8aae471bb
--- /dev/null
+++ b/e2e/cypress/tests/user.spec.ts
@@ -0,0 +1,22 @@
+import { loginBeforeEach } from "../support/setup";
+import { getDataCy } from "../support/util";
+import "cypress-file-upload";
+
+//login
+describe("User tests", () => {
+ it("should visit the users page and update the profile", function () {
+ cy.fixture("data.json").then(data => {
+ const adminUser = data.users.find(user => user.username === "admin_user");
+ const newUser = data.users.find(user => user["first-name"] && user["last-name"]);
+ loginBeforeEach(adminUser.username, adminUser.password);
+ cy.visit("/users");
+ cy.contains("My profile").click();
+ //getDataCy("my-profile").click(); TODO: Find a way to use the data-cy tag instead of current method
+ getDataCy("edit-profile").click();
+ getDataCy("first-name").find("input").clear().type(newUser["first-name"]);
+ getDataCy("last-name").find("input").clear().type(newUser["last-name"]);
+ getDataCy("submit").click();
+ cy.contains(`${newUser["first-name"]} ${newUser["last-name"]}`).should("exist");
+ });
+ });
+});
diff --git a/e2e/package-lock.json b/e2e/package-lock.json
index fa61ddfdf..b9db195f8 100644
--- a/e2e/package-lock.json
+++ b/e2e/package-lock.json
@@ -9,6 +9,8 @@
"version": "0.0.1",
"devDependencies": {
"cypress": "^13.6.2",
+ "cypress-file-upload": "^5.0.8",
+ "prettier": "^2.8.8",
"typescript": "~4.8.4"
},
"engines": {
@@ -609,6 +611,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",
@@ -1528,6 +1542,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
diff --git a/e2e/package.json b/e2e/package.json
index 4709473de..22826d2d4 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -4,11 +4,14 @@
"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",
+ "style": "prettier --check cypress/",
+ "style:fix": "prettier --write cypress/"
},
"devDependencies": {
"cypress": "^13.6.2",
+ "cypress-file-upload": "^5.0.8",
+ "prettier": "^2.8.8",
"typescript": "~4.8.4"
},
"engines": {
diff --git a/frontend/src/components/CodebaseEditForm.vue b/frontend/src/components/CodebaseEditForm.vue
index e4605a0ab..583b2e6e3 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/releaseEditor/ProgressSidebar.vue b/frontend/src/components/releaseEditor/ProgressSidebar.vue
index 53aec2e54..f0b2a0428 100644
--- a/frontend/src/components/releaseEditor/ProgressSidebar.vue
+++ b/frontend/src/components/releaseEditor/ProgressSidebar.vue
@@ -27,6 +27,7 @@
>