diff --git a/.ci/E2E-tests/cleanup.sh b/.ci/E2E-tests/cleanup.sh index 49750033d2f1..59956fa455e5 100755 --- a/.ci/E2E-tests/cleanup.sh +++ b/.ci/E2E-tests/cleanup.sh @@ -12,12 +12,8 @@ docker container stop $(docker ps -a -q) || true docker container rm $(docker ps -a -q) || true docker volume rm $(docker volume ls -q) || true -docker compose -f ./docker/cypress-E2E-tests-mysql.yml down -v -docker compose -f ./docker/cypress-E2E-tests-postgres.yml down -v docker compose -f ./docker/playwright-E2E-tests-mysql.yml down -v -docker compose -f ./docker/cypress-E2E-tests-local.yml down -v docker compose -f ./docker/playwright-E2E-tests-multi-node.yml down -v -docker compose -f ./docker/cypress-E2E-tests-multi-node.yml down -v # show all running docker containers and volumes after the cleanup to detect issues echo "SHOW RUNNING Docker containers and volumes:" diff --git a/.ci/E2E-tests/execute.sh b/.ci/E2E-tests/execute.sh index 18fbd4ebfa7d..e98ca13b5c28 100755 --- a/.ci/E2E-tests/execute.sh +++ b/.ci/E2E-tests/execute.sh @@ -6,11 +6,8 @@ DB="mysql" echo "CONFIGURATION:" echo "$CONFIGURATION" -echo "Test framework:" -echo "$TEST_FRAMEWORK" -if [ "$TEST_FRAMEWORK" = "playwright" ]; then - if [ "$CONFIGURATION" = "mysql" ]; then +if [ "$CONFIGURATION" = "mysql" ]; then COMPOSE_FILE="playwright-E2E-tests-mysql.yml" elif [ "$CONFIGURATION" = "postgres" ]; then COMPOSE_FILE="playwright-E2E-tests-postgres.yml" @@ -22,21 +19,6 @@ if [ "$TEST_FRAMEWORK" = "playwright" ]; then else echo "Invalid configuration. Please choose among mysql, postgres, mysql-localci or multi-node." exit 1 - fi -else - if [ "$CONFIGURATION" = "mysql" ]; then - COMPOSE_FILE="cypress-E2E-tests-mysql.yml" - elif [ "$CONFIGURATION" = "postgres" ]; then - COMPOSE_FILE="cypress-E2E-tests-postgres.yml" - DB="postgres" - elif [ "$CONFIGURATION" = "local" ]; then - COMPOSE_FILE="cypress-E2E-tests-local.yml" - elif [ "$CONFIGURATION" = "multi-node" ]; then - COMPOSE_FILE="cypress-E2E-tests-multi-node.yml" - else - echo "Invalid configuration. Please choose among mysql, postgres, local or multi-node." - exit 1 - fi fi echo "Compose file:" @@ -51,8 +33,7 @@ export HOST_HOSTNAME=$(hostname) cd docker #just pull everything else than artemis-app as we build it later either way -if [ "$TEST_FRAMEWORK" = "playwright" ]; then - if [ "$CONFIGURATION" = "multi-node" ]; then +if [ "$CONFIGURATION" = "multi-node" ]; then echo "Building for playwright (multi-node)" docker compose -f $COMPOSE_FILE pull artemis-playwright $DB nginx docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app-node-1 artemis-app-node-2 artemis-app-node-3 @@ -62,20 +43,8 @@ if [ "$TEST_FRAMEWORK" = "playwright" ]; then docker compose -f $COMPOSE_FILE pull artemis-playwright $DB nginx docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app docker compose -f $COMPOSE_FILE up --exit-code-from artemis-playwright - fi -else - if [ "$CONFIGURATION" = "multi-node" ]; then - echo "Building for cypress (multi-node)" - docker compose -f $COMPOSE_FILE pull artemis-cypress $DB nginx - docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app-node-1 artemis-app-node-2 artemis-app-node-3 - docker compose -f $COMPOSE_FILE up --exit-code-from artemis-cypress - else - echo "Building for cypress" - docker compose -f $COMPOSE_FILE pull artemis-cypress $DB nginx - docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app - docker compose -f $COMPOSE_FILE up --exit-code-from artemis-cypress - fi fi + exitCode=$? cd .. echo "Container exit code: $exitCode" diff --git a/.github/labeler.yml b/.github/labeler.yml index 5dd34c477214..d9295deb1372 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -10,9 +10,9 @@ tests: - changed-files: - any-glob-to-any-file: src/test/**/* -cypress: +playwright: - changed-files: - - any-glob-to-any-file: src/test/cypress/**/* + - any-glob-to-any-file: src/test/playwright/**/* database: - changed-files: diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 2116605bea0a..f5f2dd4edb71 100644 --- a/.github/workflows/analysis-of-endpoint-connections.yml +++ b/.github/workflows/analysis-of-endpoint-connections.yml @@ -2,7 +2,13 @@ name: Analysis of Endpoint Connections on: workflow_dispatch: - push: + pull_request: + types: + - opened + - synchronize + paths: + - 'src/main/java/**' + - 'src/main/webapp/**' # Keep in sync with build.yml and test.yml and codeql-analysis.yml env: @@ -20,7 +26,7 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v4 with: java-version: '${{ env.java }}' @@ -59,7 +65,7 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' diff --git a/.gitignore b/.gitignore index 13cfd9a24518..8f71a8ae13d5 100644 --- a/.gitignore +++ b/.gitignore @@ -188,13 +188,6 @@ data-exports/ /docker/.docker-data/artemis-postgres-data/* !/docker/.docker-data/artemis-postgres-data/.gitkeep -###################### -# Cypress -###################### -/src/test/cypress/screenshots/ -/src/test/cypress/videos/ -/src/test/cypress/build - ###################### # Playwright ###################### diff --git a/README.md b/README.md index 092d74dcdbb0..bbfc8945e870 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Refer to [Using JHipster in production](http://www.jhipster.tech/production) for The following command can automate the deployment to a server. The example shows the deployment to the main Artemis test server (which runs a virtual machine): ```shell -./artemis-server-cli deploy username@artemistest.ase.in.tum.de -w build/libs/Artemis-7.5.1.war +./artemis-server-cli deploy username@artemistest.ase.in.tum.de -w build/libs/Artemis-7.5.2.war ``` ## Architecture diff --git a/build.gradle b/build.gradle index 8eda5b8226e5..e53437485601 100644 --- a/build.gradle +++ b/build.gradle @@ -27,12 +27,12 @@ plugins { id "com.github.ben-manes.versions" version "0.51.0" id "com.github.andygoossens.modernizer" version "${modernizer_plugin_version}" id "com.gorylenko.gradle-git-properties" version "2.4.2" - id "org.owasp.dependencycheck" version "10.0.3" + id "org.owasp.dependencycheck" version "10.0.4" id "com.adarshr.test-logger" version "4.0.0" } group = "de.tum.in.www1.artemis" -version = "7.5.1" +version = "7.5.2" description = "Interactive Learning with Individual Feedback" java { @@ -284,12 +284,12 @@ dependencies { implementation "org.apache.sshd:sshd-sftp:${sshd_version}" // https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml - implementation "net.sourceforge.plantuml:plantuml:1.2024.6" + implementation "net.sourceforge.plantuml:plantuml:1.2024.5" implementation "org.jasypt:jasypt:1.9.3" implementation "me.xdrop:fuzzywuzzy:1.4.0" implementation("org.yaml:snakeyaml") { version { - strictly "2.2" + strictly "2.3" // needed to reduce the number of vulnerabilities, also see https://mvnrepository.com/artifact/org.yaml/snakeyaml } } @@ -330,7 +330,7 @@ dependencies { implementation "tech.jhipster:jhipster-framework:${jhipster_dependencies_version}" implementation "org.springframework.boot:spring-boot-starter-cache:${spring_boot_version}" - implementation "io.micrometer:micrometer-registry-prometheus:1.12.6" + implementation "io.micrometer:micrometer-registry-prometheus:1.13.3" implementation "net.logstash.logback:logstash-logback-encoder:8.0" // Defines low-level streaming API, and includes JSON-specific implementations @@ -349,13 +349,19 @@ dependencies { implementation "com.hazelcast:hazelcast:${hazelcast_version}" implementation "com.hazelcast:hazelcast-spring:${hazelcast_version}" implementation "com.hazelcast:hazelcast-hibernate53:5.2.0" + implementation "javax.cache:cache-api:1.1.1" implementation "org.hibernate.orm:hibernate-core:${hibernate_version}" + implementation "com.zaxxer:HikariCP:5.1.0" + implementation "org.apache.commons:commons-text:1.12.0" implementation "org.apache.commons:commons-math3:3.6.1" + implementation "javax.transaction:javax.transaction-api:1.3" + implementation "org.liquibase:liquibase-core:${liquibase_version}" + implementation "org.springframework.boot:spring-boot-starter-validation:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-loader-tools:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-starter-mail:${spring_boot_version}" @@ -421,12 +427,12 @@ dependencies { } implementation "io.springfox:springfox-bean-validators:3.0.0" implementation "com.mysql:mysql-connector-j:9.0.0" - implementation "org.postgresql:postgresql:42.7.3" + implementation "org.postgresql:postgresql:42.7.4" implementation "org.zalando:problem-spring-web:0.29.1" implementation "org.zalando:jackson-datatype-problem:0.27.1" implementation "com.ibm.icu:icu4j-charset:75.1" - implementation "com.github.seancfoley:ipaddress:5.5.0" + implementation "com.github.seancfoley:ipaddress:5.5.1" implementation "org.apache.maven:maven-model:3.9.9" // NOTE: 3.0.2 is broken for splitting lecture specific PDFs implementation "org.apache.pdfbox:pdfbox:3.0.1" @@ -442,6 +448,9 @@ dependencies { // use newest version of gson to avoid security issues through outdated dependencies implementation "com.google.code.gson:gson:2.11.0" + + implementation "com.google.errorprone:error_prone_annotations:2.31.0" + annotationProcessor "org.hibernate:hibernate-jpamodelgen:${hibernate_version}" annotationProcessor("org.glassfish.jaxb:jaxb-runtime:${jaxb_runtime_version}") { exclude group: "jakarta.ws.rs", module: "jsr311-api" @@ -474,7 +483,7 @@ dependencies { testImplementation "org.awaitility:awaitility:4.2.2" testImplementation "org.apache.maven.shared:maven-invoker:3.3.0" testImplementation "org.gradle:gradle-tooling-api:8.10" - testImplementation "org.apache.maven.surefire:surefire-report-parser:3.4.0" + testImplementation "org.apache.maven.surefire:surefire-report-parser:3.5.0" testImplementation "com.opencsv:opencsv:5.9" testImplementation("io.zonky.test:embedded-database-spring-test:2.5.1") { exclude group: "org.testcontainers", module: "mariadb" @@ -486,7 +495,7 @@ dependencies { } testImplementation("net.bytebuddy:byte-buddy") { version { - strictly "1.14.19" + strictly "1.15.1" } } // cannot update due to "Syntax error in SQL statement "WITH ids_to_delete" diff --git a/docker/artemis-migration-check-postgres.yml b/docker/artemis-migration-check-postgres.yml index 5f7cf661c39f..1ac60ab3ef45 100644 --- a/docker/artemis-migration-check-postgres.yml +++ b/docker/artemis-migration-check-postgres.yml @@ -9,8 +9,8 @@ services: service: artemis-app env_file: - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-postgres.env - ./artemis/config/migration-check.env depends_on: postgresql: diff --git a/docker/artemis/config/cypress-local.env b/docker/artemis/config/playwright-local.env similarity index 91% rename from docker/artemis/config/cypress-local.env rename to docker/artemis/config/playwright-local.env index 5ca5931297ad..6b7805deacca 100644 --- a/docker/artemis/config/cypress-local.env +++ b/docker/artemis/config/playwright-local.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Artemis configuration overrides for the Cypress E2E Postgres setups +# Artemis configuration overrides for the Playwright E2E Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_PROFILES_ACTIVE="artemis,scheduling,localvc,localci,buildagent,core,prod,docker" diff --git a/docker/artemis/config/cypress-postgres.env b/docker/artemis/config/playwright-postgres.env similarity index 95% rename from docker/artemis/config/cypress-postgres.env rename to docker/artemis/config/playwright-postgres.env index ae155aca2873..9cfcc9ade6a6 100644 --- a/docker/artemis/config/cypress-postgres.env +++ b/docker/artemis/config/playwright-postgres.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Artemis configuration overrides for the Cypress E2E Postgres setups +# Artemis configuration overrides for the Playwright E2E Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_PROFILES_ACTIVE="artemis,scheduling,jenkins,gitlab,core,prod,docker" diff --git a/docker/artemis/config/cypress.env b/docker/artemis/config/playwright.env similarity index 95% rename from docker/artemis/config/cypress.env rename to docker/artemis/config/playwright.env index c056a8d9004c..c57d79d581c7 100644 --- a/docker/artemis/config/cypress.env +++ b/docker/artemis/config/playwright.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Common Artemis configurations for the Cypress E2E MySQL and Postgres setups +# Common Artemis configurations for the Playwright E2E MySQL and Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_DATASOURCE_PASSWORD="" diff --git a/docker/cypress-E2E-tests-local.yml b/docker/cypress-E2E-tests-local.yml deleted file mode 100644 index cf69e47df760..000000000000 --- a/docker/cypress-E2E-tests-local.yml +++ /dev/null @@ -1,62 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup MySQL -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mysql: - extends: - file: ./mysql.yml - service: mysql - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - user: 0:0 - depends_on: - mysql: - condition: service_healthy - env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-local.env - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Local" - SORRY_CYPRESS_PROJECT_ID: "artemis-local" - CYPRESS_createUsers: "true" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:local & sleep 60 && npm run cypress:record:local & wait)" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data diff --git a/docker/cypress-E2E-tests-multi-node.yml b/docker/cypress-E2E-tests-multi-node.yml deleted file mode 100644 index 5fb326f75f60..000000000000 --- a/docker/cypress-E2E-tests-multi-node.yml +++ /dev/null @@ -1,134 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress setup for multi-node -# ---------------------------------------------------------------------------------------------------------------------- - -services: - artemis-app-node-1: - &artemis-app-base - container_name: artemis-app-node-1 - extends: - file: ./artemis.yml - service: artemis-app - image: ghcr.io/ls1intum/artemis:${ARTEMIS_DOCKER_TAG:-latest} - depends_on: - &depends-on-base - mysql: - condition: service_healthy - jhipster-registry: - condition: service_healthy - activemq-broker: - condition: service_healthy - pull_policy: always - restart: always - group_add: - - ${DOCKER_GROUP_ID:-0} - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node1.env - - ./artemis/config/cypress.env - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - artemis-app-node-2: - <<: *artemis-app-base - container_name: artemis-app-node-2 - depends_on: - <<: *depends-on-base - artemis-app-node-1: - condition: service_healthy - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node2.env - - ./artemis/config/cypress.env - - artemis-app-node-3: - <<: *artemis-app-base - container_name: artemis-app-node-3 - depends_on: - <<: *depends-on-base - artemis-app-node-1: - condition: service_healthy - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node3.env - - ./artemis/config/cypress.env - - jhipster-registry: - extends: - file: ./broker-registry.yml - service: jhipster-registry - networks: - - artemis - - activemq-broker: - extends: - file: ./broker-registry.yml - service: activemq-broker - networks: - - artemis - - mysql: - extends: - file: ./mysql.yml - service: mysql - restart: always - - nginx: - extends: - file: ./nginx.yml - service: nginx - depends_on: - artemis-app-node-1: - condition: service_started - artemis-app-node-2: - condition: service_started - artemis-app-node-3: - condition: service_started - restart: always - ports: - - '80:80' - - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary - - '54321:54321' - volumes: - - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} - target: "/certs/fullchain.pem" - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} - target: "/certs/priv_key.pem" - - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - nginx: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Local" - SORRY_CYPRESS_PROJECT_ID: "artemis-local" - CYPRESS_createUsers: "true" - command: > - sh -c ' - cd /app/artemis/src/test/cypress && - chmod 777 /root && - npm ci && - npm run cypress:setup && - (npm run cypress:record:local & sleep 60 && npm run cypress:record:local & wait) - ' - -networks: - artemis: - driver: "bridge" - name: artemis - -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data - diff --git a/docker/cypress-E2E-tests-mysql.yml b/docker/cypress-E2E-tests-mysql.yml deleted file mode 100644 index a14c4c163ce7..000000000000 --- a/docker/cypress-E2E-tests-mysql.yml +++ /dev/null @@ -1,60 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup MySQL -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mysql: - extends: - file: ./mysql.yml - service: mysql - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - depends_on: - mysql: - condition: service_healthy - env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-mysql.env - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "MySQL" - SORRY_CYPRESS_PROJECT_ID: "artemis-mysql" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:mysql & sleep 60 && npm run cypress:record:mysql & wait)" -# Old run method using plain cypress kept here as backup -# command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data diff --git a/docker/cypress-E2E-tests-postgres.yml b/docker/cypress-E2E-tests-postgres.yml deleted file mode 100644 index 41fe8edf5faa..000000000000 --- a/docker/cypress-E2E-tests-postgres.yml +++ /dev/null @@ -1,61 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup Postgres -# ---------------------------------------------------------------------------------------------------------------------- - -services: - postgres: - extends: - file: ./postgres.yml - service: postgres - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - depends_on: - postgres: - condition: service_healthy - env_file: - - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Postgres" - SORRY_CYPRESS_PROJECT_ID: "artemis-postgres" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:postgres & sleep 60 && npm run cypress:record:postgres & wait)" -# Old run method using plain cypress kept here as backup -# command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-postgres-data: - name: artemis-postgres-data - artemis-data: - name: artemis-data diff --git a/docker/cypress.yml b/docker/cypress.yml deleted file mode 100644 index c4dd1aa58bbb..000000000000 --- a/docker/cypress.yml +++ /dev/null @@ -1,41 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress base service -# ---------------------------------------------------------------------------------------------------------------------- - -services: - artemis-cypress: - # Cypress image with node and chrome browser installed (Cypress installation needs to be done separately because we require additional dependencies) - image: docker.io/cypress/browsers:node-20.6.1-chrome-116.0.5845.187-1-ff-117.0-edge-116.0.1938.76-1 - pull_policy: if_not_present - environment: - CYPRESS_baseUrl: "https://artemis-nginx" - CYPRESS_video: "${bamboo_cypress_video_enabled}" - CYPRESS_adminUsername: "${bamboo_artemis_admin_username}" - CYPRESS_adminPassword: "${bamboo_artemis_admin_password}" - CYPRESS_username: "${bamboo_cypress_username_template}" - CYPRESS_password: "${bamboo_cypress_password_template}" - CYPRESS_allowGroupCustomization: "true" - CYPRESS_studentGroupName: "artemis-e2etest-students" - CYPRESS_tutorGroupName: "artemis-e2etest-tutors" - CYPRESS_editorGroupName: "artemis-e2etest-editors" - CYPRESS_instructorGroupName: "artemis-e2etest-instructors" - CYPRESS_createUsers: "${bamboo_cypress_create_users}" - # use alternative cypress version to avoid blocking sorry cypress (see https://currents.dev/readme/integration-with-cypress/alternative-cypress-binaries) - CYPRESS_DOWNLOAD_MIRROR: "https://cy-cdn.currents.dev" - SORRY_CYPRESS_KEY: "${bamboo_sorry_cypress_record_secret}" - SORRY_CYPRESS_URL: "${bamboo_sorry_cypress_url}" - SORRY_CYPRESS_BUILD_ID: "${bamboo_buildNumber}" - SORRY_CYPRESS_BRANCH_NAME: "${bamboo_planRepository_branchName}" - SORRY_CYPRESS_RERUN_COUNT: "${bamboo_RerunBuildTriggerReason_noOfRetries}" - SORRY_CYPRESS_PROJECT_ID: "artemis-mysql" - NO_COLOR: "1" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - volumes: - - ..:/app/artemis - networks: - - artemis - -networks: - artemis: - driver: "bridge" - name: artemis diff --git a/docker/nginx.yml b/docker/nginx.yml index b7fbc47288cd..6e111a256a91 100644 --- a/docker/nginx.yml +++ b/docker/nginx.yml @@ -18,8 +18,6 @@ services: - ./nginx/dhparam.pem:/etc/nginx/dhparam.pem:ro - ./nginx/nginx_503.html:/usr/share/nginx/html/503.html:ro - ./nginx/70-artemis-setup.sh:/docker-entrypoint.d/70-artemis-setup.sh - - ../src/test/cypress/certs/artemis-nginx+4.pem:/certs/fullchain.pem:ro - - ../src/test/cypress/certs/artemis-nginx+4-key.pem:/certs/priv_key.pem:ro - ../src/test/playwright/certs/artemis-nginx+4.pem:/certs/fullchain.pem:ro - ../src/test/playwright/certs/artemis-nginx+4-key.pem:/certs/priv_key.pem:ro # ulimits adopted from the nginx_security_limits.conf in the Artemis ansible collection diff --git a/docker/nginx/artemis-nginx-cypress.conf b/docker/nginx/artemis-nginx-playwright.conf similarity index 100% rename from docker/nginx/artemis-nginx-cypress.conf rename to docker/nginx/artemis-nginx-playwright.conf diff --git a/docker/playwright-E2E-tests-multi-node.yml b/docker/playwright-E2E-tests-multi-node.yml index 576a695d37ca..bd8a0f52ff05 100644 --- a/docker/playwright-E2E-tests-multi-node.yml +++ b/docker/playwright-E2E-tests-multi-node.yml @@ -25,7 +25,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node1.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env artemis-app-node-2: <<: *artemis-app-base @@ -37,7 +37,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node2.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env artemis-app-node-3: <<: *artemis-app-base @@ -49,7 +49,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node3.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env jhipster-registry: extends: @@ -86,17 +86,17 @@ services: ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' volumes: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" diff --git a/docker/playwright-E2E-tests-mysql-localci.yml b/docker/playwright-E2E-tests-mysql-localci.yml index a5536baabbb9..aa501ae75192 100644 --- a/docker/playwright-E2E-tests-mysql-localci.yml +++ b/docker/playwright-E2E-tests-mysql-localci.yml @@ -17,8 +17,8 @@ services: mysql: condition: service_healthy env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-local.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-local.env volumes: - /var/run/docker.sock:/var/run/docker.sock @@ -31,11 +31,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/playwright-E2E-tests-mysql.yml b/docker/playwright-E2E-tests-mysql.yml index 65f9a343b515..c269ce9b206e 100644 --- a/docker/playwright-E2E-tests-mysql.yml +++ b/docker/playwright-E2E-tests-mysql.yml @@ -16,8 +16,8 @@ services: mysql: condition: service_healthy env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-mysql.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-mysql.env nginx: extends: @@ -28,11 +28,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/playwright-E2E-tests-postgres.yml b/docker/playwright-E2E-tests-postgres.yml index b2f1aa201bbf..92bde9b2cfe0 100644 --- a/docker/playwright-E2E-tests-postgres.yml +++ b/docker/playwright-E2E-tests-postgres.yml @@ -17,8 +17,8 @@ services: condition: service_healthy env_file: - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-postgres.env nginx: extends: @@ -29,11 +29,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/sorry-cypress/.htpasswd b/docker/sorry-cypress/.htpasswd deleted file mode 100644 index 5122db186396..000000000000 --- a/docker/sorry-cypress/.htpasswd +++ /dev/null @@ -1,4 +0,0 @@ -# Sample .htaccess file with credentials artemis_admin:artemis_admin -# Change for production! - -artemis_admin: $apr1$95ecxdaf$I1MlWdUoSYOfjdlPqZRGq/ diff --git a/docker/sorry-cypress/README.md b/docker/sorry-cypress/README.md deleted file mode 100644 index 4ed3fb156daf..000000000000 --- a/docker/sorry-cypress/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Sorry Cypress - -Sorry Cypress is an open-source, self-hosted alternative to the Cypress Dashboard, which allows you to manage, execute, and observe your Cypress test runs and results. - -## Usage - -The dashboard is available on https://sorry-cypress.ase.cit.tum.de, which is secured with a basic authentication. The credentials can be found on [Confluence](https://confluence.ase.in.tum.de/display/ArTEMiS/Sorry+Cypress+Dashboard). - -After login, you can see the active projects for artemis. One for the MySQL and one for the Postgres runs. Clicking on either project will reveal the last runs. Each run is named with a combination of the branch name and the run number. E.g for the 4th run of the branch `feature/add-awesomeness` the name would be `feature/add-awesomeness #4`. On the run overview page, each run shows some basic information, like the run time, if the run is currently running (red dot in front of the run name), the passed and failed tests etc. -To debug one of the recent runs, click on the single run and sorry cypress now shows a table with all the different test files that were tested. Select a failed test and you can now see a video of this run. Since cypress is a graphical testing tool, analyzing a video is ofter much more helpful then analyzing the corresponding logs. - -*Hint*: Sometimes it takes a while until the videos are available. So if no video is shown for the run, just visit the page again some minutes later. - -## Tech Stack - -Also visit the sorry cypress docs for more information: https://docs.sorry-cypress.dev/ - -### Dashboard - -As mentioned earlier the dashboard allows the developers to get an overview of the last test runs and helps them debug the runs. - -### API - -The API service is used to gather information from the CI and also provide them to the dashboard. - -### MongoDB - -This service is used as a database to store all the run information. - -### Director - -A service that accepts data from the api and saves it into the database. - -### Minio - -A AWS S3 and Google Cloud compatible dropin replacement, that is used as a storage provider, storing the videos and screenshots of the run. - -### Nginx - -Used as a reverse proxy, that allows to access the different endpoints via HTTPS. diff --git a/docker/sorry-cypress/SETUP.md b/docker/sorry-cypress/SETUP.md deleted file mode 100644 index 7d2027c5af6b..000000000000 --- a/docker/sorry-cypress/SETUP.md +++ /dev/null @@ -1,25 +0,0 @@ -# Setup Sorry Cypress - -1. Use the docker compose file (called `sorry-cypress.yml`) in this folder as base. -2. Copy the `sorry-cypress.env` file to the folder, where the compose files is placed. Now change all the values in the env file accordingly. - 1. Replace the `` and `` with applicable values (e.g random). These values are needed to manage the minio instance. - 2. Set a random key for the `ALLOWED_KEYS` (by replacing ``). This key is needed to authorize bamboo against the sorry-cypress dashboard. -3. Place all the necessary NGINX config files in their locations - 1. Place the `nginx.conf` file from this folder into this path `files/nginx/nginx.conf` - 2. Place a generated `.htpasswd` file into this path `files/nginx/.htpasswd` (e.g. use a online htpasswd generator). This file is used for the basic auth part of the main dashboard. - 3. Place/link a public SSL certificate file in this path `files/nginx/fullchain.pem` - 4. Place/link a private SSL certificate file in this path `files/nginx/privkey.pem` -4. Ensure that all the URLs within the `nginx.conf` and the `sorry-cypress.yml` file are still valid -5. Start the containers by using `docker compose up -f sorry-cypress.yml` -6. Login into the minio dashboard (https://minio.sorry-cypress.ase.cit.tum.de) -7. Create a new user called "sorry-cypress" (Minio Dashboard -> Identity -> Users) -8. Assign this user only the `writeOnly` policy -9. Generate a random access key and a random secret key (e.g by using `openssl rand -base64 24`) -10. Create and set the just generated `ACCESSKEY` and `SECRETKEY` for this user in the `sorry-cypress.env` file (select the user -> Service Accounts --> Create Access Key) -11. Insert the `ACCESSKEY` into the .env file by replacing the `` value -12. Insert the `SECRETKEY` into the .env file by replacing the `` value -13. Within the bucket settings, set the lifecycle to delete files ("Expiry") after 14 days. -14. Under `Policies` create a new policy called `sorry-cypress` with the content of the `minio-user-policy.json` file. Now assign this policy to the `sorry-cypress` user (under `Identity` -> `Users`). This will allow the `sorry-cypress` user to upload and delete files to/from the bucket. -15. Recreate the director container, since the new keys need to be applied (e.g with the following commands `docker compose pull -f sorry-cypress.yml` & `docker compose up -f sorry-cypress.yml`) -16. Access the sorry-cypress dashboard (https://sorry-cypress.ase.cit.tum.de) and create two new projects `artemis-mysql` and `artemis-postgresql`. Set the timeout to a reasonable number (e.g 150 min) -17. Now everything should be setup diff --git a/docker/sorry-cypress/minio-user-policy.json b/docker/sorry-cypress/minio-user-policy.json deleted file mode 100644 index ed036808944a..000000000000 --- a/docker/sorry-cypress/minio-user-policy.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:DeleteObject", - "s3:GetBucketLocation", - "s3:GetObject", - "s3:ListBucket", - "s3:ListMultipartUploadParts", - "s3:PutObject", - "s3:AbortMultipartUpload" - ], - "Resource": [ - "arn:aws:s3:::sorry-cypress", - "arn:aws:s3:::sorry-cypress/*" - ] - } - ] -} diff --git a/docker/sorry-cypress/nginx.conf b/docker/sorry-cypress/nginx.conf deleted file mode 100644 index 4230e2b6961f..000000000000 --- a/docker/sorry-cypress/nginx.conf +++ /dev/null @@ -1,129 +0,0 @@ -http { - server { - listen 80; - server_name ${NGINX_MAIN_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_MAIN_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - location / { - proxy_pass http://sry-cypress-dashboard:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - - auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/.htpasswd; - } - - location /api { - proxy_pass http://sry-cypress-api:4000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - - auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/.htpasswd; - } - - location /director { - rewrite ^/director(/?.*)$ $1 break; - proxy_pass http://sry-cypress-director:1234; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } - - server { - listen 80; - server_name ${NGINX_STORAGE_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_STORAGE_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - # This is needed to allow video file uploads, that are greater than 1MB - client_max_body_size 100M; - - location / { - proxy_pass http://sry-cypress-minio:9000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } - - server { - listen 80; - server_name ${NGINX_MINIO_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_MINIO_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - # This is needed to allow video file uploads, that are greater than 1MB - client_max_body_size 100M; - - location / { - proxy_pass http://sry-cypress-minio:9090; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } -} - -events { } diff --git a/docker/sorry-cypress/sorry-cypress.env b/docker/sorry-cypress/sorry-cypress.env deleted file mode 100644 index 13dda8bd8c07..000000000000 --- a/docker/sorry-cypress/sorry-cypress.env +++ /dev/null @@ -1,22 +0,0 @@ -SORRY_CYPRESS_TAG="2.5.4" - -MONGO_INITDB_ROOT_USERNAME="sorry-cypress" -MONGO_INITDB_ROOT_PASSWORD="sorry-cypress" -MONGODB_URI="mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017" -MONGODB_DATABASE="sorry-cypress" - -DASHBOARD_URL="https://sorry-cypress.ase.cit.tum.de" -ALLOWED_KEYS="" -MINIO_ACCESS_KEY="" -MINIO_SECRET_KEY="" -MINIO_ENDPOINT="storage.sorry-cypress.ase.cit.tum.de" -MINIO_URL="https://storage.sorry-cypress.ase.cit.tum.de" - -GRAPHQL_SCHEMA_URL="https://sorry-cypress.ase.cit.tum.de/api" - -MINIO_ROOT_USER="artemis" -MINIO_ROOT_PASSWORD="" - -NGINX_MAIN_URL="sorry-cypress.ase.cit.tum.de" -NGINX_STORAGE_URL="storage.sorry-cypress.ase.cit.tum.de" -NGINX_MINIO_URL="minio.sorry-cypress.ase.cit.tum.de" diff --git a/docker/sorry-cypress/sorry-cypress.yml b/docker/sorry-cypress/sorry-cypress.yml deleted file mode 100644 index baa7ef99bc32..000000000000 --- a/docker/sorry-cypress/sorry-cypress.yml +++ /dev/null @@ -1,107 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Sorry Cypress Setup -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mongo: - image: docker.io/library/mongo:4.4 - container_name: sry-cypress-mongo - restart: always - volumes: - - ${MONGO_VOLUME_MOUNT:-./files/mongo}:/data/db - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - - director: - image: docker.io/agoldis/sorry-cypress-director:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-director - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - EXECUTION_DRIVER: '../execution/mongo/driver' - SCREENSHOTS_DRIVER: '../screenshots/minio.driver' - MINIO_PORT: '443' - MINIO_USESSL: 'true' - MINIO_BUCKET: 'sorry-cypress' - PROBE_LOGGER: 'false' - depends_on: - - mongo - - api: - image: docker.io/agoldis/sorry-cypress-api:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-api - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - APOLLO_PLAYGROUND: 'false' - depends_on: - - mongo - - dashboard: - image: docker.io/agoldis/sorry-cypress-dashboard:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-dashboard - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - GRAPHQL_CLIENT_CREDENTIALS: 'include' - PORT: 8080 - CI_URL: '' - expose: - - '8080' - depends_on: - - mongo - - api - - storage: - image: docker.io/minio/minio - container_name: sry-cypress-minio - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - volumes: - - ${MINIO_VOLUME_MOUNT:-./files/minio}:/data - command: minio server --console-address ":9090" /data - - createbuckets: - image: docker.io/minio/mc - container_name: sry-cypress-minio-bucket-creator - depends_on: - - storage - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - entrypoint: > - /bin/sh -c " - sleep 3; - /usr/bin/mc config host add myminio http://storage:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD; - /usr/bin/mc mb myminio/sorry-cypress; - /usr/bin/mc anonymous set download myminio/sorry-cypress; - exit 0; - " - - nginx: - image: docker.io/library/nginx - container_name: sry-cypress-nginx - restart: always - ports: - - 80:80 - - 443:443 - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx - volumes: - - type: bind - source: ${NGINX_PROXY_CONFIG_PATH:-./nginx.conf} - target: '/etc/nginx/templates/nginx.conf.template' - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../../src/test/cypress/certs/artemis-nginx+4.pem} - target: '/etc/certificates/fullchain.pem' - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../../src/test/cypress/certs/artemis-nginx+4-key.pem} - target: '/etc/certificates/privkey.pem' - - type: bind - source: ${NGINX_PROXY_HTPASSWD:-./.htpasswd} - target: '/etc/nginx/.htpasswd' diff --git a/docker/test-server-multi-node-mysql-localci.yml b/docker/test-server-multi-node-mysql-localci.yml index 5bfc08a9c1fe..8de8a926068b 100644 --- a/docker/test-server-multi-node-mysql-localci.yml +++ b/docker/test-server-multi-node-mysql-localci.yml @@ -128,10 +128,10 @@ services: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-multi-node-postgresql-localci.yml b/docker/test-server-multi-node-postgresql-localci.yml index 8e0dc02bb592..124a1936aab2 100644 --- a/docker/test-server-multi-node-postgresql-localci.yml +++ b/docker/test-server-multi-node-postgresql-localci.yml @@ -100,10 +100,10 @@ services: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-mysql-localci.yml b/docker/test-server-mysql-localci.yml index 29b1881c6bb7..91c7c353bab0 100644 --- a/docker/test-server-mysql-localci.yml +++ b/docker/test-server-mysql-localci.yml @@ -47,10 +47,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-mysql.yml b/docker/test-server-mysql.yml index bcd82539e146..5f8c10981cc5 100644 --- a/docker/test-server-mysql.yml +++ b/docker/test-server-mysql.yml @@ -44,10 +44,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-postgresql-localci.yml b/docker/test-server-postgresql-localci.yml index 600cd29bf261..d93058788912 100644 --- a/docker/test-server-postgresql-localci.yml +++ b/docker/test-server-postgresql-localci.yml @@ -46,10 +46,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-postgresql.yml b/docker/test-server-postgresql.yml index d09cb52dce53..0e3c37e95627 100644 --- a/docker/test-server-postgresql.yml +++ b/docker/test-server-postgresql.yml @@ -43,10 +43,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/eslint.config.js b/eslint.config.js index 14d17e283ee0..622299e984e1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -27,7 +27,6 @@ module.exports = [ 'repos-download/', 'src/main/generated/', 'src/main/resources/', - 'src/test/cypress/', // until we delete those files, we ignore them 'target/', 'uploads/', ], @@ -37,19 +36,13 @@ module.exports = [ languageOptions: { parser: typescriptParser, parserOptions: { - project: [ - './tsconfig.json', - './tsconfig.app.json', - './tsconfig.spec.json', - 'src/test/cypress/tsconfig.json', - 'src/test/playwright/tsconfig.json', - ], + project: ['./tsconfig.json', './tsconfig.app.json', './tsconfig.spec.json', 'src/test/playwright/tsconfig.json'], }, }, plugins: { '@typescript-eslint': tsPlugin, '@angular-eslint': angularPlugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { ...prettierPlugin.configs.recommended.rules, @@ -102,7 +95,7 @@ module.exports = [ }, plugins: { '@angular-eslint': angularPlugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { // ...angularPlugin.configs['template/recommended'].rules, @@ -126,7 +119,7 @@ module.exports = [ { files: ['src/test/javascript/**'], plugins: { - 'jest': jestPlugin, + jest: jestPlugin, 'jest-extended': jestExtendedPlugin, }, rules: { diff --git a/gradle.properties b/gradle.properties index cfff6f39ed14..80dbe5279b3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,23 +6,22 @@ node_version=20.14.0 npm_version=10.7.0 # Dependency versions -jhipster_dependencies_version=8.6.0 -spring_boot_version=3.3.2 -spring_security_version=6.3.2 -# TODO: before we upgrade to 6.5.x, we need to make sure that there are no performance issues with empty sets or lists -hibernate_version=6.4.9.Final +jhipster_dependencies_version=8.7.0 +spring_boot_version=3.3.3 +spring_security_version=6.3.3 +# TODO: upgrading to 6.6.0 currently leads to issues due to internal changes in Hibernate and potentially wrong use in Artemis server code +hibernate_version=6.4.10.Final # TODO: can we update to 5.x? opensaml_version=4.3.2 jwt_version=0.12.6 jaxb_runtime_version=4.0.5 -# TODO: we cannot update to 5.5.0 because we currently use the CP Subsystem for fenced locks, however CP Subsystem is only available to Enterprise customers -hazelcast_version=5.4.0 +hazelcast_version=5.5.0 junit_version=5.10.2 -mockito_version=5.12.0 +mockito_version=5.13.0 fasterxml_version=2.17.2 jgit_version=6.10.0.202406032230-r sshd_version=2.13.2 -checkstyle_version=10.17.0 +checkstyle_version=10.18.1 jplag_version=5.1.0 slf4j_version=2.0.16 sentry_version=7.14.0 diff --git a/package-lock.json b/package-lock.json index f619a609c426..0867dc1f015b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,27 @@ { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "18.2.1", - "@angular/cdk": "18.2.1", - "@angular/common": "18.2.1", - "@angular/compiler": "18.2.1", - "@angular/core": "18.2.1", - "@angular/forms": "18.2.1", - "@angular/localize": "18.2.1", - "@angular/material": "18.2.1", - "@angular/platform-browser": "18.2.1", - "@angular/platform-browser-dynamic": "18.2.1", - "@angular/router": "18.2.1", - "@angular/service-worker": "18.2.1", + "@angular/animations": "18.2.2", + "@angular/cdk": "18.2.2", + "@angular/common": "18.2.2", + "@angular/compiler": "18.2.2", + "@angular/core": "18.2.2", + "@angular/forms": "18.2.2", + "@angular/localize": "18.2.2", + "@angular/material": "18.2.2", + "@angular/platform-browser": "18.2.2", + "@angular/platform-browser-dynamic": "18.2.2", + "@angular/router": "18.2.2", + "@angular/service-worker": "18.2.2", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -31,10 +31,10 @@ "@fortawesome/free-regular-svg-icons": "6.6.0", "@fortawesome/free-solid-svg-icons": "6.6.0", "@ls1intum/apollon": "3.3.14", - "@ng-bootstrap/ng-bootstrap": "17.0.0", + "@ng-bootstrap/ng-bootstrap": "17.0.1", "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", - "@sentry/angular": "8.26.0", + "@sentry/angular": "8.27.0", "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", @@ -59,12 +59,12 @@ "ngx-infinite-scroll": "18.0.0", "ngx-webstorage": "18.0.0", "papaparse": "5.4.1", - "posthog-js": "1.157.2", + "posthog-js": "1.160.0", "rxjs": "7.8.1", "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.4", + "simple-statistics": "7.8.5", "smoothscroll-polyfill": "0.4.4", "sockjs-client": "1.6.1", "split.js": "1.6.5", @@ -77,33 +77,33 @@ }, "devDependencies": { "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.1", + "@angular-devkit/build-angular": "18.2.2", "@angular-eslint/builder": "18.3.0", "@angular-eslint/eslint-plugin": "18.3.0", "@angular-eslint/eslint-plugin-template": "18.3.0", "@angular-eslint/schematics": "18.3.0", "@angular-eslint/template-parser": "18.3.0", - "@angular/cli": "18.2.1", - "@angular/compiler-cli": "18.2.1", - "@angular/language-service": "18.2.1", - "@sentry/types": "8.26.0", + "@angular/cli": "18.2.2", + "@angular/compiler-cli": "18.2.2", + "@angular/language-service": "18.2.2", + "@sentry/types": "8.27.0", "@types/crypto-js": "4.2.2", "@types/d3-shape": "3.1.6", "@types/dompurify": "3.0.5", "@types/jest": "29.5.12", "@types/lodash-es": "4.17.12", - "@types/node": "22.5.0", + "@types/node": "22.5.1", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", "@types/sockjs-client": "1.5.4", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.2.0", - "@typescript-eslint/parser": "8.2.0", + "@typescript-eslint/eslint-plugin": "8.3.0", + "@typescript-eslint/parser": "8.3.0", "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", - "eslint-plugin-jest": "28.8.0", + "eslint-plugin-jest": "28.8.1", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", "folder-hash": "4.0.4", @@ -116,7 +116,7 @@ "jest-junit": "16.0.0", "jest-preset-angular": "14.2.2", "lint-staged": "15.2.9", - "ng-mocks": "14.13.0", + "ng-mocks": "14.13.1", "prettier": "3.3.3", "sass": "1.77.8", "ts-jest": "29.2.5", @@ -209,13 +209,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.1.tgz", - "integrity": "sha512-XTnJfCBMDQl3xF4w/eNrq821gbj2Ig1cqbzpRflhz4pqrANTAfHfPoIC7piWEZ60FNlHapzb6fvh6tJUGXG9og==", + "version": "0.1802.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.2.tgz", + "integrity": "sha512-LPRl9jhcf0NgshaL6RoUy1uL/cAyNt7oxctoZ9EHUu8eh5E9W/jZGhVowjOLpirwqYhmEzKJJIeS49Ssqs3RQg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.1", + "@angular-devkit/core": "18.2.2", "rxjs": "7.8.1" }, "engines": { @@ -225,17 +225,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.1.tgz", - "integrity": "sha512-ANsTWKjIlEvJ6s276TbwnDhkoHhQDfsNiRFUDRGBZu94UNR78ImQZSyKYGHJOeQQH6jpBtraA1rvW5WKozAtlw==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.2.tgz", + "integrity": "sha512-7HEnTN2T1jnjuItXKcApOsoYGgfou4+POju3ZbwIQukDZ3B2COskvQkVTxqPNrQ0ZjT2mxZYoVlmGW9M+7N25g==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.1", - "@angular-devkit/build-webpack": "0.1802.1", - "@angular-devkit/core": "18.2.1", - "@angular/build": "18.2.1", + "@angular-devkit/architect": "0.1802.2", + "@angular-devkit/build-webpack": "0.1802.2", + "@angular-devkit/core": "18.2.2", + "@angular/build": "18.2.2", "@babel/core": "7.25.2", "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", @@ -246,7 +246,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "7.25.0", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.1", + "@ngtools/webpack": "18.2.2", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -288,7 +288,7 @@ "tslib": "2.6.3", "vite": "5.4.0", "watchpack": "2.4.1", - "webpack": "5.93.0", + "webpack": "5.94.0", "webpack-dev-middleware": "7.3.0", "webpack-dev-server": "5.0.4", "webpack-merge": "6.0.1", @@ -379,13 +379,13 @@ "license": "0BSD" }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.1.tgz", - "integrity": "sha512-xOP9Hxkj/mWYdMTa/8uNxFTv7z+3UiGdt4VAO7vetV5qkU/S9rRq8FEKviCc2llXfwkhInSgeeHpWKdATa+YIQ==", + "version": "0.1802.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.2.tgz", + "integrity": "sha512-Pj+YmKh0nJOKl6QAsqYh3SqfuVJrFqjyp5WrG9BgfsMD9GCMD+5teMHNYJlp+vG/C8e7VdZp4rqOon8K9Xn4Mw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/architect": "0.1802.2", "rxjs": "7.8.1" }, "engines": { @@ -399,9 +399,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.1.tgz", - "integrity": "sha512-fSuGj6CxiTFR+yjuVcaWqaVb5Wts39CSBYRO1BlsOlbuWFZ2NKC/BAb5bdxpB31heCBJi7e3XbPvcMMJIcnKlA==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.2.tgz", + "integrity": "sha512-Zz0tGptI/QQnUBDdp+1G5wGwQWMjpfe2oO+UohkrDVgFS71yVj4VDnOy51kMTxBvzw+36evTgthPpmzqPIfxBw==", "dev": true, "license": "MIT", "dependencies": { @@ -427,13 +427,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.1.tgz", - "integrity": "sha512-2t/q0Jcv7yqhAzEdNgsxoGSCmPgD4qfnVOJ7EJw3LNIA+kX1CmtN4FESUS0i49kN4AyNJFAI5O2pV8iJiliKaw==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.2.tgz", + "integrity": "sha512-PU6+3nX+gQ3gofR7BGwXuvNUNeeV2raURaZjlPfGpBqjyTBxukMV71QsTTWptAZT4WibCWkTFp6X1gvsOGbjMg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.1", + "@angular-devkit/core": "18.2.2", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -546,9 +546,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.1.tgz", - "integrity": "sha512-jit452yuE6DMVV09E6RAjgapgw64mMVH31ccpPvMDekzPsTuP3KNKtgRFU/k2DFhYJvyczM1AqqlgccE/JGaRw==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.2.tgz", + "integrity": "sha512-jh/dGrY77HGm54HdTiQsxmvoRfFeJgHeWAK2+nWCPoc4b7OHcWxy/04cYffs0/27ThmABmppP7ERAyZ0f60uow==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -557,18 +557,18 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.1" + "@angular/core": "18.2.2" } }, "node_modules/@angular/build": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.1.tgz", - "integrity": "sha512-HwzjB+I31cAtjTTbbS2NbayzfcWthaKaofJlSmZIst3PN+GwLZ8DU0DRpd/xu5AXkk+DoAIWd+lzUIaqngz6ow==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.2.tgz", + "integrity": "sha512-okaDdTMXnDhvnnnih6rPQnexL6htfEAPr19bB1Ci9d31gEjVuKZCjlcw2sPZ6BUyilwC9nZlCI5vbH1Ljf6mzA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/architect": "0.1802.2", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -648,9 +648,9 @@ } }, "node_modules/@angular/cdk": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.1.tgz", - "integrity": "sha512-6y4MmpEPXze6igUHkLsBUPkxw32F8+rmW0xVXZchkSyGlFgqfh53ueXoryWb0qL4s5enkNY6AzXnKAqHfPNkVQ==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.2.tgz", + "integrity": "sha512-+u7ZcMA24WO03vDzlBJJWq+okZLFDeW9JrtHzrdiT09FDt4sdUp+7PddXaZcRHIXjJL+CaCLQ6slaqPNEufqgg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -665,18 +665,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.1.tgz", - "integrity": "sha512-SomUFDHanY4o7k3XBGf1eFt4z1h05IGJHfcbl2vxoc0lY59VN13m/pZsD2AtpqtJTzLQT02XQOUP4rmBbGoQ+Q==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.2.tgz", + "integrity": "sha512-HVVaMxnbID0q+V3KE+JqzGbPHcBUFo1RKhBZ/jxY7USZNzgtyYbRc0IYqPWNdr99UT5QefTJrjVazJo1nqQZvQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.1", - "@angular-devkit/core": "18.2.1", - "@angular-devkit/schematics": "18.2.1", + "@angular-devkit/architect": "0.1802.2", + "@angular-devkit/core": "18.2.2", + "@angular-devkit/schematics": "18.2.2", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.1", + "@schematics/angular": "18.2.2", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -699,9 +699,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.1.tgz", - "integrity": "sha512-N0ZJO1/iU9UhprplZRPvBcdRgA/i6l6Ng5gXs5ymHBJ0lxsB+mDVCmC4jISjR9gAWc426xXwLaOpuP5Gv3f/yg==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.2.tgz", + "integrity": "sha512-AQe4xnnNNch/sXRnV82C8FmhijxPATKfPGojC2qbAG2o6VkWKgt5Lbj0O8WxvSIOS5Syedv+O2kLY/JMGWHNtw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -710,14 +710,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.1", + "@angular/core": "18.2.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.1.tgz", - "integrity": "sha512-5e9ygKEcsBoV6xpaGKVrtsLxLETlrM0oB7twl4qG/xuKYqCLj8cRQMcAKSqDfTPzWMOAQc7pHdk+uFVo/8dWHA==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.2.tgz", + "integrity": "sha512-gmVNCXZiv/CIk2eKRLnH19N9VsPuE2s3Oxm0MNi003zk1cLy7D4YEm4fSrjKXtPY8MMpRXiu5f63W94hLwWEVw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -726,7 +726,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.1" + "@angular/core": "18.2.2" }, "peerDependenciesMeta": { "@angular/core": { @@ -735,9 +735,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.1.tgz", - "integrity": "sha512-D+Qba0r6RfHfffzrebGYp54h05AxpkagLjit/GczKNgWSP1gIgZxSfi88D+GvFmeWvZxWN1ecAQ+yqft9hJqWg==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.2.tgz", + "integrity": "sha512-fF7lDrTA12YGqVjF4LyMi4hm58cv9G6CWmzSlvun0nMYCwrbRNnakZsj19dOfiIqqu4MwHaF4w3PTmUSxkMuiw==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -758,14 +758,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.1", + "@angular/compiler": "18.2.2", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.1.tgz", - "integrity": "sha512-9KrSpJ65UlJZNXrE18NszcfOwb5LZgG+LYi5Doe7amt218R1bzb3trvuAm0ZzMaoKh4ugtUCkzEOd4FALPEX6w==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.2.tgz", + "integrity": "sha512-Rx6XajL0Ydj9hXUSPDvL2Q/kMzWtbiE3VxZFJnkE+fLQiWvr0GncB+NTb/nQ6QlPQ0ly60DvuI3KLcGDuFtGVA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -779,9 +779,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.1.tgz", - "integrity": "sha512-T7z8KUuj2PoPxrMrAruQVJha+x4a9Y6IrKYtArgOQQlTwCEJuqpVYuOk5l3fwWpHE9bVEjvgkAMI1D5YXA/U6w==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.2.tgz", + "integrity": "sha512-K8cv0w6o7+ocQfUrdSA3XaKrYfa1+2TlmtyxPHjEd2mCu2R+Yqo5RqJ3P8keFewJ1+bSLhz6xnn6mumwl0RnUQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -790,16 +790,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.1", - "@angular/core": "18.2.1", - "@angular/platform-browser": "18.2.1", + "@angular/common": "18.2.2", + "@angular/core": "18.2.2", + "@angular/platform-browser": "18.2.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.1.tgz", - "integrity": "sha512-JI4oox9ELNdDVg0uJqCwgyFoK4XrowV14wSoNpGhpTLModRg3eDS6q+8cKn27cjTQRZvpReyYSTfiZMB8j4eqQ==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.2.tgz", + "integrity": "sha512-aROQNQeLf+o+F5OVvE/9BUe/Tpv8pjzmrZlogBbic5cb4IqSNhR4RjxbgIyXBO/6bhLCZwqfmMqRbW2J2xqMkg==", "dev": true, "license": "MIT", "engines": { @@ -807,9 +807,9 @@ } }, "node_modules/@angular/localize": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.1.tgz", - "integrity": "sha512-nNdB6ehXCSBpQ75sTh6Gcwy2rgExfZEkGcPARJLpjqQlHO+Mk3b1y3ka6XT9M2qQYUeyukncTFUMEZWwHICsOA==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.2.tgz", + "integrity": "sha512-grWQ3CVbizOWCthGpyIlNNnZCpF/xpWYa6tIsPzKOXLCyqFQ7vOEtSludNN1nsUmMlZQt76+wA17Fx0qcNx0EA==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -826,21 +826,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.1", - "@angular/compiler-cli": "18.2.1" + "@angular/compiler": "18.2.2", + "@angular/compiler-cli": "18.2.2" } }, "node_modules/@angular/material": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.1.tgz", - "integrity": "sha512-DBSJGqLttT9vYpLGWTuuRoOKd1mNelS0jnNo7jNZyMpjcGfuhNzmPtYiBkXfNsAl7YoXoUmX8+4uh1JZspQGqA==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.2.tgz", + "integrity": "sha512-c+EQo1GEvM2w3qasgV/BGxB0bpJeSGs2WcMVTXCYVMcqEk8nwpALwfZiCAYl8JoKoiC5k993zz19xP2Eu14qkQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.1", + "@angular/cdk": "18.2.2", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -849,9 +849,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.1.tgz", - "integrity": "sha512-hQABX7QotGmCIR3EhCBCDh5ZTvQao+JkuK5CCw2G1PkRfJMBwEpjNqnyhz41hZhWiGlucp9jgbeypppW+mIQEw==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.2.tgz", + "integrity": "sha512-Bfvl8elCFxyJ9vlwamr4X5sVMcp/tSwBal2coyl0WR+/PH2PAAtf+/WMYxIN90yZmPiJx6RZWUSJRlHOFiFp3A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -860,9 +860,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.1", - "@angular/common": "18.2.1", - "@angular/core": "18.2.1" + "@angular/animations": "18.2.2", + "@angular/common": "18.2.2", + "@angular/core": "18.2.2" }, "peerDependenciesMeta": { "@angular/animations": { @@ -871,9 +871,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.1.tgz", - "integrity": "sha512-tYJHtshbaKrtnRA15k3vrveSVBqkVUGhINvGugFA2vMtdTOfhfPw+hhzYrcwJibgU49rHogCfI9mkIbpNRYntA==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.2.tgz", + "integrity": "sha512-UM/+1nY4iIj1v4lxAmV3XRHPAh/4qfNKScCLq8tJGot64rPCbtCl0Rl8rFFGqxAFvTErVDaJycUgWNZSfVl/hw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -882,16 +882,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.1", - "@angular/compiler": "18.2.1", - "@angular/core": "18.2.1", - "@angular/platform-browser": "18.2.1" + "@angular/common": "18.2.2", + "@angular/compiler": "18.2.2", + "@angular/core": "18.2.2", + "@angular/platform-browser": "18.2.2" } }, "node_modules/@angular/router": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.1.tgz", - "integrity": "sha512-gVyqW6fYnG7oq1DlZSXJMQ2Py2dJQB7g6XVtRcYB1gR4aeowx5N9ws7PjqAi0ih91ASq2MmP4OlSSWLq+eaMGg==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.2.tgz", + "integrity": "sha512-tBHwuNtZNjzYAoVdveTI1ke/ZnQjKhc7gqDk9HCH2JUpdQhGbTvCKwDM51ktJpPMPcZlA263lQyy7VIyvdtK0A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -900,16 +900,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.1", - "@angular/core": "18.2.1", - "@angular/platform-browser": "18.2.1", + "@angular/common": "18.2.2", + "@angular/core": "18.2.2", + "@angular/platform-browser": "18.2.2", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.1.tgz", - "integrity": "sha512-Is4arGy+4HjyvALmR/GsWI4SwXYVJ1IkauAgxPsQKvWLNHdX7a/CEgEEVQGXq96H46QX9O2OcW69PnPatmJIXg==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.2.tgz", + "integrity": "sha512-az0v0gNkAjOQ4DThDWfNJv2DkH63B4Vj/WnXd8pbY/C7Be6w3S1mN2y9vJClWAzUH/GSLQHnOrZJfnZtTc8M0w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -921,8 +921,8 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.1", - "@angular/core": "18.2.1" + "@angular/common": "18.2.2", + "@angular/core": "18.2.2" } }, "node_modules/@babel/code-frame": { @@ -1283,13 +1283,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "license": "MIT", "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -1311,12 +1311,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -1504,13 +1504,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz", + "integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2721,16 +2721,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2739,12 +2739,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.25.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", - "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2754,9 +2754,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -4094,6 +4094,16 @@ "node": ">=8" } }, + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/console/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4218,6 +4228,16 @@ "node": ">=8" } }, + "node_modules/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/core/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4415,6 +4435,16 @@ "node": ">=8" } }, + "node_modules/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/reporters/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4488,6 +4518,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/test-sequencer/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/transform": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", @@ -4585,6 +4625,16 @@ "node": ">=8" } }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/transform/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5045,9 +5095,9 @@ ] }, "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-17.0.0.tgz", - "integrity": "sha512-hTbBtozJlpevF1RO6J2adCoXiAkMTPV3wmXIyK05dVha4VsKjHibgaL6YldToKoh6ElQnIYkPEIJHX9z5EtyMw==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-17.0.1.tgz", + "integrity": "sha512-utbm8OXIoqVVYGVzQkOS773ymbjc+UMkXv8lyi7hTqLhCQs0rZ0yA74peqVZRuOGXLHgcSTA7fnJhA80iQOblw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -5062,9 +5112,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.1.tgz", - "integrity": "sha512-v86U3jOoy5R9ZWe9Q0LbHRx/IBw1lbn0ldBU+gIIepREyVvb9CcH/vAyIb2Fw1zaYvvfG1OyzdrHyW8iGXjdnQ==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.2.tgz", + "integrity": "sha512-YhADmc+lVjLt3kze07A+yLry2yzcghdclu+7D3EDfa6fG2Pk33HK3MY2I0Z0BO+Ivoq7cV7yxm+naR+Od0Y5ng==", "dev": true, "license": "MIT", "engines": { @@ -5709,14 +5759,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.1.tgz", - "integrity": "sha512-bBV7I+MCbdQmBPUFF4ECg37VReM0+AdQsxgwkjBBSYExmkErkDoDgKquwL/tH7stDCc5IfTd0g9BMeosRgDMug==", + "version": "18.2.2", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.2.tgz", + "integrity": "sha512-0uPA1kQ38RnbNrzMlveX/QAqQIDu2INl5IYd3EUbJZRfYSp1VVyOSyuIBJ+1iUl5Y5VUa2uylaVZXhFdKWprXw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.1", - "@angular-devkit/schematics": "18.2.1", + "@angular-devkit/core": "18.2.2", + "@angular-devkit/schematics": "18.2.2", "jsonc-parser": "3.3.1" }, "engines": { @@ -5726,73 +5776,73 @@ } }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.26.0.tgz", - "integrity": "sha512-O2Tj+WK33/ZVp5STnz6ZL0OO+/Idk2KqsH0ITQkQmyZ2z0kdzWOeqK7s7q3/My6rB1GfPcyqPcBBv4dVv92FYQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.27.0.tgz", + "integrity": "sha512-YTIwQ1GM1NTRXgN4DvpFSQ2x4pjlqQ0FQAyHW5x2ZYv4z7VmqG4Xkid1P/srQUipECk6nxkebfD4WR19nLsvnQ==", "license": "MIT", "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.26.0.tgz", - "integrity": "sha512-hQtw1gg8n6ERK1UH47F7ZI1zOsbhu0J2VX+TrnkpaQR2FgxDW1oe9Ja6oCV4CQKuR4w+1ZI/Kj4imSt0K33kEw==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.27.0.tgz", + "integrity": "sha512-b71PQc9aK1X9b/SO1DiJlrnAEx4n0MzPZQ/tKd9oRWDyGit6pJWZfQns9r2rvc96kJPMOTxFAa/upXRCkA723A==", "license": "MIT", "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.26.0.tgz", - "integrity": "sha512-JDY7W2bswlp5c3483lKP4kcb75fHNwGNfwD8x8FsY9xMjv7nxeXjLpR5cCEk1XqPq2+n6w4j7mJOXhEXGiUIKg==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.27.0.tgz", + "integrity": "sha512-Ofucncaon98dvlxte2L//hwuG9yILSxNrTz/PmO0k+HzB9q+oBic4667QF+azWR2qv4oKSWpc+vEovP3hVqveA==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.26.0.tgz", - "integrity": "sha512-2CFQW6f9aJHIo/DqmqYa9PaYoLn1o36ywc0h8oyGrD4oPCbrnE5F++PmTdc71GBODu41HBn/yoCTLmxOD+UjpA==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.27.0.tgz", + "integrity": "sha512-uuEfiWbjwugB9M4KxXxovHYiKRqg/R6U4EF8xM/Ub4laUuEcWsfRp7lQ3MxL3qYojbca8ncIFic2bIoKMPeejA==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/replay": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/angular": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.26.0.tgz", - "integrity": "sha512-9YolcJMdEzS6hbImal3jrAbzGZGM7DpmfSOfzt1Cs4bYTD9gCxKRkLyUgiNRIlrIBO7CkdpMrCSY+nEohvCw7A==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.27.0.tgz", + "integrity": "sha512-0BjjrqnVMofVbQGEwfZgYAZWFl4ewkWRjcUj+NIX4iJpRZZniKZxo6XOlo/pTkt4oVHsbNHJO0C1tS+gRZFErg==", "license": "MIT", "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "tslib": "^2.4.1" }, "engines": { @@ -5806,52 +5856,52 @@ } }, "node_modules/@sentry/browser": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.26.0.tgz", - "integrity": "sha512-e5s6eKlwLZWzTwQcBwqyAGZMMuQROW9Z677VzwkSyREWAIkKjfH2VBxHATnNGc0IVkNHjD7iH3ixo3C0rLKM3w==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.27.0.tgz", + "integrity": "sha512-eL1eaHwoYUGkp4mpeYesH6WtCrm+0u9jYCW5Lm0MAeTmpx22BZKEmj0OljuUJXGnJwFbvPDlRjyz6QG11m8kZA==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry-internal/feedback": "8.26.0", - "@sentry-internal/replay": "8.26.0", - "@sentry-internal/replay-canvas": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry-internal/feedback": "8.27.0", + "@sentry-internal/replay": "8.27.0", + "@sentry-internal/replay-canvas": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", - "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.27.0.tgz", + "integrity": "sha512-4frlXluHT3Du+Omw91K04jpvbfMtydvg4Bxj2+gt/DT19Swhm/fbEpzdUjgbAd3Jinj/n0qk/jFRXjr9JZKFjg==", "license": "MIT", "dependencies": { - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/types": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.26.0.tgz", - "integrity": "sha512-zKmh6SWsJh630rpt7a9vP4Cm4m1C2gDTUqUiH565CajCL/4cePpNWYrNwalSqsOSL7B9OrczA1+n6a6XvND+ng==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.27.0.tgz", + "integrity": "sha512-B6lrP46+m2x0lfqWc9F4VcUbN893mVGnPEd7KIMRk95mPzkFJ3sNxggTQF5/ZfNO7lDQYQb22uysB5sj/BqFiw==", "license": "MIT", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/utils": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", - "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.27.0.tgz", + "integrity": "sha512-gyJM3SyLQe0A3mkQVVNdKYvk3ZoikkYgyA/D+5StFNLKdyUgEbJgXOGXrQSSYPF7BSX6Sc5b0KHCglPII0KuKw==", "license": "MIT", "dependencies": { - "@sentry/types": "8.26.0" + "@sentry/types": "8.27.0" }, "engines": { "node": ">=14.18" @@ -6271,27 +6321,18 @@ } }, "node_modules/@types/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -6454,9 +6495,9 @@ } }, "node_modules/@types/node": { - "version": "22.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", - "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", + "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", "dev": true, "license": "MIT", "dependencies": { @@ -6660,17 +6701,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", - "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz", + "integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/type-utils": "8.2.0", - "@typescript-eslint/utils": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/type-utils": "8.3.0", + "@typescript-eslint/utils": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -6694,16 +6735,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", - "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz", + "integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/typescript-estree": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", "debug": "^4.3.4" }, "engines": { @@ -6723,14 +6764,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", - "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz", + "integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0" + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6741,14 +6782,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", - "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz", + "integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.2.0", - "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/typescript-estree": "8.3.0", + "@typescript-eslint/utils": "8.3.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -6766,9 +6807,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", - "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz", + "integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==", "dev": true, "license": "MIT", "engines": { @@ -6780,16 +6821,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", - "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz", + "integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/visitor-keys": "8.2.0", + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -6809,16 +6850,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", - "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz", + "integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.2.0", - "@typescript-eslint/types": "8.2.0", - "@typescript-eslint/typescript-estree": "8.2.0" + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/typescript-estree": "8.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6832,13 +6873,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", - "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz", + "integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/types": "8.3.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7497,6 +7538,16 @@ "node": ">=8" } }, + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/babel-jest/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8085,9 +8136,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "version": "1.0.30001654", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001654.tgz", + "integrity": "sha512-wLJc602fW0OdrUR+PqsBUH3dgrjDcT+mWs/Kw86zPvgjiqOiI2TXMkBFK4KihYzZclmJxrFwgYhZDSEogFai/g==", "funding": [ { "type": "opencollective", @@ -8196,9 +8247,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", + "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", "dev": true, "license": "MIT" }, @@ -8639,53 +8690,6 @@ "node": ">=10.13.0" } }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", - "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/core-js": { "version": "3.38.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", @@ -9649,6 +9653,16 @@ "node": ">=8" } }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -9777,9 +9791,9 @@ } }, "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -9993,9 +10007,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -10208,10 +10222,41 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/eslint-plugin-deprecation/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-deprecation/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/eslint-plugin-jest": { - "version": "28.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz", - "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==", + "version": "28.8.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.1.tgz", + "integrity": "sha512-G46XMyYu6PtSNJUkQ0hsPjzXYpzq/O4vpCciMizTKRJG8kNsRreGoMRDG6H9FIB/xVgfFuclVnuX4XRvFUzrZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10379,6 +10424,37 @@ "node": ">=4.0" } }, + "node_modules/eslint-plugin-jest-extended/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jest-extended/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -11448,21 +11524,21 @@ } }, "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -12812,6 +12888,16 @@ "node": ">=8" } }, + "node_modules/jest-circus/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13044,6 +13130,16 @@ "node": ">=8" } }, + "node_modules/jest-config/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-config/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13589,6 +13685,16 @@ "node": ">=8" } }, + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13772,6 +13878,16 @@ "node": ">=8" } }, + "node_modules/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-resolve/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14012,6 +14128,16 @@ "node": ">=8" } }, + "node_modules/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-runtime/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -16123,9 +16249,9 @@ "license": "MIT" }, "node_modules/ng-mocks": { - "version": "14.13.0", - "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.13.0.tgz", - "integrity": "sha512-cQ6nUj/P+v7X52gYU6bAj/03iDKl2pzbPy2V0tx/d5lxME063Vxc190p6UPlJkbRIxcB+OqSALPgQvy83efzjw==", + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.13.1.tgz", + "integrity": "sha512-eyfnjXeC108SqVD09i/cBwCpKkK0JjBoAg8jp7oQS2HS081K3WJTttFpgLGeLDYKmZsZ6nYpI+HHNQ3OksaJ7A==", "dev": true, "license": "MIT", "funding": { @@ -16231,9 +16357,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "dev": true, "license": "MIT", "bin": { @@ -17073,13 +17199,16 @@ "license": "MIT" }, "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/pepjs": { @@ -17407,9 +17536,9 @@ "license": "MIT" }, "node_modules/posthog-js": { - "version": "1.157.2", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.157.2.tgz", - "integrity": "sha512-ATYKGs+Q51u26nHHhrhWNh1whqFm7j/rwQQYw+y6/YzNmRlo+YsqrGZji9nqXb9/4fo0ModDr+ZmuOI3hKkUXA==", + "version": "1.160.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.160.0.tgz", + "integrity": "sha512-K/RRgmPYIpP69nnveCJfkclb8VU+R+jsgqlrKaLGsM5CtQM9g01WOzAiT3u36WLswi58JiFMXgJtECKQuoqTgQ==", "license": "MIT", "dependencies": { "fflate": "^0.4.8", @@ -18808,12 +18937,12 @@ } }, "node_modules/simple-statistics": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.4.tgz", - "integrity": "sha512-KHC7X+4Dji2rFgnPU7FxPPp4GxPz9hvQCHx2x6JbjLYNKuSMHcoNZ54gF0xBBMOAvNtWmfCHcfC4MD2T89ffEA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.5.tgz", + "integrity": "sha512-yw4aOnkvPLbL80zamrEKznAnk5cIIkjEcx/z0aQl+m/YKMmVufrnWgWJWRspqZtwh+ElZXRhJ0MtnUjFUQV5Ow==", "license": "ISC", "engines": { - "node": ">= 18" + "node": "*" } }, "node_modules/sisteransi": { @@ -18824,13 +18953,16 @@ "license": "MIT" }, "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/slice-ansi": { @@ -21018,13 +21150,12 @@ } }, "node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -21033,7 +21164,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/package.json b/package.json index f19185aadfb5..6a576592bbf0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "description": "Interactive Learning with Individual Feedback", "private": true, "license": "MIT", @@ -13,18 +13,18 @@ "node_modules" ], "dependencies": { - "@angular/animations": "18.2.1", - "@angular/cdk": "18.2.1", - "@angular/common": "18.2.1", - "@angular/compiler": "18.2.1", - "@angular/core": "18.2.1", - "@angular/forms": "18.2.1", - "@angular/localize": "18.2.1", - "@angular/material": "18.2.1", - "@angular/platform-browser": "18.2.1", - "@angular/platform-browser-dynamic": "18.2.1", - "@angular/router": "18.2.1", - "@angular/service-worker": "18.2.1", + "@angular/animations": "18.2.2", + "@angular/cdk": "18.2.2", + "@angular/common": "18.2.2", + "@angular/compiler": "18.2.2", + "@angular/core": "18.2.2", + "@angular/forms": "18.2.2", + "@angular/localize": "18.2.2", + "@angular/material": "18.2.2", + "@angular/platform-browser": "18.2.2", + "@angular/platform-browser-dynamic": "18.2.2", + "@angular/router": "18.2.2", + "@angular/service-worker": "18.2.2", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -34,10 +34,10 @@ "@fortawesome/free-regular-svg-icons": "6.6.0", "@fortawesome/free-solid-svg-icons": "6.6.0", "@ls1intum/apollon": "3.3.14", - "@ng-bootstrap/ng-bootstrap": "17.0.0", + "@ng-bootstrap/ng-bootstrap": "17.0.1", "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", - "@sentry/angular": "8.26.0", + "@sentry/angular": "8.27.0", "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", @@ -62,12 +62,12 @@ "ngx-infinite-scroll": "18.0.0", "ngx-webstorage": "18.0.0", "papaparse": "5.4.1", - "posthog-js": "1.157.2", + "posthog-js": "1.160.0", "rxjs": "7.8.1", "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.4", + "simple-statistics": "7.8.5", "smoothscroll-polyfill": "0.4.4", "sockjs-client": "1.6.1", "split.js": "1.6.5", @@ -113,33 +113,33 @@ }, "devDependencies": { "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.1", + "@angular-devkit/build-angular": "18.2.2", "@angular-eslint/builder": "18.3.0", "@angular-eslint/eslint-plugin": "18.3.0", "@angular-eslint/eslint-plugin-template": "18.3.0", "@angular-eslint/schematics": "18.3.0", "@angular-eslint/template-parser": "18.3.0", - "@angular/cli": "18.2.1", - "@angular/compiler-cli": "18.2.1", - "@angular/language-service": "18.2.1", - "@sentry/types": "8.26.0", + "@angular/cli": "18.2.2", + "@angular/compiler-cli": "18.2.2", + "@angular/language-service": "18.2.2", + "@sentry/types": "8.27.0", "@types/crypto-js": "4.2.2", "@types/d3-shape": "3.1.6", "@types/dompurify": "3.0.5", "@types/jest": "29.5.12", "@types/lodash-es": "4.17.12", - "@types/node": "22.5.0", + "@types/node": "22.5.1", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", "@types/sockjs-client": "1.5.4", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.2.0", - "@typescript-eslint/parser": "8.2.0", + "@typescript-eslint/eslint-plugin": "8.3.0", + "@typescript-eslint/parser": "8.3.0", "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", - "eslint-plugin-jest": "28.8.0", + "eslint-plugin-jest": "28.8.1", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", "folder-hash": "4.0.4", @@ -152,7 +152,7 @@ "jest-junit": "16.0.0", "jest-preset-angular": "14.2.2", "lint-staged": "15.2.9", - "ng-mocks": "14.13.0", + "ng-mocks": "14.13.1", "prettier": "3.3.3", "sass": "1.77.8", "ts-jest": "29.2.5", diff --git a/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java b/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java index e64bb62fccbb..674012737006 100644 --- a/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java +++ b/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java @@ -19,11 +19,12 @@ import org.springframework.core.env.Environment; import de.tum.in.www1.artemis.config.ProgrammingLanguageConfiguration; +import de.tum.in.www1.artemis.config.TheiaConfiguration; import tech.jhipster.config.DefaultProfileUtil; import tech.jhipster.config.JHipsterConstants; @SpringBootApplication -@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class }) +@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class, TheiaConfiguration.class }) public class ArtemisApp { private static final Logger log = LoggerFactory.getLogger(ArtemisApp.class); diff --git a/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java new file mode 100644 index 000000000000..02a50a5201ed --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java @@ -0,0 +1,43 @@ +package de.tum.in.www1.artemis.config; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; + +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage; + +@Profile(PROFILE_THEIA) +@Configuration +@ConfigurationProperties(prefix = "theia") +public class TheiaConfiguration { + + private Map> images; + + public void setImages(final Map> images) { + this.images = images; + } + + /** + * Get the images for all languages + * + * @return a map of language -> [flavor/name -> image-link] + */ + public Map> getImagesForAllLanguages() { + return images; + } + + /** + * Get the images for a specific language + * + * @param language the language for which the images should be retrieved + * @return a map of flavor/name -> image-link + */ + public Map getImagesForLanguage(ProgrammingLanguage language) { + return images.get(language); + } + +} diff --git a/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java index 45bfdaf4a97b..f88d93656d1b 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java @@ -8,6 +8,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Configuration @Profile("gitlab | gitlabci") public class GitLabApiConfiguration { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java b/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java index b44ce90a2e67..ccb797194f8d 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java @@ -732,8 +732,9 @@ public String toString() { public void validateProgrammingSettings() { // Check if a participation mode was selected - if (!Boolean.TRUE.equals(isAllowOnlineEditor()) && !Boolean.TRUE.equals(isAllowOfflineIde())) { - throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor or the offline IDE", "Exercise", "noParticipationModeAllowed"); + if (!Boolean.TRUE.equals(isAllowOnlineEditor()) && !Boolean.TRUE.equals(isAllowOfflineIde()) && !isAllowOnlineIde()) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor, the offline IDE, or the online IDE", "Exercise", + "noParticipationModeAllowed"); } // Check if Xcode has no online code editor enabled @@ -745,6 +746,11 @@ public void validateProgrammingSettings() { if (getProgrammingLanguage() == null) { throw new BadRequestAlertException("No programming language was specified", "Exercise", "programmingLanguageNotSet"); } + + // Check if theia image was selected if the online IDE is enabled + if (isAllowOnlineIde() && buildConfig.getTheiaImage() == null) { + throw new BadRequestAlertException("The Theia image must be selected if the online IDE is enabled", "Exercise", "theiaImageNotSet"); + } } /** diff --git a/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java b/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java index 16800e0021c7..816d18157234 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java @@ -37,6 +37,9 @@ public class Lti13ClientRegistration { @JsonProperty("jwks_uri") private String jwksUri; + @JsonProperty("logo_uri") + private String logoUri; + @JsonProperty("token_endpoint_auth_method") private String tokenEndpointAuthMethod; @@ -67,6 +70,7 @@ public Lti13ClientRegistration(String serverUrl, String clientRegistrationId) { this.setRedirectUris(List.of(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH)); this.setInitiateLoginUri(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_INITIATION_PATH + "/" + clientRegistrationId); this.setJwksUri(serverUrl + "/.well-known/jwks.json"); + this.setLogoUri(serverUrl + "/public/images/logo.png"); Lti13ToolConfiguration toolConfiguration = getLti13ToolConfiguration(serverUrl); this.setLti13ToolConfiguration(toolConfiguration); @@ -83,6 +87,7 @@ private static Lti13ToolConfiguration getLti13ToolConfiguration(String serverUrl } toolConfiguration.setDomain(domain); toolConfiguration.setTargetLinkUri(serverUrl + "/courses"); + toolConfiguration.setDescription("Artemis: Interactive Learning with Individual Feedback"); toolConfiguration.setClaims(Arrays.asList("iss", "email", "sub", "name", "given_name", "family_name")); Message deepLinkingMessage = new Message(CustomLti13Configurer.LTI13_DEEPLINK_MESSAGE_REQUEST, serverUrl + "/" + CustomLti13Configurer.LTI13_DEEPLINK_REDIRECT_PATH); toolConfiguration.setMessages(List.of(deepLinkingMessage)); @@ -145,6 +150,14 @@ public void setJwksUri(String jwksUri) { this.jwksUri = jwksUri; } + public String getLogoUri() { + return logoUri; + } + + public void setLogoUri(String logoUri) { + this.logoUri = logoUri; + } + public String getTokenEndpointAuthMethod() { return tokenEndpointAuthMethod; } @@ -179,6 +192,8 @@ public static class Lti13ToolConfiguration { @JsonProperty("target_link_uri") private String targetLinkUri; + private String description; + private List messages; private List claims; @@ -199,6 +214,14 @@ public void setTargetLinkUri(String targetLinkUri) { this.targetLinkUri = targetLinkUri; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public List getMessages() { return messages; } diff --git a/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java b/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java index 44f21ed0bf25..77279e2a6593 100644 --- a/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java +++ b/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java @@ -1,5 +1,8 @@ package de.tum.in.www1.artemis.exception; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabCIException extends ContinuousIntegrationException { public GitLabCIException(String message) { diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java index 470e789cdb27..1187d8c0e11d 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java @@ -222,8 +222,6 @@ SELECT COUNT(c) > 0 List findAllByShortName(String shortName); - Optional findById(long courseId); - /** * Returns the title of the course with the given id. * diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java index 127e51c9efab..afac757c9ccd 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java @@ -58,7 +58,7 @@ public interface ExamRepository extends ArtemisJpaRepository { /** * Find all exams for multiple courses that are already visible to the user (either registered, at least tutor or the exam is a test exam) * - * @param courseIds set of courseIds that the exams should be retreived + * @param courseIds set of courseIds that the exams should be retrieved * @param userId the id of the user requesting the exams * @param groupNames the groups of the user requesting the exams * @param now the current date, typically ZonedDateTime.now() diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java index b86b9ea3abc3..3924394b81ee 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java @@ -39,9 +39,9 @@ Set findAllExamSessionsWithTheSameIpAddressAndBrowserFingerprintByE @Param("studentExamId") Long studentExamId, @Param("ipAddress") String ipAddress, @Param("browserFingerprintHash") String browserFingerprintHash); @Query(""" - SELECT es - FROM ExamSession es - WHERE es.studentExam.exam.id = :examId + SELECT es + FROM ExamSession es + WHERE es.studentExam.exam.id = :examId """) Set findAllExamSessionsByExamId(@Param("examId") long examId); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java index e659f82db28d..04dd3b8732f2 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java @@ -765,6 +765,17 @@ default Page findAllWithGroupsByIsDeletedIsFalse(Pageable pageable) { """) void updateUserSshPublicKeyHash(@Param("userId") long userId, @Param("sshPublicKeyHash") String sshPublicKeyHash, @Param("sshPublicKey") String sshPublicKey); + @Modifying + @Transactional // ok because of modifying query + @Query(""" + UPDATE User user + SET user.vcsAccessToken = :vcsAccessToken, + user.vcsAccessTokenExpiryDate = :vcsAccessTokenExpiryDate + WHERE user.id = :userId + """) + void updateUserVcsAccessToken(@Param("userId") long userId, @Param("vcsAccessToken") String vcsAccessToken, + @Param("vcsAccessTokenExpiryDate") ZonedDateTime vcsAccessTokenExpiryDate); + @Modifying @Transactional // ok because of modifying query @Query(""" diff --git a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java index 6fb44f9e1bec..3fd9d62c03e1 100644 --- a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java +++ b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java @@ -18,8 +18,6 @@ @Service public class JWTCookieService { - private static final String CYPRESS_PROFILE = "cypress"; - private static final String DEVELOPMENT_PROFILE = "dev"; private final TokenProvider tokenProvider; @@ -61,9 +59,8 @@ public ResponseCookie buildLogoutCookie() { */ private ResponseCookie buildJWTCookie(String jwt, Duration duration) { - // TODO - Remove cypress workaround once cypress uses https and find a better solution for testing locally in Safari Collection activeProfiles = Arrays.asList(environment.getActiveProfiles()); - boolean isSecure = !activeProfiles.contains(CYPRESS_PROFILE) && !activeProfiles.contains(DEVELOPMENT_PROFILE); + boolean isSecure = !activeProfiles.contains(DEVELOPMENT_PROFILE); return ResponseCookie.from(JWT_COOKIE_NAME, jwt).httpOnly(true) // Must be httpOnly .sameSite("Lax") // Must be Lax to allow navigation links to Artemis to work diff --git a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java index 4143a0e00bbe..724a8f98e347 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java @@ -10,8 +10,6 @@ import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; -import javax.annotation.CheckReturnValue; - import org.hibernate.Hibernate; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; @@ -19,6 +17,8 @@ import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; +import com.google.errorprone.annotations.CheckReturnValue; + import de.tum.in.www1.artemis.domain.Authority; import de.tum.in.www1.artemis.domain.Course; import de.tum.in.www1.artemis.domain.Exercise; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java index 4bf736963136..1b27d35bd3dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java @@ -10,6 +10,9 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public abstract class AbstractGitLabAuthorizationInterceptor implements ClientHttpRequestInterceptor { private static final String GITLAB_AUTHORIZATION_HEADER_NAME = "Private-Token"; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java index 9a78a348d9d8..2e461a5e1106 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java @@ -3,6 +3,9 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlab") @Component public class GitLabAuthorizationInterceptor extends AbstractGitLabAuthorizationInterceptor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java index b34cd9c61dba..f217ef874422 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java @@ -2,6 +2,9 @@ import de.tum.in.www1.artemis.exception.VersionControlException; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabException extends VersionControlException { public GitLabException() { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java index e5d5fb7b3c4e..e903ffe004f5 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java @@ -31,6 +31,9 @@ /** * Provides VCS access token services for GitLab via means of personal access tokens. */ +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlab") public class GitLabPersonalAccessTokenManagementService extends VcsTokenManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java index 103a58652025..4cdddb4cf7a1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java @@ -66,6 +66,9 @@ import de.tum.in.www1.artemis.service.connectors.vcs.VersionControlRepositoryPermission; import de.tum.in.www1.artemis.service.util.UrlUtils; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlab") @Service public class GitLabService extends AbstractVersionControlService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java index 298dee6f4e1b..36f65cc51902 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java @@ -1,5 +1,8 @@ package de.tum.in.www1.artemis.service.connectors.gitlab; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabUserDoesNotExistException extends GitLabException { public GitLabUserDoesNotExistException(String login) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java index 9733c7a131c0..0f8f67198b55 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java @@ -28,6 +28,9 @@ import de.tum.in.www1.artemis.service.connectors.vcs.VcsTokenManagementService; import de.tum.in.www1.artemis.service.connectors.vcs.VcsUserManagementService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlab") public class GitLabUserManagementService implements VcsUserManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java index b24a6013dc93..951aafa36d8d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java @@ -11,6 +11,9 @@ import de.tum.in.www1.artemis.config.Constants; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Component @Profile("gitlab") public class GitlabInfoContributor implements InfoContributor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java index 6942e2da6343..d937d570bb45 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabCommitDTO(@JsonProperty("id") String hash, String message, ZonedDateTime timestamp, @JsonProperty("url") String commitUrl, Author author, List added, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java index 2662aef4d7d7..0ceec1bc02dc 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java @@ -9,6 +9,9 @@ * * @param id The id of the personal access token. */ +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenListResponseDTO(@JsonProperty Long id) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java index 263b15057000..154830c818b3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenRequestDTO(String name, @JsonProperty("user_id") Long userId, String[] scopes, @JsonProperty("expires_at") Date expiresAt) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java index e8f4333975f7..6920d3db2bee 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenResponseDTO(String name, @JsonProperty("user_id") Long userId, String[] scopes, @JsonProperty("expires_at") Date expiresAt, String token) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java index 605679d176bf..71353195ac21 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabProjectDTO(int id, String name, String description, @JsonProperty("web_url") URL webUrl, @JsonProperty("git_ssh_url") String sshUrl, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java index 23858285deda..917628db09e8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java @@ -8,6 +8,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPushNotificationDTO(@JsonProperty("object_kind") String triggerType, @JsonProperty("event_name") String eventName, @JsonProperty("before") String previousHash, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java index f4e84034af8c..245eefed4498 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java @@ -5,6 +5,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabRepositoryDTO(String name, String url, String description, URL homepage) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java index 79de2d27aa42..258aa2df4269 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java @@ -5,6 +5,9 @@ import de.tum.in.www1.artemis.service.connectors.gitlab.AbstractGitLabAuthorizationInterceptor; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Component public class GitLabCIAuthorizationInterceptor extends AbstractGitLabAuthorizationInterceptor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java index 80e9dab30be0..23500df008f1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java @@ -19,6 +19,9 @@ import de.tum.in.www1.artemis.service.ResourceLoaderService; import de.tum.in.www1.artemis.service.connectors.ci.AbstractBuildPlanCreator; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIBuildPlanService extends AbstractBuildPlanCreator { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java index 832d100a7585..ee088f9d901a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java @@ -10,6 +10,9 @@ import de.tum.in.www1.artemis.config.Constants; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Component @Profile("gitlabci") public class GitLabCIInfoContributor implements InfoContributor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java index 52b10ee2c9be..661824ea06d8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java @@ -14,6 +14,9 @@ import de.tum.in.www1.artemis.service.programming.ProgrammingLanguageFeature; import de.tum.in.www1.artemis.service.programming.ProgrammingLanguageFeatureService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIProgrammingLanguageFeatureService extends ProgrammingLanguageFeatureService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java index 620e22e09a55..f71e0fe8253b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java @@ -25,6 +25,9 @@ import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; import de.tum.in.www1.artemis.service.programming.ProgrammingExerciseFeedbackCreationService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCIResultService extends AbstractContinuousIntegrationResultService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java index 2c78eb608c4e..45d66cf19272 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java @@ -42,6 +42,9 @@ import de.tum.in.www1.artemis.service.connectors.ci.notification.dto.TestResultsDTO; import de.tum.in.www1.artemis.web.rest.dto.CheckoutDirectoriesDTO; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCIService extends AbstractContinuousIntegrationService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java index 72c4fcb6150b..d2fe1c62525f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java @@ -15,6 +15,9 @@ import de.tum.in.www1.artemis.service.UriService; import de.tum.in.www1.artemis.service.connectors.ci.ContinuousIntegrationTriggerService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCITriggerService implements ContinuousIntegrationTriggerService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java index f70dd7a82ffa..e15e0dd7038f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java @@ -12,6 +12,9 @@ import de.tum.in.www1.artemis.exception.ContinuousIntegrationException; import de.tum.in.www1.artemis.service.connectors.ci.CIUserManagementService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIUserManagementService implements CIUserManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java index fd7e0cc0dc89..dfc7dcf776c2 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java @@ -20,7 +20,6 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import de.tum.in.www1.artemis.domain.BuildJob; @@ -73,8 +72,6 @@ public class LocalCIResultProcessingService { private IMap buildAgentInformation; - private FencedLock resultQueueLock; - private UUID listenerId; public LocalCIResultProcessingService(@Qualifier("hazelcastInstance") HazelcastInstance hazelcastInstance, ProgrammingExerciseGradingService programmingExerciseGradingService, @@ -97,7 +94,6 @@ public LocalCIResultProcessingService(@Qualifier("hazelcastInstance") HazelcastI public void init() { this.resultQueue = this.hazelcastInstance.getQueue("buildResultQueue"); this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); - this.resultQueueLock = this.hazelcastInstance.getCPSubsystem().getLock("resultQueueLock"); this.listenerId = resultQueue.addItemListener(new ResultQueueListener(), true); } @@ -112,9 +108,7 @@ public void removeListener() { public void processResult() { // set lock to prevent multiple nodes from processing the same build job - resultQueueLock.lock(); ResultQueueItem resultQueueItem = resultQueue.poll(); - resultQueueLock.unlock(); if (resultQueueItem == null) { return; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java index bdaea1f46fd5..939fa41cee19 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java @@ -27,7 +27,6 @@ import com.hazelcast.collection.IQueue; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import com.hazelcast.topic.ITopic; @@ -66,11 +65,6 @@ public class SharedQueueManagementService { private IMap dockerImageCleanupInfo; - /** - * Lock to prevent multiple nodes from processing the same build job. - */ - private FencedLock sharedLock; - private ITopic canceledBuildJobsTopic; public SharedQueueManagementService(BuildJobRepository buildJobRepository, @Qualifier("hazelcastInstance") HazelcastInstance hazelcastInstance, ProfileService profileService) { @@ -86,7 +80,6 @@ public SharedQueueManagementService(BuildJobRepository buildJobRepository, @Qual public void init() { this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); this.processingJobs = this.hazelcastInstance.getMap("processingJobs"); - this.sharedLock = this.hazelcastInstance.getCPSubsystem().getLock("buildJobQueueLock"); this.queue = this.hazelcastInstance.getQueue("buildJobQueue"); this.canceledBuildJobsTopic = hazelcastInstance.getTopic("canceledBuildJobsTopic"); this.dockerImageCleanupInfo = this.hazelcastInstance.getMap("dockerImageCleanupInfo"); @@ -148,28 +141,22 @@ public List getBuildAgentInformationWithoutRecentBuildJob * @param buildJobId id of the build job to cancel */ public void cancelBuildJob(String buildJobId) { - sharedLock.lock(); - try { - // Remove build job if it is queued - if (queue.stream().anyMatch(job -> Objects.equals(job.id(), buildJobId))) { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem job : queue) { - if (Objects.equals(job.id(), buildJobId)) { - toRemove.add(job); - } - } - queue.removeAll(toRemove); - } - else { - // Cancel build job if it is currently being processed - BuildJobQueueItem buildJob = processingJobs.remove(buildJobId); - if (buildJob != null) { - triggerBuildJobCancellation(buildJobId); + // Remove build job if it is queued + if (queue.stream().anyMatch(job -> Objects.equals(job.id(), buildJobId))) { + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem job : queue) { + if (Objects.equals(job.id(), buildJobId)) { + toRemove.add(job); } } + queue.removeAll(toRemove); } - finally { - sharedLock.unlock(); + else { + // Cancel build job if it is currently being processed + BuildJobQueueItem buildJob = processingJobs.remove(buildJobId); + if (buildJob != null) { + triggerBuildJobCancellation(buildJobId); + } } } @@ -188,30 +175,17 @@ private void triggerBuildJobCancellation(String buildJobId) { * Cancel all queued build jobs. */ public void cancelAllQueuedBuildJobs() { - sharedLock.lock(); - try { - log.debug("Cancelling all queued build jobs"); - queue.clear(); - } - finally { - sharedLock.unlock(); - } + log.debug("Cancelling all queued build jobs"); + queue.clear(); } /** * Cancel all running build jobs. */ public void cancelAllRunningBuildJobs() { - sharedLock.lock(); - try { - for (BuildJobQueueItem buildJob : processingJobs.values()) { - cancelBuildJob(buildJob.id()); - } - } - finally { - sharedLock.unlock(); + for (BuildJobQueueItem buildJob : processingJobs.values()) { + cancelBuildJob(buildJob.id()); } - } /** @@ -220,13 +194,7 @@ public void cancelAllRunningBuildJobs() { * @param agentName name of the agent */ public void cancelAllRunningBuildJobsForAgent(String agentName) { - sharedLock.lock(); - try { - processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgentAddress(), agentName)).forEach(job -> cancelBuildJob(job.id())); - } - finally { - sharedLock.unlock(); - } + processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgentAddress(), agentName)).forEach(job -> cancelBuildJob(job.id())); } /** @@ -235,19 +203,13 @@ public void cancelAllRunningBuildJobsForAgent(String agentName) { * @param courseId id of the course */ public void cancelAllQueuedBuildJobsForCourse(long courseId) { - sharedLock.lock(); - try { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem job : queue) { - if (job.courseId() == courseId) { - toRemove.add(job); - } + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem job : queue) { + if (job.courseId() == courseId) { + toRemove.add(job); } - queue.removeAll(toRemove); - } - finally { - sharedLock.unlock(); } + queue.removeAll(toRemove); } /** @@ -269,25 +231,19 @@ public void cancelAllRunningBuildJobsForCourse(long courseId) { * @param participationId id of the participation */ public void cancelAllJobsForParticipation(long participationId) { - sharedLock.lock(); - try { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem queuedJob : queue) { - if (queuedJob.participationId() == participationId) { - toRemove.add(queuedJob); - } + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem queuedJob : queue) { + if (queuedJob.participationId() == participationId) { + toRemove.add(queuedJob); } - queue.removeAll(toRemove); + } + queue.removeAll(toRemove); - for (BuildJobQueueItem runningJob : processingJobs.values()) { - if (runningJob.participationId() == participationId) { - cancelBuildJob(runningJob.id()); - } + for (BuildJobQueueItem runningJob : processingJobs.values()) { + if (runningJob.participationId() == participationId) { + cancelBuildJob(runningJob.id()); } } - finally { - sharedLock.unlock(); - } } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java index 853b89493827..d19d0fae96ba 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java @@ -32,7 +32,6 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import de.tum.in.www1.artemis.domain.BuildLogEntry; @@ -65,11 +64,6 @@ public class SharedQueueProcessingService { private final BuildAgentSshKeyService buildAgentSSHKeyService; - /** - * Lock to prevent multiple nodes from processing the same build job. - */ - private FencedLock sharedLock; - private IQueue queue; private IQueue resultQueue; @@ -104,7 +98,6 @@ public SharedQueueProcessingService(@Qualifier("hazelcastInstance") HazelcastIns public void init() { this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); this.processingJobs = this.hazelcastInstance.getMap("processingJobs"); - this.sharedLock = this.hazelcastInstance.getCPSubsystem().getLock("buildJobQueueLock"); this.queue = this.hazelcastInstance.getQueue("buildJobQueue"); this.resultQueue = this.hazelcastInstance.getQueue("buildResultQueue"); this.listenerId = this.queue.addItemListener(new QueuedBuildJobItemListener(), true); @@ -176,14 +169,8 @@ private void checkAvailabilityAndProcessNextBuild() { return; } - // Lock the queue to prevent multiple nodes from processing the same build job - sharedLock.lock(); - try { - buildJob = addToProcessingJobs(); - } - finally { - sharedLock.unlock(); - } + buildJob = addToProcessingJobs(); + processBuild(buildJob); } catch (RejectedExecutionException e) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java index 97d02ca20df7..22c2cccd6546 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java @@ -74,6 +74,8 @@ public class UserDTO extends AuditingEntityDTO { private String vcsAccessToken; + private ZonedDateTime vcsAccessTokenExpiryDate; + private String sshPublicKey; private ZonedDateTime irisAccepted; @@ -250,6 +252,14 @@ public void setVcsAccessToken(String vcsAccessToken) { this.vcsAccessToken = vcsAccessToken; } + public void setVcsAccessTokenExpiryDate(ZonedDateTime zoneDateTime) { + this.vcsAccessTokenExpiryDate = zoneDateTime; + } + + public ZonedDateTime getVcsAccessTokenExpiryDate() { + return vcsAccessTokenExpiryDate; + } + public String getSshPublicKey() { return sshPublicKey; } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java index 29ba05bcd377..5734c258da9a 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java @@ -2,27 +2,42 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.time.ZonedDateTime; import java.util.Optional; import jakarta.validation.Valid; +import jakarta.ws.rs.BadRequestException; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import de.tum.in.www1.artemis.config.icl.ssh.HashUtils; import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AccountService; +import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCPersonalAccessTokenManagementService; import de.tum.in.www1.artemis.service.dto.PasswordChangeDTO; import de.tum.in.www1.artemis.service.dto.UserDTO; import de.tum.in.www1.artemis.service.user.UserCreationService; import de.tum.in.www1.artemis.service.user.UserService; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException; +import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.rest.errors.EmailAlreadyUsedException; import de.tum.in.www1.artemis.web.rest.errors.PasswordViolatesRequirementsException; @@ -34,6 +49,11 @@ @RequestMapping("api/") public class AccountResource { + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private static final Logger log = LoggerFactory.getLogger(AccountResource.class); + private final UserRepository userRepository; private final UserService userService; @@ -96,4 +116,120 @@ public ResponseEntity changePassword(@RequestBody PasswordChangeDTO passwo return ResponseEntity.ok().build(); } + + /** + * PUT account/ssh-public-key : sets the ssh public key + * + * @param sshPublicKey the ssh public key to set + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @PutMapping("account/ssh-public-key") + @EnforceAtLeastStudent + public ResponseEntity addSshPublicKey(@RequestBody String sshPublicKey) throws GeneralSecurityException, IOException { + + User user = userRepository.getUser(); + log.debug("REST request to add SSH key to user {}", user.getLogin()); + // Parse the public key string + AuthorizedKeyEntry keyEntry; + try { + keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(sshPublicKey); + } + catch (IllegalArgumentException e) { + throw new BadRequestAlertException("Invalid SSH key format", "SSH key", "invalidKeyFormat", true); + } + // Extract the PublicKey object + PublicKey publicKey = keyEntry.resolvePublicKey(null, null, null); + String keyHash = HashUtils.getSha512Fingerprint(publicKey); + userRepository.updateUserSshPublicKeyHash(user.getId(), keyHash, sshPublicKey); + return ResponseEntity.ok().build(); + } + + /** + * PUT account/ssh-public-key : sets the ssh public key + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @DeleteMapping("account/ssh-public-key") + @EnforceAtLeastStudent + public ResponseEntity deleteSshPublicKey() { + User user = userRepository.getUser(); + log.debug("REST request to remove SSH key of user {}", user.getLogin()); + userRepository.updateUserSshPublicKeyHash(user.getId(), null, null); + + log.debug("Successfully deleted SSH key of user {}", user.getLogin()); + return ResponseEntity.ok().build(); + } + + /** + * PUT account/user-vcs-access-token : creates a vcsAccessToken for a user + * + * @param expiryDate The expiry date which should be set for the token + * @return the ResponseEntity with a userDTO containing the token: with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @PutMapping("account/user-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity createVcsAccessToken(@RequestParam("expiryDate") ZonedDateTime expiryDate) { + User user = userRepository.getUser(); + log.debug("REST request to create a new VCS access token for user {}", user.getLogin()); + if (expiryDate.isBefore(ZonedDateTime.now()) || expiryDate.isAfter(ZonedDateTime.now().plusYears(1))) { + throw new BadRequestException("Invalid expiry date provided"); + } + + userRepository.updateUserVcsAccessToken(user.getId(), LocalVCPersonalAccessTokenManagementService.generateSecureVCSAccessToken(), expiryDate); + log.debug("Successfully created a VCS access token for user {}", user.getLogin()); + user = userRepository.getUser(); + UserDTO userDTO = new UserDTO(); + userDTO.setLogin(user.getLogin()); + userDTO.setVcsAccessToken(user.getVcsAccessToken()); + userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate()); + return ResponseEntity.ok(userDTO); + } + + /** + * DELETE account/user-vcs-access-token : deletes the vcsAccessToken of a user + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @DeleteMapping("account/user-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity deleteVcsAccessToken() { + User user = userRepository.getUser(); + log.debug("REST request to remove VCS access token key of user {}", user.getLogin()); + userRepository.updateUserVcsAccessToken(user.getId(), null, null); + log.debug("Successfully deleted VCS access token of user {}", user.getLogin()); + return ResponseEntity.ok().build(); + } + + /** + * GET account/participation-vcs-access-token : get the vcsToken for of a user for a participation + * + * @param participationId the participation for which the access token should be fetched + * + * @return the versionControlAccessToken belonging to the provided participation and user + */ + @GetMapping("account/participation-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity getVcsAccessToken(@RequestParam("participationId") Long participationId) { + User user = userRepository.getUser(); + + log.debug("REST request to get VCS access token of user {} for participation {}", user.getLogin(), participationId); + return ResponseEntity.ok(userService.getParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); + } + + /** + * PUT account/participation-vcs-access-token : get the vcsToken for of a user for a participation + * + * @param participationId the participation for which the access token should be fetched + * + * @return the versionControlAccessToken belonging to the provided participation and user + */ + @PutMapping("account/participation-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity createVcsAccessToken(@RequestParam("participationId") Long participationId) { + User user = userRepository.getUser(); + + log.debug("REST request to create a new VCS access token for user {} for participation {}", user.getLogin(), participationId); + return ResponseEntity.ok(userService.createParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java index 747208042837..8415cbc6f104 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java @@ -2,24 +2,18 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.PublicKey; import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; -import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -29,7 +23,6 @@ import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import de.tum.in.www1.artemis.config.icl.ssh.HashUtils; import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; @@ -39,7 +32,6 @@ import de.tum.in.www1.artemis.service.dto.UserInitializationDTO; import de.tum.in.www1.artemis.service.user.UserCreationService; import de.tum.in.www1.artemis.service.user.UserService; -import de.tum.in.www1.artemis.web.rest.util.HeaderUtil; import tech.jhipster.web.util.PaginationUtil; /** @@ -67,9 +59,6 @@ @RequestMapping("api/") public class UserResource { - @Value("${jhipster.clientApp.name}") - private String applicationName; - private static final Logger log = LoggerFactory.getLogger(UserResource.class); private final UserService userService; @@ -180,81 +169,4 @@ public ResponseEntity setIrisAcceptedToTimestamp() { userRepository.updateIrisAcceptedToDate(user.getId(), ZonedDateTime.now()); return ResponseEntity.ok().build(); } - - /** - * PUT users/sshpublickey : sets the ssh public key - * - * @param sshPublicKey the ssh public key to set - * - * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) - */ - @PutMapping("users/sshpublickey") - @EnforceAtLeastStudent - public ResponseEntity addSshPublicKey(@RequestBody String sshPublicKey) throws GeneralSecurityException, IOException { - - User user = userRepository.getUser(); - log.debug("REST request to add SSH key to user {}", user.getLogin()); - // Parse the public key string - AuthorizedKeyEntry keyEntry; - try { - keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(sshPublicKey); - } - catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(applicationName, true, "sshUserSettings", "saveSshKeyError", "Invalid SSH key format")) - .body(null); - } - // Extract the PublicKey object - PublicKey publicKey = keyEntry.resolvePublicKey(null, null, null); - String keyHash = HashUtils.getSha512Fingerprint(publicKey); - userRepository.updateUserSshPublicKeyHash(user.getId(), keyHash, sshPublicKey); - return ResponseEntity.ok().build(); - } - - /** - * PUT users/sshpublickey : sets the ssh public key - * - * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) - */ - @DeleteMapping("users/sshpublickey") - @EnforceAtLeastStudent - public ResponseEntity deleteSshPublicKey() { - User user = userRepository.getUser(); - log.debug("REST request to remove SSH key of user {}", user.getLogin()); - userRepository.updateUserSshPublicKeyHash(user.getId(), null, null); - - log.debug("Successfully deleted SSH key of user {}", user.getLogin()); - return ResponseEntity.ok().build(); - } - - /** - * GET users/vcsToken : get the vcsToken for of a user for a participation - * - * @param participationId the participation for which the access token should be fetched - * - * @return the versionControlAccessToken belonging to the provided participation and user - */ - @GetMapping("users/vcsToken") - @EnforceAtLeastStudent - public ResponseEntity getVcsAccessToken(@RequestParam("participationId") Long participationId) { - User user = userRepository.getUser(); - - log.debug("REST request to get VCS access token of user {} for participation {}", user.getLogin(), participationId); - return ResponseEntity.ok(userService.getParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); - } - - /** - * PUT users/vcsToken : get the vcsToken for of a user for a participation - * - * @param participationId the participation for which the access token should be fetched - * - * @return the versionControlAccessToken belonging to the provided participation and user - */ - @PutMapping("users/vcsToken") - @EnforceAtLeastStudent - public ResponseEntity createVcsAccessToken(@RequestParam("participationId") Long participationId) { - User user = userRepository.getUser(); - - log.debug("REST request to create a new VCS access token for user {} for participation {}", user.getLogin(), participationId); - return ResponseEntity.ok(userService.createParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); - } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java index 9480b6e77d79..7950c09cb773 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java @@ -165,6 +165,7 @@ public ResponseEntity getAccount() { UserDTO userDTO = new UserDTO(user); // we set this value on purpose here: the user can only fetch their own information, make the token available for constructing the token-based clone-URL userDTO.setVcsAccessToken(user.getVcsAccessToken()); + userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate()); userDTO.setSshPublicKey(user.getSshPublicKey()); log.info("GET /account {} took {}ms", user.getLogin(), System.currentTimeMillis() - start); return ResponseEntity.ok(userDTO); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java index 180d2ca8e077..2620f59f6a5d 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java @@ -1,6 +1,7 @@ package de.tum.in.www1.artemis.web.rest.programming; import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; import java.io.IOException; import java.net.URI; @@ -18,6 +19,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -89,6 +91,7 @@ import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException; import de.tum.in.www1.artemis.web.rest.util.HeaderUtil; import de.tum.in.www1.artemis.web.websocket.dto.ProgrammingExerciseTestCaseStateDTO; +import io.jsonwebtoken.lang.Arrays; /** * REST controller for managing ProgrammingExercise. @@ -151,6 +154,8 @@ public class ProgrammingExerciseResource { private final Optional athenaModuleService; + private final Environment environment; + public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, UserRepository userRepository, AuthorizationCheckService authCheckService, CourseService courseService, Optional continuousIntegrationService, Optional versionControlService, ExerciseService exerciseService, @@ -160,7 +165,8 @@ public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExer GradingCriterionRepository gradingCriterionRepository, CourseRepository courseRepository, GitService gitService, AuxiliaryRepositoryService auxiliaryRepositoryService, SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository, TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepository, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ChannelRepository channelRepository, Optional athenaModuleService) { + BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ChannelRepository channelRepository, Optional athenaModuleService, + Environment environment) { this.programmingExerciseTaskService = programmingExerciseTaskService; this.programmingExerciseRepository = programmingExerciseRepository; this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; @@ -184,6 +190,7 @@ public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExer this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository; this.channelRepository = channelRepository; this.athenaModuleService = athenaModuleService; + this.environment = environment; } /** @@ -303,9 +310,26 @@ public ResponseEntity updateProgrammingExercise(@RequestBod updatedProgrammingExercise.getBuildConfig().isTestwiseCoverageEnabled())) { throw new BadRequestAlertException("Testwise coverage enabled flag must not be changed", ENTITY_NAME, "testwiseCoverageCannotChange"); } - if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde())) { - return ResponseEntity.badRequest().headers(HeaderUtil.createAlert(applicationName, - "You need to allow at least one participation mode, the online editor or the offline IDE", "noParticipationModeAllowed")).body(null); + // Check if theia Profile is enabled + if (Arrays.asList(this.environment.getActiveProfiles()).contains(PROFILE_THEIA)) { + // Require 1 / 3 participation modes to be enabled + if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde()) + && !updatedProgrammingExercise.isAllowOnlineIde()) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor, the offline IDE, or the online IDE", ENTITY_NAME, + "noParticipationModeAllowed"); + } + } + else { + // Require 1 / 2 participation modes to be enabled + if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde())) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor or the offline IDE", ENTITY_NAME, + "noParticipationModeAllowed"); + } + } + + // Verify that a theia image is provided when the online IDE is enabled + if (updatedProgrammingExercise.isAllowOnlineIde() && updatedProgrammingExercise.getBuildConfig().getTheiaImage() == null) { + throw new BadRequestAlertException("You need to provide a Theia image when the online IDE is enabled", ENTITY_NAME, "noTheiaImageProvided"); } // Forbid changing the course the exercise belongs to. if (!Objects.equals(programmingExerciseBeforeUpdate.getCourseViaExerciseGroupOrCourseMember().getId(), diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java new file mode 100644 index 000000000000..24a6977afa5b --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java @@ -0,0 +1,41 @@ +package de.tum.in.www1.artemis.web.rest.theia; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; + +import java.util.Map; + +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.tum.in.www1.artemis.config.TheiaConfiguration; +import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage; +import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; + +@Profile(PROFILE_THEIA) +@RestController +@RequestMapping("api/theia/") +public class TheiaConfigurationResource { + + private final TheiaConfiguration theiaConfiguration; + + public TheiaConfigurationResource(TheiaConfiguration theiaConfiguration) { + this.theiaConfiguration = theiaConfiguration; + } + + /** + * GET /api/theia/images?language=: Get the images for a specific language + * + * @param language the language for which the images should be retrieved + * @return a map of flavor/name -> image-link + */ + @GetMapping("images") + @EnforceAtLeastInstructor + public ResponseEntity> getImagesForLanguage(@RequestParam("language") ProgrammingLanguage language) { + return ResponseEntity.ok(this.theiaConfiguration.getImagesForLanguage(language)); + } + +} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 242f427c6a8e..ec007df2f264 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -129,4 +129,13 @@ eureka: # Theia configuration theia: - portal-url: https://theia-test.k8s.ase.cit.tum.de + portal-url: https://theia-test.k8s.ase.cit.tum.de + + images: + java: + Java-17: "ghcr.io/ls1intum/theia/java-17:latest" + Java-Test: "ghcr.io/ls1intum/theia/java-test:latest" + Java-Test2: "ghcr.io/ls1intum/theia/java-test:2" + c: + C: "ghcr.io/ls1intum/theia/c:latest" + diff --git a/src/main/resources/config/application-theia.yml b/src/main/resources/config/application-theia.yml index 2b8e1346d008..95b058f66e20 100644 --- a/src/main/resources/config/application-theia.yml +++ b/src/main/resources/config/application-theia.yml @@ -1,2 +1,13 @@ theia: - portal-url: https://your-theia-instance.com + portal-url: https://your-theia-instance.com + + # Theia IDE images available for the different programming languages + images: + # Upper level key is the language category (must match the language key in the programming-exercise configuration) + java: + # Lower level key can be multiple flavors of the image, e.g. version, tag, or additional dependencies + Java-17: "my-registry/my-image:my-tag" + # Add more flavors here (e.g. Java-11, Java-8, etc.) + # Add more languages here (e.g. c, python, etc.) + c: + C: "my-registry/my-image:my-tag" diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 8c391da0b7a9..8c66b009451c 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -198,6 +198,9 @@ spring: thread-name-prefix: artemis-scheduling- pool: size: 2 + threads: + virtual: + enabled: true thymeleaf: mode: HTML output: diff --git a/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts b/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts index bb8614435e1d..8855ae1fb047 100644 --- a/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts +++ b/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts @@ -3,7 +3,7 @@ import { HttpResponse } from '@angular/common/http'; import { EntityArrayResponseType as ExerciseEntityArrayResponseType, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { Exercise } from 'app/entities/exercise.model'; import { SortService } from 'app/shared/service/sort.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; @Component({ diff --git a/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts b/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts index a2418b6b35d4..3245b9f4af72 100644 --- a/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts +++ b/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, HostListener, Input, Output } from '@angular/c import { Result } from 'app/entities/result.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { ActivatedRoute } from '@angular/router'; import { ComplaintType } from 'app/entities/complaint.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts index c7c7aa77c910..d854c6f7ad58 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts +++ b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts @@ -3,10 +3,10 @@ import { SafeHtml } from '@angular/platform-browser'; import { UMLDiagramType, UMLModel } from '@ls1intum/apollon'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; diff --git a/src/main/webapp/app/assessment/athena.service.ts b/src/main/webapp/app/assessment/athena.service.ts index 01e7d4ca0def..962e56b70a39 100644 --- a/src/main/webapp/app/assessment/athena.service.ts +++ b/src/main/webapp/app/assessment/athena.service.ts @@ -5,9 +5,9 @@ import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ModelingFeedbackSuggestion, ProgrammingFeedbackSuggestion, TextFeedbackSuggestion } from 'app/entities/feedback-suggestion.model'; import { Exercise } from 'app/entities/exercise.model'; import { FEEDBACK_SUGGESTION_ACCEPTED_IDENTIFIER, FEEDBACK_SUGGESTION_IDENTIFIER, Feedback, FeedbackType } from 'app/entities/feedback.model'; -import { TextBlock } from 'app/entities/text-block.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { PROFILE_ATHENA } from 'app/app.constants'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { UMLModel, findElement } from '@ls1intum/apollon'; diff --git a/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts index b37486509496..b469dcab432f 100644 --- a/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts +++ b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts @@ -6,7 +6,7 @@ import { StudentParticipation } from 'app/entities/participation/student-partici import { Result } from 'app/entities/result.model'; import { Course } from 'app/entities/course.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AccountService } from 'app/core/auth/account.service'; import { Submission } from 'app/entities/submission.model'; import { filter } from 'rxjs/operators'; diff --git a/src/main/webapp/app/core/auth/account.service.ts b/src/main/webapp/app/core/auth/account.service.ts index 81e0c074557b..1b6a82c3a0a0 100644 --- a/src/main/webapp/app/core/auth/account.service.ts +++ b/src/main/webapp/app/core/auth/account.service.ts @@ -12,7 +12,7 @@ import { StudentParticipation } from 'app/entities/participation/student-partici import { Exercise, getCourseFromExercise } from 'app/entities/exercise.model'; import { Authority } from 'app/shared/constants/authority.constants'; import { TranslateService } from '@ngx-translate/core'; -import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { EntityResponseType } from 'app/complaints/complaint.service'; export interface IAccountService { save: (account: any) => Observable>; @@ -39,7 +39,6 @@ export class AccountService implements IAccountService { private authenticated = false; private authenticationState = new BehaviorSubject(undefined); private prefilledUsernameValue?: string; - private versionControlAccessTokenRequired: boolean; constructor( private translateService: TranslateService, @@ -47,7 +46,6 @@ export class AccountService implements IAccountService { private http: HttpClient, private websocketService: JhiWebsocketService, private featureToggleService: FeatureToggleService, - private profileService: ProfileService, ) {} get userIdentity() { @@ -137,19 +135,10 @@ export class AccountService implements IAccountService { this.userIdentity = undefined; } - if (this.versionControlAccessTokenRequired === undefined) { - this.profileService.getProfileInfo().subscribe((profileInfo) => { - this.versionControlAccessTokenRequired = profileInfo.useVersionControlAccessToken ?? false; - }); - } - // check and see if we have retrieved the userIdentity data from the server. // if we have, reuse it by immediately resolving if (this.userIdentity) { - // in case a token is required but not present in the user, we cannot simply return the cached object - if (!this.versionControlAccessTokenRequired || this.userIdentity.vcsAccessToken !== undefined) { - return Promise.resolve(this.userIdentity); - } + return Promise.resolve(this.userIdentity); } // retrieve the userIdentity data from the server, update the identity object, and then resolve. @@ -336,7 +325,7 @@ export class AccountService implements IAccountService { if (this.userIdentity) { this.userIdentity.sshPublicKey = sshPublicKey; } - return this.http.put('api/users/sshpublickey', sshPublicKey); + return this.http.put('api/account/ssh-public-key', sshPublicKey); } /** @@ -346,7 +335,24 @@ export class AccountService implements IAccountService { if (this.userIdentity) { this.userIdentity.sshPublicKey = undefined; } - return this.http.delete('api/users/sshpublickey'); + return this.http.delete('api/account/ssh-public-key'); + } + + /** + * Sends a request to the server to delete the user's current vcsAccessToken + */ + deleteUserVcsAccessToken(): Observable { + return this.http.delete('api/account/user-vcs-access-token'); + } + + /** + * Sends a request to the server to create a new vcsAccessToken for the user + * + * @param expiryDate The expiry date which should get set for the vcsAccessToken + */ + addNewVcsAccessToken(expiryDate: string): Observable { + const params = new HttpParams().set('expiryDate', expiryDate); + return this.http.put('api/account/user-vcs-access-token', null, { observe: 'response', params }); } /** @@ -357,7 +363,7 @@ export class AccountService implements IAccountService { */ getVcsAccessToken(participationId: number): Observable> { const params = new HttpParams().set('participationId', participationId); - return this.http.get('api/users/vcsToken', { observe: 'response', params, responseType: 'text' as 'json' }); + return this.http.get('api/account/participation-vcs-access-token', { observe: 'response', params, responseType: 'text' as 'json' }); } /** @@ -368,6 +374,6 @@ export class AccountService implements IAccountService { */ createVcsAccessToken(participationId: number): Observable> { const params = new HttpParams().set('participationId', participationId); - return this.http.put('api/users/vcsToken', null, { observe: 'response', params, responseType: 'text' as 'json' }); + return this.http.put('api/account/participation-vcs-access-token', null, { observe: 'response', params, responseType: 'text' as 'json' }); } } diff --git a/src/main/webapp/app/core/user/user.model.ts b/src/main/webapp/app/core/user/user.model.ts index 0c30062efbd0..816cf4fc9a9c 100644 --- a/src/main/webapp/app/core/user/user.model.ts +++ b/src/main/webapp/app/core/user/user.model.ts @@ -14,6 +14,7 @@ export class User extends Account { public visibleRegistrationNumber?: string; public password?: string; public vcsAccessToken?: string; + public vcsAccessTokenExpiryDate?: string; public sshPublicKey?: string; public irisAccepted?: dayjs.Dayjs; @@ -35,6 +36,7 @@ export class User extends Account { password?: string, imageUrl?: string, vcsAccessToken?: string, + vcsAccessTokenExpiryDate?: string, sshPublicKey?: string, irisAccepted?: dayjs.Dayjs, ) { @@ -48,6 +50,7 @@ export class User extends Account { this.lastNotificationRead = lastNotificationRead; this.password = password; this.vcsAccessToken = vcsAccessToken; + this.vcsAccessTokenExpiryDate = vcsAccessTokenExpiryDate; this.sshPublicKey = sshPublicKey; this.irisAccepted = irisAccepted; } diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts index 1f813c75104c..a39b6af80e77 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts @@ -13,7 +13,7 @@ import { Course } from 'app/entities/course.model'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import { FilterProp as TeamFilterProp } from 'app/exercises/shared/team/teams.component'; import { SortService } from 'app/shared/service/sort.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts index 704c19d37fdc..7bc20e00e55c 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts @@ -9,7 +9,7 @@ import { Course } from 'app/entities/course.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { AlertService } from 'app/core/util/alert.service'; import { HttpErrorResponse } from '@angular/common/http'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { AccountService } from 'app/core/auth/account.service'; import { onError } from 'app/shared/util/global.utils'; diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.html b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.html index 10feb00946b8..2341a771ae26 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.html +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.html @@ -1,4 +1,4 @@ -@if (isLectureUnitLoading()) { +@if (isLoading()) {
diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts index a7cd72938c93..c927e2a4532e 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts @@ -1,12 +1,11 @@ -import { Component, InputSignal, Signal, WritableSignal, inject, input, signal } from '@angular/core'; +import { Component, InputSignal, WritableSignal, effect, inject, input, signal } from '@angular/core'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { AlertService } from 'app/core/util/alert.service'; import { LectureUnit, LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model'; import { ArtemisLectureUnitsModule } from 'app/overview/course-lectures/lecture-units.module'; import { LectureUnitCompletionEvent } from 'app/overview/course-lectures/course-lecture-details.component'; import { LearningPathNavigationService } from 'app/course/learning-paths/services/learning-path-navigation.service'; -import { Observable, lastValueFrom, switchMap } from 'rxjs'; -import { toObservable, toSignal } from '@angular/core/rxjs-interop'; +import { lastValueFrom } from 'rxjs'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { VideoUnitComponent } from 'app/overview/course-lectures/video-unit/video-unit.component'; import { TextUnitComponent } from 'app/overview/course-lectures/text-unit/text-unit.component'; @@ -27,18 +26,22 @@ export class LearningPathLectureUnitComponent { private readonly alertService: AlertService = inject(AlertService); readonly lectureUnitId: InputSignal = input.required(); - readonly isLectureUnitLoading: WritableSignal = signal(false); - private readonly lectureUnit$: Observable = toObservable(this.lectureUnitId).pipe(switchMap((lectureUnitId) => this.getLectureUnit(lectureUnitId))); - readonly lectureUnit: Signal = toSignal(this.lectureUnit$); + readonly isLoading: WritableSignal = signal(false); + readonly lectureUnit = signal(undefined); - async getLectureUnit(lectureUnitId: number): Promise { + constructor() { + effect(() => this.loadLectureUnit(this.lectureUnitId()), { allowSignalWrites: true }); + } + + async loadLectureUnit(lectureUnitId: number): Promise { try { - this.isLectureUnitLoading.set(true); - return await lastValueFrom(this.lectureUnitService.getLectureUnitById(lectureUnitId)); + this.isLoading.set(true); + const lectureUnit = await lastValueFrom(this.lectureUnitService.getLectureUnitById(lectureUnitId)); + this.lectureUnit.set(lectureUnit); } catch (error) { this.alertService.error(error); } finally { - this.isLectureUnitLoading.set(false); + this.isLoading.set(false); } } diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts index 15589bdb24bc..97609dd46c27 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts @@ -1,4 +1,4 @@ -import { Component, InputSignal, OnInit, OutputEmitterRef, Signal, WritableSignal, computed, inject, input, output, signal } from '@angular/core'; +import { Component, InputSignal, OutputEmitterRef, Signal, WritableSignal, computed, effect, inject, input, output, signal, untracked } from '@angular/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { AlertService } from 'app/core/util/alert.service'; import { LearningPathApiService } from 'app/course/learning-paths/services/learning-path-api.service'; @@ -15,7 +15,7 @@ import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; templateUrl: './learning-path-nav-overview-learning-objects.component.html', styleUrl: './learning-path-nav-overview-learning-objects.component.scss', }) -export class LearningPathNavOverviewLearningObjectsComponent implements OnInit { +export class LearningPathNavOverviewLearningObjectsComponent { protected readonly faCheckCircle: IconDefinition = faCheckCircle; protected readonly faLock: IconDefinition = faLock; @@ -38,8 +38,13 @@ export class LearningPathNavOverviewLearningObjectsComponent implements OnInit { readonly onLearningObjectSelected: OutputEmitterRef = output(); - ngOnInit(): void { - this.loadLearningObjects(); + constructor() { + effect( + () => { + untracked(async () => await this.loadLearningObjects()); + }, + { allowSignalWrites: true }, + ); } async loadLearningObjects(): Promise { @@ -57,9 +62,9 @@ export class LearningPathNavOverviewLearningObjectsComponent implements OnInit { } } - selectLearningObject(learningObject: LearningPathNavigationObjectDTO): void { + async selectLearningObject(learningObject: LearningPathNavigationObjectDTO): Promise { if (!learningObject.unreleased) { - this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), learningObject); + await this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), learningObject); this.onLearningObjectSelected.emit(); } } diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts index 669bde84d5b3..a63830f98bc7 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts @@ -1,4 +1,4 @@ -import { Component, InputSignal, OutputEmitterRef, Signal, WritableSignal, computed, inject, input, output, signal, viewChild } from '@angular/core'; +import { Component, InputSignal, OutputEmitterRef, Signal, WritableSignal, computed, effect, inject, input, output, signal, viewChild } from '@angular/core'; import { NgbAccordionDirective, NgbAccordionModule, NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { CommonModule } from '@angular/common'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; @@ -10,8 +10,6 @@ import { LearningPathApiService } from 'app/course/learning-paths/services/learn import { CompetencyGraphModalComponent } from 'app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component'; import { LearningPathNavOverviewLearningObjectsComponent } from 'app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component'; import { LearningPathNavigationService } from 'app/course/learning-paths/services/learning-path-navigation.service'; -import { toObservable, toSignal } from '@angular/core/rxjs-interop'; -import { Observable, switchMap } from 'rxjs'; @Component({ selector: 'jhi-learning-path-nav-overview', @@ -34,25 +32,26 @@ export class LearningPathNavOverviewComponent { readonly onLearningObjectSelected: OutputEmitterRef = output(); readonly isLoading: WritableSignal = signal(false); - private readonly competencies$: Observable = toObservable(this.learningPathId).pipe( - switchMap((learningPathId) => this.loadCompetencies(learningPathId)), - ); - readonly competencies: Signal = toSignal(this.competencies$); + readonly competencies = signal([]); // competency id of currently selected learning object readonly currentCompetencyId: Signal = computed(() => this.learningPathNavigationService.currentLearningObject()?.competencyId); // current competency of learning path (not the one of the selected learning object) readonly currentCompetencyOnPath: Signal = computed(() => this.competencies()?.find((competency) => competency.masteryProgress < 1)); - async loadCompetencies(learningPathId: number): Promise { + constructor() { + effect(async () => await this.loadCompetencies(this.learningPathId()), { allowSignalWrites: true }); + } + + async loadCompetencies(learningPathId: number): Promise { try { this.isLoading.set(true); const competencies = await this.learningPathApiService.getLearningPathCompetencies(learningPathId); - this.isLoading.set(false); - return competencies; + this.competencies.set(competencies); } catch (error) { this.alertService.error(error); - return []; + } finally { + this.isLoading.set(false); } } diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts index 906cf5148089..90eb7bb2b43e 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts @@ -1,4 +1,4 @@ -import { Component, InputSignal, OnInit, Signal, WritableSignal, computed, inject, input, signal } from '@angular/core'; +import { Component, InputSignal, Signal, WritableSignal, computed, effect, inject, input, signal } from '@angular/core'; import { LearningPathNavigationObjectDTO } from 'app/entities/competency/learning-path.model'; import { CommonModule } from '@angular/common'; import { NgbAccordionModule, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; @@ -15,7 +15,7 @@ import { LearningPathNavigationService } from 'app/course/learning-paths/service templateUrl: './learning-path-student-nav.component.html', styleUrl: './learning-path-student-nav.component.scss', }) -export class LearningPathNavComponent implements OnInit { +export class LearningPathNavComponent { protected readonly faChevronDown: IconDefinition = faChevronDown; protected readonly faCheckCircle: IconDefinition = faCheckCircle; protected readonly faFlag: IconDefinition = faFlag; @@ -37,12 +37,12 @@ export class LearningPathNavComponent implements OnInit { readonly isDropdownOpen: WritableSignal = signal(false); - ngOnInit(): void { - this.learningPathNavigationService.loadLearningPathNavigation(this.learningPathId()); + constructor() { + effect(async () => await this.learningPathNavigationService.loadLearningPathNavigation(this.learningPathId()), { allowSignalWrites: true }); } - selectLearningObject(selectedLearningObject: LearningPathNavigationObjectDTO): void { - this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), selectedLearningObject); + async selectLearningObject(selectedLearningObject: LearningPathNavigationObjectDTO): Promise { + await this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), selectedLearningObject); } completeLearningPath(): void { diff --git a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts index 18d74923ab92..7df486af265c 100644 --- a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts +++ b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Signal, inject, signal } from '@angular/core'; +import { Component, effect, inject, signal } from '@angular/core'; import { LearningObjectType } from 'app/entities/competency/learning-path.model'; import { map } from 'rxjs'; import { toSignal } from '@angular/core/rxjs-interop'; @@ -29,7 +29,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; ArtemisSharedModule, ], }) -export class LearningPathStudentPageComponent implements OnInit { +export class LearningPathStudentPageComponent { protected readonly LearningObjectType = LearningObjectType; private readonly learningApiService: LearningPathApiService = inject(LearningPathApiService); @@ -39,12 +39,12 @@ export class LearningPathStudentPageComponent implements OnInit { readonly isLearningPathIdLoading = signal(false); readonly learningPathId = signal(undefined); - readonly courseId: Signal = toSignal(this.activatedRoute.parent!.parent!.params.pipe(map((params) => params.courseId))); + readonly courseId = toSignal(this.activatedRoute.parent!.parent!.params.pipe(map((params) => Number(params.courseId))), { requireSync: true }); readonly currentLearningObject = this.learningPathNavigationService.currentLearningObject; readonly isLearningPathNavigationLoading = this.learningPathNavigationService.isLoading; - ngOnInit(): void { - this.loadLearningPathId(this.courseId()); + constructor() { + effect(async () => await this.loadLearningPathId(this.courseId()), { allowSignalWrites: true }); } private async loadLearningPathId(courseId: number): Promise { diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index cc260d0a289b..a3406ad5f46b 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -21,7 +21,7 @@ import { OrganizationSelectorComponent } from 'app/shared/organization-selector/ import { faBan, faExclamationTriangle, faPen, faQuestionCircle, faSave, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; import { base64StringToBlob } from 'app/utils/blob-util'; import { ImageCroppedEvent } from 'app/shared/image-cropper/interfaces/image-cropped-event.interface'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts b/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts index 43d6336168a7..123e334cca33 100644 --- a/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts +++ b/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts @@ -1,5 +1,5 @@ import { CourseForDashboardDTO } from 'app/course/manage/course-for-dashboard-dto'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; export class CoursesForDashboardDTO { courses: CourseForDashboardDTO[]; diff --git a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts index 7f98ff940ef1..4f49ce9fe006 100644 --- a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts +++ b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts @@ -9,7 +9,7 @@ import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programmin import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model'; import { ModelingExerciseService } from 'app/exercises/modeling/manage/modeling-exercise.service'; import { AlertService } from 'app/core/util/alert.service'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { Detail } from 'app/detail-overview-list/detail.model'; import { UMLModel } from '@ls1intum/apollon'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; diff --git a/src/main/webapp/app/detail-overview-list/detail.model.ts b/src/main/webapp/app/detail-overview-list/detail.model.ts index f8e8a5562044..77085194fb0b 100644 --- a/src/main/webapp/app/detail-overview-list/detail.model.ts +++ b/src/main/webapp/app/detail-overview-list/detail.model.ts @@ -1,12 +1,12 @@ import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; -import type { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import type { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { ProgrammingExerciseInstructorRepositoryType } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; -import { BuildLogStatisticsDTO } from 'app/entities/build-log-statistics-dto'; +import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; import { DetailType } from 'app/detail-overview-list/detail-overview-list.component'; import { SafeHtml } from '@angular/platform-browser'; import { UMLDiagramType, UMLModel } from '@ls1intum/apollon'; diff --git a/src/main/webapp/app/entities/consistency-check-result.model.ts b/src/main/webapp/app/entities/consistency-check-result.model.ts index b887135572b7..67b23aeb1193 100644 --- a/src/main/webapp/app/entities/consistency-check-result.model.ts +++ b/src/main/webapp/app/entities/consistency-check-result.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export const enum ErrorType { TEMPLATE_REPO_MISSING = 'TEMPLATE_REPO_MISSING', diff --git a/src/main/webapp/app/entities/course.model.ts b/src/main/webapp/app/entities/course.model.ts index 77abadd7a640..4f179de3a687 100644 --- a/src/main/webapp/app/entities/course.model.ts +++ b/src/main/webapp/app/entities/course.model.ts @@ -3,11 +3,11 @@ import dayjs from 'dayjs/esm'; import { Lecture } from 'app/entities/lecture.model'; import { Exercise } from 'app/entities/exercise.model'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Competency } from 'app/entities/competency.model'; import { Organization } from 'app/entities/organization.model'; import { Post } from 'app/entities/metis/post.model'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { OnlineCourseConfiguration } from 'app/entities/online-course-configuration.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; diff --git a/src/main/webapp/app/entities/exam-checklist.model.ts b/src/main/webapp/app/entities/exam/exam-checklist.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-checklist.model.ts rename to src/main/webapp/app/entities/exam/exam-checklist.model.ts diff --git a/src/main/webapp/app/entities/exam-exercise-overview-item.model.ts b/src/main/webapp/app/entities/exam/exam-exercise-overview-item.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-exercise-overview-item.model.ts rename to src/main/webapp/app/entities/exam/exam-exercise-overview-item.model.ts diff --git a/src/main/webapp/app/entities/exam-information.model.ts b/src/main/webapp/app/entities/exam/exam-information.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-information.model.ts rename to src/main/webapp/app/entities/exam/exam-information.model.ts diff --git a/src/main/webapp/app/entities/exam-page.model.ts b/src/main/webapp/app/entities/exam/exam-page.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-page.model.ts rename to src/main/webapp/app/entities/exam/exam-page.model.ts diff --git a/src/main/webapp/app/entities/exam-session.model.ts b/src/main/webapp/app/entities/exam/exam-session.model.ts similarity index 97% rename from src/main/webapp/app/entities/exam-session.model.ts rename to src/main/webapp/app/entities/exam/exam-session.model.ts index feb4670ac5c9..96fc05c9b400 100644 --- a/src/main/webapp/app/entities/exam-session.model.ts +++ b/src/main/webapp/app/entities/exam/exam-session.model.ts @@ -1,6 +1,6 @@ import { BaseEntity } from 'app/shared/model/base-entity'; import dayjs from 'dayjs/esm'; -import { StudentExam } from './student-exam.model'; +import { StudentExam } from '../student-exam.model'; export enum SuspiciousSessionReason { DIFFERENT_STUDENT_EXAMS_SAME_IP_ADDRESS = 'DIFFERENT_STUDENT_EXAMS_SAME_IP_ADDRESS', diff --git a/src/main/webapp/app/entities/exam-user-dto.model.ts b/src/main/webapp/app/entities/exam/exam-user-dto.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-user-dto.model.ts rename to src/main/webapp/app/entities/exam/exam-user-dto.model.ts diff --git a/src/main/webapp/app/entities/exam-user.model.ts b/src/main/webapp/app/entities/exam/exam-user.model.ts similarity index 93% rename from src/main/webapp/app/entities/exam-user.model.ts rename to src/main/webapp/app/entities/exam/exam-user.model.ts index 53a46e26e36d..e1d3edae6fb1 100644 --- a/src/main/webapp/app/entities/exam-user.model.ts +++ b/src/main/webapp/app/entities/exam/exam-user.model.ts @@ -1,5 +1,5 @@ import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { BaseEntity } from 'app/shared/model/base-entity'; export class ExamUser implements BaseEntity { diff --git a/src/main/webapp/app/entities/exam-users-attendance-check-dto.model.ts b/src/main/webapp/app/entities/exam/exam-users-attendance-check-dto.model.ts similarity index 100% rename from src/main/webapp/app/entities/exam-users-attendance-check-dto.model.ts rename to src/main/webapp/app/entities/exam/exam-users-attendance-check-dto.model.ts diff --git a/src/main/webapp/app/entities/exam.model.ts b/src/main/webapp/app/entities/exam/exam.model.ts similarity index 97% rename from src/main/webapp/app/entities/exam.model.ts rename to src/main/webapp/app/entities/exam/exam.model.ts index f8c813558467..4ebd6fea578f 100644 --- a/src/main/webapp/app/entities/exam.model.ts +++ b/src/main/webapp/app/entities/exam/exam.model.ts @@ -1,4 +1,4 @@ -import { ExamUser } from 'app/entities/exam-user.model'; +import { ExamUser } from 'app/entities/exam/exam-user.model'; import dayjs from 'dayjs/esm'; import { Course } from 'app/entities/course.model'; import { StudentExam } from 'app/entities/student-exam.model'; diff --git a/src/main/webapp/app/entities/exercise-category.model.ts b/src/main/webapp/app/entities/exercise-category.model.ts index bc134d0b7f29..68f09c914d2d 100644 --- a/src/main/webapp/app/entities/exercise-category.model.ts +++ b/src/main/webapp/app/entities/exercise-category.model.ts @@ -1,6 +1,30 @@ export class ExerciseCategory { public color?: string; + + // TODO should be renamed to "name" -> accessing variable via "category.name" instead of "category.category" - requires database migration (stored as json in database, see the table "exercise_categories") public category?: string; - constructor() {} + constructor(category: string | undefined, color: string | undefined) { + this.color = color; + this.category = category; + } + + equals(otherExerciseCategory: ExerciseCategory): boolean { + return this.color === otherExerciseCategory.color && this.category === otherExerciseCategory.category; + } + + /** + * @param otherExerciseCategory + * @returns the alphanumerical order of the two exercise categories based on their display text + */ + compare(otherExerciseCategory: ExerciseCategory): number { + if (this.category === otherExerciseCategory.category) { + return 0; + } + + const displayText = this.category?.toLowerCase() ?? ''; + const otherCategoryDisplayText = otherExerciseCategory.category?.toLowerCase() ?? ''; + + return displayText < otherCategoryDisplayText ? -1 : 1; + } } diff --git a/src/main/webapp/app/entities/exercise-group.model.ts b/src/main/webapp/app/entities/exercise-group.model.ts index 65c14544f2e4..aeca98ee9109 100644 --- a/src/main/webapp/app/entities/exercise-group.model.ts +++ b/src/main/webapp/app/entities/exercise-group.model.ts @@ -1,5 +1,5 @@ import { Exercise } from 'app/entities/exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { BaseEntity } from 'app/shared/model/base-entity'; export class ExerciseGroup implements BaseEntity { diff --git a/src/main/webapp/app/entities/feedback.model.ts b/src/main/webapp/app/entities/feedback.model.ts index 7666973744e9..7c6809e6e2f3 100644 --- a/src/main/webapp/app/entities/feedback.model.ts +++ b/src/main/webapp/app/entities/feedback.model.ts @@ -1,9 +1,9 @@ import { BaseEntity } from 'app/shared/model/base-entity'; import { Result } from 'app/entities/result.model'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { GradingInstruction } from 'app/exercises/shared/structured-grading-criterion/grading-instruction.model'; import { convertToHtmlLinebreaks, escapeString } from 'app/utils/text.utils'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; export enum FeedbackHighlightColor { RED = 'rgba(219, 53, 69, 0.6)', diff --git a/src/main/webapp/app/entities/grading-scale.model.ts b/src/main/webapp/app/entities/grading-scale.model.ts index 7652eb6e9248..2042535aab54 100644 --- a/src/main/webapp/app/entities/grading-scale.model.ts +++ b/src/main/webapp/app/entities/grading-scale.model.ts @@ -1,7 +1,7 @@ import { GradeStep } from 'app/entities/grade-step.model'; import { BaseEntity } from 'app/shared/model/base-entity'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; export class GradingScale implements BaseEntity { public static readonly DEFAULT_PLAGIARISM_GRADE = 'U'; // This should be the same as the corresponding constant in GradingScale.java diff --git a/src/main/webapp/app/entities/hestia/exercise-hint.model.ts b/src/main/webapp/app/entities/hestia/exercise-hint.model.ts index d48c828adba2..fa61b885b7ec 100644 --- a/src/main/webapp/app/entities/hestia/exercise-hint.model.ts +++ b/src/main/webapp/app/entities/hestia/exercise-hint.model.ts @@ -1,4 +1,4 @@ -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts b/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts index 4471eb472fce..5fccf4e2f4aa 100644 --- a/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts +++ b/src/main/webapp/app/entities/hestia/programming-exercise-git-diff-report.model.ts @@ -1,4 +1,4 @@ -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; import { ProgrammingExerciseGitDiffEntry } from 'app/entities/hestia/programming-exercise-git-diff-entry.model'; diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts b/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts index eb1ee53e4ec2..9380ec858a4f 100644 --- a/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts +++ b/src/main/webapp/app/entities/hestia/programming-exercise-solution-entry.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; export class ProgrammingExerciseSolutionEntry implements BaseEntity { diff --git a/src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts b/src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts index d83351b2883b..43d4a5b5d144 100644 --- a/src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts +++ b/src/main/webapp/app/entities/hestia/programming-exercise-task.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; export class ProgrammingExerciseServerSideTask implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts b/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts index 47c49d57483d..59234fffbcfe 100644 --- a/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts +++ b/src/main/webapp/app/entities/hestia/testwise-coverage-report-entry.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; export class TestwiseCoverageReportEntry implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/iris/iris-exercise-chat-session.model.ts b/src/main/webapp/app/entities/iris/iris-exercise-chat-session.model.ts index 752551cb58c7..c86342e1d332 100644 --- a/src/main/webapp/app/entities/iris/iris-exercise-chat-session.model.ts +++ b/src/main/webapp/app/entities/iris/iris-exercise-chat-session.model.ts @@ -1,5 +1,5 @@ import { IrisSession } from 'app/entities/iris/iris-session.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class IrisExerciseChatSession extends IrisSession { exercise?: ProgrammingExercise; diff --git a/src/main/webapp/app/entities/metis/conversation/channel.model.ts b/src/main/webapp/app/entities/metis/conversation/channel.model.ts index 450cfc266691..dbcc9bb3aac1 100644 --- a/src/main/webapp/app/entities/metis/conversation/channel.model.ts +++ b/src/main/webapp/app/entities/metis/conversation/channel.model.ts @@ -1,7 +1,7 @@ import { Conversation, ConversationDTO, ConversationType } from 'app/entities/metis/conversation/conversation.model'; import { Exercise } from 'app/entities/exercise.model'; import { Lecture } from 'app/entities/lecture.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; // IMPORTANT NOTICE: The following strings have to be consistent with // the types defined in ChannelSubType.java diff --git a/src/main/webapp/app/entities/participation/solution-programming-exercise-participation.model.ts b/src/main/webapp/app/entities/participation/solution-programming-exercise-participation.model.ts index d9db5fc2b9d3..80bad35f24db 100644 --- a/src/main/webapp/app/entities/participation/solution-programming-exercise-participation.model.ts +++ b/src/main/webapp/app/entities/participation/solution-programming-exercise-participation.model.ts @@ -1,5 +1,5 @@ import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class SolutionProgrammingExerciseParticipation extends Participation { public programmingExercise?: ProgrammingExercise; diff --git a/src/main/webapp/app/entities/participation/template-programming-exercise-participation.model.ts b/src/main/webapp/app/entities/participation/template-programming-exercise-participation.model.ts index 248a30b3e3f4..d6db1617acec 100644 --- a/src/main/webapp/app/entities/participation/template-programming-exercise-participation.model.ts +++ b/src/main/webapp/app/entities/participation/template-programming-exercise-participation.model.ts @@ -1,5 +1,5 @@ import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class TemplateProgrammingExerciseParticipation extends Participation { public programmingExercise?: ProgrammingExercise; diff --git a/src/main/webapp/app/entities/programming/aeolus.result.ts b/src/main/webapp/app/entities/programming/aeolus.result.ts new file mode 100644 index 000000000000..593a09df693e --- /dev/null +++ b/src/main/webapp/app/entities/programming/aeolus.result.ts @@ -0,0 +1,7 @@ +export class AeolusResult { + name: string; + path: string; + ignore: string; + type?: string; + before?: boolean; +} diff --git a/src/main/webapp/app/entities/build-agent.model.ts b/src/main/webapp/app/entities/programming/build-agent.model.ts similarity index 84% rename from src/main/webapp/app/entities/build-agent.model.ts rename to src/main/webapp/app/entities/programming/build-agent.model.ts index bb33b3a4dd4e..172a8596b299 100644 --- a/src/main/webapp/app/entities/build-agent.model.ts +++ b/src/main/webapp/app/entities/programming/build-agent.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { BuildJob } from 'app/entities/build-job.model'; +import { BuildJob } from 'app/entities/programming/build-job.model'; export class BuildAgent implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/build-config.model.ts b/src/main/webapp/app/entities/programming/build-config.model.ts similarity index 100% rename from src/main/webapp/app/entities/build-config.model.ts rename to src/main/webapp/app/entities/programming/build-config.model.ts diff --git a/src/main/webapp/app/entities/build-job.model.ts b/src/main/webapp/app/entities/programming/build-job.model.ts similarity index 93% rename from src/main/webapp/app/entities/build-job.model.ts rename to src/main/webapp/app/entities/programming/build-job.model.ts index 55155414cc8d..a36bb9756091 100644 --- a/src/main/webapp/app/entities/build-job.model.ts +++ b/src/main/webapp/app/entities/programming/build-job.model.ts @@ -1,7 +1,7 @@ import { StringBaseEntity } from 'app/shared/model/base-entity'; -import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { JobTimingInfo } from 'app/entities/job-timing-info.model'; -import { BuildConfig } from 'app/entities/build-config.model'; +import { BuildConfig } from 'app/entities/programming/build-config.model'; import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; diff --git a/src/main/webapp/app/entities/build-log-statistics-dto.ts b/src/main/webapp/app/entities/programming/build-log-statistics-dto.ts similarity index 100% rename from src/main/webapp/app/entities/build-log-statistics-dto.ts rename to src/main/webapp/app/entities/programming/build-log-statistics-dto.ts diff --git a/src/main/webapp/app/entities/build-log.model.ts b/src/main/webapp/app/entities/programming/build-log.model.ts similarity index 99% rename from src/main/webapp/app/entities/build-log.model.ts rename to src/main/webapp/app/entities/programming/build-log.model.ts index db33d9a63412..fd4135a005cd 100644 --- a/src/main/webapp/app/entities/build-log.model.ts +++ b/src/main/webapp/app/entities/programming/build-log.model.ts @@ -1,6 +1,6 @@ import { safeUnescape } from 'app/shared/util/security.util'; import { Annotation } from 'app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; export enum BuildLogType { ERROR = 'ERROR', diff --git a/src/main/webapp/app/entities/build-plan-checkout-directories-dto.ts b/src/main/webapp/app/entities/programming/build-plan-checkout-directories-dto.ts similarity index 100% rename from src/main/webapp/app/entities/build-plan-checkout-directories-dto.ts rename to src/main/webapp/app/entities/programming/build-plan-checkout-directories-dto.ts diff --git a/src/main/webapp/app/entities/build-plan.model.ts b/src/main/webapp/app/entities/programming/build-plan.model.ts similarity index 71% rename from src/main/webapp/app/entities/build-plan.model.ts rename to src/main/webapp/app/entities/programming/build-plan.model.ts index f1f79e0e1e9f..cb6abecf235d 100644 --- a/src/main/webapp/app/entities/build-plan.model.ts +++ b/src/main/webapp/app/entities/programming/build-plan.model.ts @@ -1,5 +1,5 @@ import { BaseEntity } from 'app/shared/model/base-entity'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class BuildPlan implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/programming/build.action.ts b/src/main/webapp/app/entities/programming/build.action.ts new file mode 100644 index 000000000000..5e9e3e4463a1 --- /dev/null +++ b/src/main/webapp/app/entities/programming/build.action.ts @@ -0,0 +1,19 @@ +import { AeolusResult } from 'app/entities/programming/aeolus.result'; + +export class BuildAction { + name: string; + runAlways: boolean; + workdir: string; + results?: AeolusResult[]; + platform?: string; + parameters: Map = new Map(); +} + +export class ScriptAction extends BuildAction { + script: string; +} + +export class PlatformAction extends BuildAction { + type: string; + kind: string; +} diff --git a/src/main/webapp/app/entities/checkout-directories-dto.ts b/src/main/webapp/app/entities/programming/checkout-directories-dto.ts similarity index 63% rename from src/main/webapp/app/entities/checkout-directories-dto.ts rename to src/main/webapp/app/entities/programming/checkout-directories-dto.ts index 71b05a32d187..657fbce65f6f 100644 --- a/src/main/webapp/app/entities/checkout-directories-dto.ts +++ b/src/main/webapp/app/entities/programming/checkout-directories-dto.ts @@ -1,4 +1,4 @@ -import { BuildPlanCheckoutDirectoriesDTO } from 'app/entities/build-plan-checkout-directories-dto'; +import { BuildPlanCheckoutDirectoriesDTO } from 'app/entities/programming/build-plan-checkout-directories-dto'; export class CheckoutDirectoriesDto { submissionBuildPlanCheckoutDirectories?: BuildPlanCheckoutDirectoriesDTO; diff --git a/src/main/webapp/app/entities/programming/docker.configuration.ts b/src/main/webapp/app/entities/programming/docker.configuration.ts new file mode 100644 index 000000000000..edc08214185c --- /dev/null +++ b/src/main/webapp/app/entities/programming/docker.configuration.ts @@ -0,0 +1,6 @@ +export class DockerConfiguration { + image: string; + tag?: string; + volumes: Map; + parameters: Map; +} diff --git a/src/main/webapp/app/entities/programming-exercise-auxiliary-repository-model.ts b/src/main/webapp/app/entities/programming/programming-exercise-auxiliary-repository-model.ts similarity index 100% rename from src/main/webapp/app/entities/programming-exercise-auxiliary-repository-model.ts rename to src/main/webapp/app/entities/programming/programming-exercise-auxiliary-repository-model.ts diff --git a/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts b/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts new file mode 100644 index 000000000000..eaa526406e08 --- /dev/null +++ b/src/main/webapp/app/entities/programming/programming-exercise-build.config.ts @@ -0,0 +1,19 @@ +import { WindFile } from 'app/entities/programming/wind.file'; + +export class ProgrammingExerciseBuildConfig { + public sequentialTestRuns?: boolean; + public buildPlanConfiguration?: string; + public buildScript?: string; + public checkoutSolutionRepository?: boolean; + public checkoutPath?: string; + public timeoutSeconds?: number; + public dockerFlags?: string; + public windfile?: WindFile; + public testwiseCoverageEnabled?: boolean; + public theiaImage?: string; + + constructor() { + this.checkoutSolutionRepository = false; // default value + this.testwiseCoverageEnabled = false; // default value + } +} diff --git a/src/main/webapp/app/entities/programming-exercise-participation.model.ts b/src/main/webapp/app/entities/programming/programming-exercise-participation.model.ts similarity index 100% rename from src/main/webapp/app/entities/programming-exercise-participation.model.ts rename to src/main/webapp/app/entities/programming/programming-exercise-participation.model.ts diff --git a/src/main/webapp/app/entities/programming-exercise-test-case-statistics.model.ts b/src/main/webapp/app/entities/programming/programming-exercise-test-case-statistics.model.ts similarity index 100% rename from src/main/webapp/app/entities/programming-exercise-test-case-statistics.model.ts rename to src/main/webapp/app/entities/programming/programming-exercise-test-case-statistics.model.ts diff --git a/src/main/webapp/app/entities/programming-exercise-test-case.model.ts b/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts similarity index 92% rename from src/main/webapp/app/entities/programming-exercise-test-case.model.ts rename to src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts index 7b0cdfa37311..2d6822dc7ce4 100644 --- a/src/main/webapp/app/entities/programming-exercise-test-case.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise-test-case.model.ts @@ -1,4 +1,4 @@ -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { TestCaseStats } from './programming-exercise-test-case-statistics.model'; diff --git a/src/main/webapp/app/entities/programming-exercise.model.ts b/src/main/webapp/app/entities/programming/programming-exercise.model.ts similarity index 76% rename from src/main/webapp/app/entities/programming-exercise.model.ts rename to src/main/webapp/app/entities/programming/programming-exercise.model.ts index a2e6a093f9a1..3d47a8320bd2 100644 --- a/src/main/webapp/app/entities/programming-exercise.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise.model.ts @@ -1,79 +1,16 @@ -import dayjs from 'dayjs/esm'; -import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; -import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; -import { Exercise, ExerciseType, resetForImport } from 'app/entities/exercise.model'; +import { AssessmentType } from 'app/entities/assessment-type.model'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; -import { SubmissionPolicy } from 'app/entities/submission-policy.model'; -import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { Exercise, ExerciseType, resetForImport } from 'app/entities/exercise.model'; import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { BuildLogStatisticsDTO } from 'app/entities/build-log-statistics-dto'; -import { AssessmentType } from 'app/entities/assessment-type.model'; - -export class BuildAction { - name: string; - runAlways: boolean; - workdir: string; - results?: AeolusResult[]; - platform?: string; - parameters: Map = new Map(); -} - -export class AeolusResult { - name: string; - path: string; - ignore: string; - type?: string; - before?: boolean; -} - -export class ScriptAction extends BuildAction { - script: string; -} - -export class PlatformAction extends BuildAction { - type: string; - kind: string; -} - -export class WindMetadata { - author: string | any; - description: string; - id: string; - name: string; - docker: DockerConfiguration; -} - -export class DockerConfiguration { - image: string; - tag?: string; - volumes: Map; - parameters: Map; -} - -export class WindFile { - api: string; - metadata: WindMetadata; - actions: BuildAction[]; -} - -export class ProgrammingExerciseBuildConfig { - public sequentialTestRuns?: boolean; - public buildPlanConfiguration?: string; - public buildScript?: string; - public checkoutSolutionRepository?: boolean; - public checkoutPath?: string; - public timeoutSeconds?: number; - public dockerFlags?: string; - public windfile?: WindFile; - public testwiseCoverageEnabled?: boolean; - - constructor() { - this.checkoutSolutionRepository = false; // default value - this.testwiseCoverageEnabled = false; // default value - } -} +import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; +import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; +import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; +import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; +import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; +import { SubmissionPolicy } from 'app/entities/submission-policy.model'; +import dayjs from 'dayjs/esm'; export enum ProgrammingLanguage { JAVA = 'JAVA', @@ -114,6 +51,7 @@ export class ProgrammingExercise extends Exercise { */ public maxStaticCodeAnalysisPenalty?: number; public allowOfflineIde?: boolean; + public allowOnlineIde?: boolean; public programmingLanguage?: ProgrammingLanguage; public packageName?: string; public showTestNamesToStudents?: boolean; @@ -148,6 +86,7 @@ export class ProgrammingExercise extends Exercise { this.templateParticipation = new TemplateProgrammingExerciseParticipation(); this.solutionParticipation = new SolutionProgrammingExerciseParticipation(); this.allowOnlineEditor = false; // default value + this.allowOnlineIde = false; // default value this.staticCodeAnalysisEnabled = false; // default value this.allowOfflineIde = true; // default value this.programmingLanguage = ProgrammingLanguage.JAVA; // default value diff --git a/src/main/webapp/app/entities/programming-submission.model.ts b/src/main/webapp/app/entities/programming/programming-submission.model.ts similarity index 100% rename from src/main/webapp/app/entities/programming-submission.model.ts rename to src/main/webapp/app/entities/programming/programming-submission.model.ts diff --git a/src/main/webapp/app/entities/repository-info.model.ts b/src/main/webapp/app/entities/programming/repository-info.model.ts similarity index 100% rename from src/main/webapp/app/entities/repository-info.model.ts rename to src/main/webapp/app/entities/programming/repository-info.model.ts diff --git a/src/main/webapp/app/entities/static-code-analysis-category.model.ts b/src/main/webapp/app/entities/programming/static-code-analysis-category.model.ts similarity index 100% rename from src/main/webapp/app/entities/static-code-analysis-category.model.ts rename to src/main/webapp/app/entities/programming/static-code-analysis-category.model.ts diff --git a/src/main/webapp/app/entities/static-code-analysis-issue.model.ts b/src/main/webapp/app/entities/programming/static-code-analysis-issue.model.ts similarity index 100% rename from src/main/webapp/app/entities/static-code-analysis-issue.model.ts rename to src/main/webapp/app/entities/programming/static-code-analysis-issue.model.ts diff --git a/src/main/webapp/app/entities/test-case-result.model.ts b/src/main/webapp/app/entities/programming/test-case-result.model.ts similarity index 100% rename from src/main/webapp/app/entities/test-case-result.model.ts rename to src/main/webapp/app/entities/programming/test-case-result.model.ts diff --git a/src/main/webapp/app/entities/programming/wind.file.ts b/src/main/webapp/app/entities/programming/wind.file.ts new file mode 100644 index 000000000000..6e960c30c44c --- /dev/null +++ b/src/main/webapp/app/entities/programming/wind.file.ts @@ -0,0 +1,8 @@ +import { BuildAction } from 'app/entities/programming/build.action'; +import { WindMetadata } from 'app/entities/programming/wind.metadata'; + +export class WindFile { + api: string; + metadata: WindMetadata; + actions: BuildAction[]; +} diff --git a/src/main/webapp/app/entities/programming/wind.metadata.ts b/src/main/webapp/app/entities/programming/wind.metadata.ts new file mode 100644 index 000000000000..6ef6f1abc38e --- /dev/null +++ b/src/main/webapp/app/entities/programming/wind.metadata.ts @@ -0,0 +1,9 @@ +import { DockerConfiguration } from 'app/entities/programming/docker.configuration'; + +export class WindMetadata { + author: string | any; + description: string; + id: string; + name: string; + docker: DockerConfiguration; +} diff --git a/src/main/webapp/app/entities/quiz/quiz-pool.model.ts b/src/main/webapp/app/entities/quiz/quiz-pool.model.ts index b6d7f82b7a83..8b59ba5473c3 100644 --- a/src/main/webapp/app/entities/quiz/quiz-pool.model.ts +++ b/src/main/webapp/app/entities/quiz/quiz-pool.model.ts @@ -1,6 +1,6 @@ import { QuizGroup } from 'app/entities/quiz/quiz-group.model'; import { QuizQuestion } from 'app/entities/quiz/quiz-question.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { BaseEntity } from 'app/shared/model/base-entity'; export class QuizPool implements BaseEntity { diff --git a/src/main/webapp/app/entities/student-exam.model.ts b/src/main/webapp/app/entities/student-exam.model.ts index 1b15503b27cd..b7eb32c151d6 100644 --- a/src/main/webapp/app/entities/student-exam.model.ts +++ b/src/main/webapp/app/entities/student-exam.model.ts @@ -1,9 +1,9 @@ import dayjs from 'dayjs/esm'; import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Exercise } from 'app/entities/exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; -import { ExamSession } from 'app/entities/exam-session.model'; +import { ExamSession } from 'app/entities/exam/exam-session.model'; export class StudentExam implements BaseEntity { public id?: number; diff --git a/src/main/webapp/app/entities/submission-policy.model.ts b/src/main/webapp/app/entities/submission-policy.model.ts index 9ae55f0d465e..c6f0d9620f4b 100644 --- a/src/main/webapp/app/entities/submission-policy.model.ts +++ b/src/main/webapp/app/entities/submission-policy.model.ts @@ -1,4 +1,4 @@ -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BaseEntity } from 'app/shared/model/base-entity'; export enum SubmissionPolicyType { diff --git a/src/main/webapp/app/entities/text-assesment-event.model.ts b/src/main/webapp/app/entities/text/text-assesment-event.model.ts similarity index 95% rename from src/main/webapp/app/entities/text-assesment-event.model.ts rename to src/main/webapp/app/entities/text/text-assesment-event.model.ts index c0b04482f1a2..478f39d62ccf 100644 --- a/src/main/webapp/app/entities/text-assesment-event.model.ts +++ b/src/main/webapp/app/entities/text/text-assesment-event.model.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs/esm'; import { BaseEntity } from 'app/shared/model/base-entity'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; export enum TextAssessmentEventType { ADD_FEEDBACK_AUTOMATICALLY_SELECTED_BLOCK = 'ADD_FEEDBACK_AUTOMATICALLY_SELECTED_BLOCK', diff --git a/src/main/webapp/app/entities/text-block-ref.model.ts b/src/main/webapp/app/entities/text/text-block-ref.model.ts similarity index 91% rename from src/main/webapp/app/entities/text-block-ref.model.ts rename to src/main/webapp/app/entities/text/text-block-ref.model.ts index b31b08353cf3..b643652a939e 100644 --- a/src/main/webapp/app/entities/text-block-ref.model.ts +++ b/src/main/webapp/app/entities/text/text-block-ref.model.ts @@ -1,4 +1,4 @@ -import { TextBlock, TextBlockType } from 'app/entities/text-block.model'; +import { TextBlock, TextBlockType } from 'app/entities/text/text-block.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; export class TextBlockRef { diff --git a/src/main/webapp/app/entities/text-block.model.ts b/src/main/webapp/app/entities/text/text-block.model.ts similarity index 96% rename from src/main/webapp/app/entities/text-block.model.ts rename to src/main/webapp/app/entities/text/text-block.model.ts index 4267639950e8..034c590439b1 100644 --- a/src/main/webapp/app/entities/text-block.model.ts +++ b/src/main/webapp/app/entities/text/text-block.model.ts @@ -1,5 +1,5 @@ import { sha1Hex } from 'app/shared/util/crypto.utils'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; export enum TextBlockType { AUTOMATIC = 'AUTOMATIC', diff --git a/src/main/webapp/app/entities/text-change.model.ts b/src/main/webapp/app/entities/text/text-change.model.ts similarity index 100% rename from src/main/webapp/app/entities/text-change.model.ts rename to src/main/webapp/app/entities/text/text-change.model.ts diff --git a/src/main/webapp/app/entities/text-exercise.model.ts b/src/main/webapp/app/entities/text/text-exercise.model.ts similarity index 100% rename from src/main/webapp/app/entities/text-exercise.model.ts rename to src/main/webapp/app/entities/text/text-exercise.model.ts diff --git a/src/main/webapp/app/entities/text-submission.model.ts b/src/main/webapp/app/entities/text/text-submission.model.ts similarity index 84% rename from src/main/webapp/app/entities/text-submission.model.ts rename to src/main/webapp/app/entities/text/text-submission.model.ts index 941e239a1a1f..922987ed7a5b 100644 --- a/src/main/webapp/app/entities/text-submission.model.ts +++ b/src/main/webapp/app/entities/text/text-submission.model.ts @@ -1,4 +1,4 @@ -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { Submission, SubmissionExerciseType } from 'app/entities/submission.model'; import { Language } from 'app/entities/course.model'; diff --git a/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts b/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts index 49a8462bc98d..e33354d96359 100644 --- a/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts +++ b/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts @@ -1,5 +1,5 @@ import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; diff --git a/src/main/webapp/app/exam/manage/exam-management.component.ts b/src/main/webapp/app/exam/manage/exam-management.component.ts index 73b0b4c6ed85..bd55ca990c1c 100644 --- a/src/main/webapp/app/exam/manage/exam-management.component.ts +++ b/src/main/webapp/app/exam/manage/exam-management.component.ts @@ -3,14 +3,14 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ActivatedRoute, Router } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { AccountService } from 'app/core/auth/account.service'; import { SortService } from 'app/shared/service/sort.service'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; import dayjs from 'dayjs/esm'; import { EventManager } from 'app/core/util/event-manager.service'; import { faClipboard, faEye, faFileImport, faListAlt, faPlus, faSort, faThList, faTimes, faUser, faWrench } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exam/manage/exam-management.service.ts b/src/main/webapp/app/exam/manage/exam-management.service.ts index 24fa83f28e40..5f2d4c8f8397 100644 --- a/src/main/webapp/app/exam/manage/exam-management.service.ts +++ b/src/main/webapp/app/exam/manage/exam-management.service.ts @@ -1,18 +1,18 @@ import { Injectable } from '@angular/core'; -import { ExamUserDTO } from 'app/entities/exam-user-dto.model'; -import { ExamUserAttendanceCheckDTO } from 'app/entities/exam-users-attendance-check-dto.model'; +import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; +import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; import { filter, map, tap } from 'rxjs/operators'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import dayjs from 'dayjs/esm'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { createRequestOption } from 'app/shared/util/request.util'; import { StudentDTO } from 'app/entities/student-dto.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExamScoreDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.model'; import { Submission, reconnectSubmissions } from 'app/entities/submission.model'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/exam/manage/exam-status.component.ts b/src/main/webapp/app/exam/manage/exam-status.component.ts index 268e12d47d66..fafa2f201137 100644 --- a/src/main/webapp/app/exam/manage/exam-status.component.ts +++ b/src/main/webapp/app/exam/manage/exam-status.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { faArrowRight, faCheckCircle, faCircleExclamation, faDotCircle, faTimes, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamChecklistService } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.service'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import dayjs from 'dayjs/esm'; import { round } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts index 1abff9c1f9ea..826a85b0a83d 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts @@ -4,7 +4,7 @@ import { faBullhorn } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { Subscription, from } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AlertService } from 'app/core/util/alert.service'; import { ExamLiveAnnouncementCreateModalComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts index 5a2a236c2396..f873b396b058 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; -import { Exam } from 'app/entities/exam.model'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { Exam } from 'app/entities/exam/exam.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { faChartBar, faEye, faListAlt, faThList, faUser, faWrench } from '@fortawesome/free-solid-svg-icons'; import { ExamChecklistService } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts index fc85375c8e28..ac6133a43d76 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts @@ -1,7 +1,7 @@ import { HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts index 8eb453d1a091..a1bb4e17c565 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts @@ -3,7 +3,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { faBan, faCheck, faSpinner } from '@fortawesome/free-solid-svg-icons'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { examWorkingTime } from 'app/exam/participate/exam.utils'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts index 5433a4e48887..4024634666c9 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts @@ -4,7 +4,7 @@ import { faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { Subscription, from } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AlertService } from 'app/core/util/alert.service'; import { ExamEditWorkingTimeDialogComponent } from './exam-edit-working-time-dialog.component'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts b/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts index 582e72ad65d5..21638aa00238 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { SafeHtml } from '@angular/platform-browser'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Subject } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { ButtonSize } from 'app/shared/components/button.component'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts b/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts index 342c7b6fb1c1..9739343cb687 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { faCheckDouble, faFont } from '@fortawesome/free-solid-svg-icons'; import { Exercise, ExerciseType, getIcon } from 'app/entities/exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts index e9b8976146f3..ca9bcbf3f1fc 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { PagingService } from 'app/exercises/shared/manage/paging.service'; import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; import { Observable } from 'rxjs'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts index 35a04a6c96cc..f97c8c3d537b 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts @@ -3,7 +3,7 @@ import { Component, Input, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExamExerciseImportComponent } from 'app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts index 946654f067f4..c022b55ec428 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; @Component({ selector: 'jhi-exam-mode-picker', diff --git a/src/main/webapp/app/exam/manage/exams/exam-update.component.ts b/src/main/webapp/app/exam/manage/exams/exam-update.component.ts index d9068bff7473..ea4d3a510ec4 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-update.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-update.component.ts @@ -6,7 +6,7 @@ import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/c import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { faBan, faExclamationTriangle, faSave } from '@fortawesome/free-solid-svg-icons'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; diff --git a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts index bfee12779e25..7acfb1fac6a6 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts @@ -5,7 +5,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { Observable } from 'rxjs'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { onError } from 'app/shared/util/global.utils'; import { faBan, faSave } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts index b2c563ecbfd1..8fcac15d391b 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts @@ -10,10 +10,10 @@ import { onError } from 'app/shared/util/global.utils'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; diff --git a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.html b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.html index 7194d5a9f141..e59af00e81b9 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.html +++ b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.html @@ -84,12 +84,13 @@ @if (displayEditorModus) {
- {{ 'artemisApp.programmingExercise.offlineIde' | artemisTranslate }} - : {{ programmingExercise.allowOfflineIde || false }} + : {{ programmingExercise.allowOfflineIde || false }}
- {{ 'artemisApp.programmingExercise.onlineEditor' | artemisTranslate }} - : {{ programmingExercise.allowOnlineEditor || false }} + : {{ programmingExercise.allowOnlineEditor || false }} +
+
+ : {{ programmingExercise.allowOnlineIde || false }}
} diff --git a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts index 949ab1482efe..a2b6d64ca6e0 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { Exercise } from 'app/entities/exercise.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { createBuildPlanUrl } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts index 7d489674a1e0..7eafe4900d96 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize } from 'app/shared/components/button.component'; import { GitDiffReportModalComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report-modal.component'; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts index f351e89940b8..1b54bd14ff8a 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts @@ -2,14 +2,14 @@ import { AfterViewInit, Component, OnDestroy, OnInit, QueryList, ViewChild, View import { ActivatedRoute } from '@angular/router'; import { StudentExam } from 'app/entities/student-exam.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ExamPage } from 'app/entities/exam-page.model'; +import { ExamPage } from 'app/entities/exam/exam-page.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; import { ExamNavigationBarComponent } from 'app/exam/participate/exam-navigation-bar/exam-navigation-bar.component'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; import dayjs from 'dayjs/esm'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { Observable, Subscription, forkJoin, map, mergeMap, toArray } from 'rxjs'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Submission } from 'app/entities/submission.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; import { FileUploadExamSubmissionComponent } from 'app/exam/participate/exercises/file-upload/file-upload-exam-submission.component'; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts index 8ca3aed74f49..f10fc54ecf2a 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts @@ -10,7 +10,7 @@ import { Course } from 'app/entities/course.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { AlertService } from 'app/core/util/alert.service'; import { HttpErrorResponse } from '@angular/common/http'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import dayjs from 'dayjs/esm'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/exam/manage/students/exam-students.component.ts b/src/main/webapp/app/exam/manage/students/exam-students.component.ts index 43e72be1cbe3..ccc0f9e42671 100644 --- a/src/main/webapp/app/exam/manage/students/exam-students.component.ts +++ b/src/main/webapp/app/exam/manage/students/exam-students.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ExamUser } from 'app/entities/exam-user.model'; +import { ExamUser } from 'app/entities/exam/exam-user.model'; import { Observable, Subject, Subscription, of } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { User } from 'app/core/user/user.model'; @@ -9,7 +9,7 @@ import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { UserService } from 'app/core/user/user.service'; import { DataTableComponent } from 'app/shared/data-table/data-table.component'; import { iconsAsHTML } from 'app/utils/icons.utils'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts index 5f2cee93c4bf..16b03fe9bbcd 100644 --- a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts +++ b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { StudentsUploadImagesDialogComponent } from 'app/exam/manage/students/upload-images/students-upload-images-dialog.component'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { faPlus, faUpload } from '@fortawesome/free-solid-svg-icons'; @Component({ diff --git a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts index 3208de448eb1..f94e4b9f836c 100644 --- a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts +++ b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts @@ -5,7 +5,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { HttpErrorResponse } from '@angular/common/http'; import { Subject } from 'rxjs'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { faArrowRight, faBan, faCheck, faCircleNotch, faSpinner, faUpload } from '@fortawesome/free-solid-svg-icons'; import { onError } from 'app/shared/util/global.utils'; diff --git a/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts b/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts index 1d78d6e3806e..e8cfdfa14426 100644 --- a/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts +++ b/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts @@ -1,12 +1,12 @@ import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ExamUserAttendanceCheckDTO } from 'app/entities/exam-users-attendance-check-dto.model'; +import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; import { SortService } from 'app/shared/service/sort.service'; import { Subject, Subscription } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { UserService } from 'app/core/user/user.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts index 18cf6ea1d227..f29b03f4d428 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { Exercise } from 'app/entities/exercise.model'; -import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam/exam-session.model'; import { SuspiciousSessionsService } from 'app/exam/manage/suspicious-behavior/suspicious-sessions.service'; import { ActivatedRoute, Router } from '@angular/router'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts index d24b473ec63c..01189459c644 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model'; import { cloneDeep } from 'lodash-es'; @Component({ diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions.service.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions.service.ts index 4f104cb23de8..8c526b0461ba 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions.service.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam/exam-session.model'; import { Observable } from 'rxjs'; @Injectable({ diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts index 05885359d821..860dd819fd01 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model'; import { StudentExam } from 'app/entities/student-exam.model'; @Component({ diff --git a/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts b/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts index 829ef039fd9a..a43fa4a9e41c 100644 --- a/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts +++ b/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Exercise } from 'app/entities/exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { FormControl, FormGroup, Validators } from '@angular/forms'; diff --git a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts index b10eb427b1dd..06d0196d6ba9 100644 --- a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts +++ b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { SortService } from 'app/shared/service/sort.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; diff --git a/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts b/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts index 1b44965d5241..daf2532e3b15 100644 --- a/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts @@ -7,7 +7,7 @@ import { ExamParticipationService } from 'app/exam/participate/exam-participatio import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { faDoorClosed } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; @Component({ diff --git a/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts b/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts index eeb5bc50f9d4..090588335574 100644 --- a/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts +++ b/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts @@ -3,7 +3,7 @@ import { SafeHtml } from '@angular/platform-browser'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { TranslateService } from '@ngx-translate/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Course } from 'app/entities/course.model'; import { AccountService } from 'app/core/auth/account.service'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; diff --git a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts index e73b90d55907..800285feeda1 100644 --- a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { LayoutService } from 'app/shared/breakpoints/layout.service'; import { CustomBreakpointNames } from 'app/shared/breakpoints/breakpoints.service'; import dayjs from 'dayjs/esm'; @@ -12,9 +12,9 @@ import { CommitState, DomainChange, DomainType } from 'app/exercises/programming import { CodeEditorRepositoryService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { map } from 'rxjs/operators'; import { CodeEditorConflictStateService } from 'app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service'; -import { ExamSession } from 'app/entities/exam-session.model'; +import { ExamSession } from 'app/entities/exam/exam-session.model'; import { faBars, faCheck, faEdit } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; diff --git a/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts b/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts index 61408881fe77..93bcecfa27ae 100644 --- a/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts @@ -6,9 +6,9 @@ import { Subscription } from 'rxjs'; import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; import { SidebarData } from 'app/types/sidebar'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { ExamSession } from 'app/entities/exam-session.model'; +import { ExamSession } from 'app/entities/exam/exam-session.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; import { CodeEditorRepositoryService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; @@ -17,7 +17,7 @@ import { ExamExerciseUpdateService } from 'app/exam/manage/exam-exercise-update. import { ButtonTooltipType, ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { map } from 'rxjs/operators'; import { CommitState, DomainChange, DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faChevronRight, faFileLines, faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; import { facSaveSuccess, facSaveWarning } from '../../../../content/icons/icons'; diff --git a/src/main/webapp/app/exam/participate/exam-participation.component.ts b/src/main/webapp/app/exam/participate/exam-participation.component.ts index 18b5eec1daf5..a26170db25ca 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.component.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.component.ts @@ -5,30 +5,30 @@ import { ExamParticipationService } from 'app/exam/participate/exam-participatio import { StudentExam } from 'app/entities/student-exam.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { ModelingSubmissionService } from 'app/exercises/modeling/participate/modeling-submission.service'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; import { Submission } from 'app/entities/submission.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { BehaviorSubject, Observable, Subject, Subscription, of, throwError } from 'rxjs'; import { catchError, distinctUntilChanged, filter, map, tap, throttleTime, timeout } from 'rxjs/operators'; import { InitializationState } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { TranslateService } from '@ngx-translate/core'; import { AlertService } from 'app/core/util/alert.service'; import dayjs from 'dayjs/esm'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { cloneDeep } from 'lodash-es'; import { Course } from 'app/entities/course.model'; import { captureException } from '@sentry/angular'; import { HttpErrorResponse } from '@angular/common/http'; -import { ExamPage } from 'app/entities/exam-page.model'; +import { ExamPage } from 'app/entities/exam/exam-page.model'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { AUTOSAVE_CHECK_INTERVAL, AUTOSAVE_EXERCISE_INTERVAL } from 'app/shared/constants/exercise-exam-constants'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; diff --git a/src/main/webapp/app/exam/participate/exam-participation.service.ts b/src/main/webapp/app/exam/participate/exam-participation.service.ts index a790afa7a361..5e125f6b1288 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.service.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.service.ts @@ -2,7 +2,7 @@ import { HttpClient, HttpErrorResponse, HttpParams, HttpResponse } from '@angula import { Injectable } from '@angular/core'; import { faLightbulb } from '@fortawesome/free-solid-svg-icons'; import { captureException } from '@sentry/angular'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise, ExerciseType, getIcon } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts b/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts index eafff2d69467..33237b989106 100644 --- a/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts +++ b/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { InformationBox, InformationBoxComponent } from 'app/shared/information-box/information-box.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; import dayjs from 'dayjs/esm'; diff --git a/src/main/webapp/app/exam/participate/exam.utils.ts b/src/main/webapp/app/exam/participate/exam.utils.ts index fecd529e9a80..1070606f377c 100644 --- a/src/main/webapp/app/exam/participate/exam.utils.ts +++ b/src/main/webapp/app/exam/participate/exam.utils.ts @@ -1,4 +1,4 @@ -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; import dayjs from 'dayjs/esm'; import { round } from 'app/shared/util/utils'; diff --git a/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts b/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts index 9e9c12dc8148..483891aba2c1 100644 --- a/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, O import { Exercise, ExerciseType, getIcon, getIconTooltip } from 'app/entities/exercise.model'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { StudentExam } from 'app/entities/student-exam.model'; -import { ExamExerciseOverviewItem } from 'app/entities/exam-exercise-overview-item.model'; +import { ExamExerciseOverviewItem } from 'app/entities/exam/exam-exercise-overview-item.model'; import { ButtonTooltipType, ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; import { facSaveSuccess, facSaveWarning } from '../../../../../content/icons/icons'; diff --git a/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts index 2179d1ba37c6..7b18cda9e20a 100644 --- a/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts @@ -3,7 +3,7 @@ import { Submission } from 'app/entities/submission.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CommitState, DomainType, EditorState } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { Exercise, ExerciseType, IncludedInOverallScore, getCourseFromExercise } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts index 6b7359bab58b..3a493af08507 100644 --- a/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { TextEditorService } from 'app/exercises/text/participate/text-editor.service'; import { Subject } from 'rxjs'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; import { Exercise, ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; diff --git a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts index fb79c9e8ffab..89dc3774bf64 100644 --- a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts +++ b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges } from '@angular/core'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { endTime, examWorkingTime, getAdditionalWorkingTime, isExamOverMultipleDays } from 'app/exam/participate/exam.utils'; import dayjs from 'dayjs/esm'; diff --git a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts index ef25fa45c928..dc3af180c411 100644 --- a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts @@ -4,7 +4,7 @@ import { Exercise, ExerciseType, IncludedInOverallScore, getIcon } from 'app/ent import dayjs from 'dayjs/esm'; import { ActivatedRoute } from '@angular/router'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { ThemeService } from 'app/core/theme/theme.service'; import { ExerciseResult, StudentExamWithGradeDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; @@ -22,7 +22,7 @@ import { faArrowUp, faEye, faEyeSlash, faFolderOpen, faInfoCircle, faPrint } fro import { cloneDeep } from 'lodash-es'; import { captureException } from '@sentry/angular'; import { AlertService } from 'app/core/util/alert.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { isExamResultPublished } from 'app/exam/participate/exam.utils'; import { Course } from 'app/entities/course.model'; diff --git a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts index 6ba27112ac6b..2243c413644a 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts @@ -1,9 +1,9 @@ import { Component, Input, OnInit, Optional } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseType } from 'app/entities/exercise.model'; import { MissingResultInformation, evaluateTemplateStatus } from 'app/exercises/shared/result/result.utils'; import { FeedbackComponentPreparedParams, prepareFeedbackComponentParameters } from 'app/exercises/shared/feedback/feedback.utils'; diff --git a/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts index d1c71b7c9349..217925a8e230 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts @@ -9,7 +9,7 @@ import { MultipleChoiceSubmittedAnswer } from 'app/entities/quiz/multiple-choice import { DragAndDropSubmittedAnswer } from 'app/entities/quiz/drag-and-drop-submitted-answer.model'; import { ShortAnswerSubmittedAnswer } from 'app/entities/quiz/short-answer-submitted-answer.model'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { Result } from 'app/entities/result.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; diff --git a/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts index 5ea1bf223f68..a1dcf5c9de34 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Exercise } from 'app/entities/exercise.model'; @Component({ diff --git a/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts b/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts index a343ac1c2743..4d90cf31e917 100644 --- a/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts +++ b/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { round } from 'app/shared/util/utils'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; import { getRelativeWorkingTimeExtension } from 'app/exam/participate/exam.utils'; diff --git a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.ts b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.ts index 6ed273f43d6f..ca4b299f55bd 100644 --- a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.ts @@ -8,13 +8,13 @@ import { ButtonSize } from 'app/shared/components/button.component'; import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { ExerciseType, IncludedInOverallScore, getCourseFromExercise } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { Complaint } from 'app/entities/complaint.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ProgrammingAssessmentManualResultService } from 'app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Location } from '@angular/common'; import { AccountService } from 'app/core/auth/account.service'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; diff --git a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts index ab616e7037ca..9a2b5af07fb0 100644 --- a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts @@ -4,7 +4,7 @@ import { ProgrammingAssessmentRepoExportDialogComponent } from 'app/exercises/pr import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { faDownload } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; @Component({ selector: 'jhi-programming-assessment-repo-export', diff --git a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts index a33b555747b7..e61c909e6893 100644 --- a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts @@ -4,7 +4,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingAssessmentRepoExportService, RepositoryExportOptions } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service'; import { HttpResponse } from '@angular/common/http'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { downloadZipFileFromResponse } from 'app/shared/util/download.util'; import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts index 56d9dffe846e..d039db390a19 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { CodeHint, CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts index 71bd928ba2ed..0df03f1664b3 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts index 254537ce8ad6..59e8150e2fe8 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; import { faWrench } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; import { AlertService } from 'app/core/util/alert.service'; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts index 44658811f194..0fdc47306615 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AlertService } from 'app/core/util/alert.service'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts index 2c2a2929fdfd..3903491a1477 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; diff --git a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts index 13a0b57ec028..5d4b3c68f675 100644 --- a/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts +++ b/src/main/webapp/app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component.ts @@ -3,8 +3,8 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { SolutionEntryDetailsModalComponent } from 'app/exercises/programming/hestia/generation-overview/solution-entry-details-modal/solution-entry-details-modal.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExerciseTestCaseType } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExerciseTestCaseType } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AlertService } from 'app/core/util/alert.service'; import { Subject } from 'rxjs'; import { faSort, faSortDown, faSortUp, faTimes } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts b/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts index 9aca74dfa4ef..b160bf88b2be 100644 --- a/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts @@ -3,9 +3,9 @@ import { faCircleNotch, faPlayCircle } from '@fortawesome/free-solid-svg-icons'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { BuildPlanService } from 'app/exercises/programming/manage/services/build-plan.service'; -import { BuildPlan } from 'app/entities/build-plan.model'; +import { BuildPlan } from 'app/entities/programming/build-plan.model'; import { ActivatedRoute } from '@angular/router'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; diff --git a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts index 3dad17de0cf1..e53bf800e1a9 100644 --- a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts @@ -12,7 +12,7 @@ import { TemplateProgrammingExerciseParticipation } from 'app/entities/participa import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { DomainChange, DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts index 27aeb8f9ff8d..236a85bf0cce 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { IssuesMap } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; +import { IssuesMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; export class IssueColumn { w: string; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts index 59c58c9fc817..58e2d7aa9dd3 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; -import { CategoryIssuesMap } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; +import { CategoryIssuesMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { TranslateService } from '@ngx-translate/core'; import { getColor } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.utils'; import { ProgrammingGradingChartsDirective } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.directive'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts index 5aceb9c57804..568402289fca 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { TestCaseStatsMap } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { TestCaseStatsMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { TranslateService } from '@ngx-translate/core'; import { getColor } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.utils'; import { ProgrammingGradingChartsDirective } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.directive'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts index d7c9dadfe0ed..03e4a076bc58 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { TestCaseStats } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { round } from 'app/shared/util/utils'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts index fec4e31523f6..6c91acef5676 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; /** * The actions of the grading page: diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts index 796b2728a4a6..c5ef675338f5 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts @@ -7,10 +7,10 @@ import { AccountService } from 'app/core/auth/account.service'; import { AlertService } from 'app/core/util/alert.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Course } from 'app/entities/course.model'; -import { IssuesMap, ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; +import { IssuesMap, ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; import { SubmissionPolicy, SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { ProgrammingGradingChartsDirective } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.directive'; import { ProgrammingExerciseGradingService, StaticCodeAnalysisCategoryUpdate } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts index 36fcfeb230b6..7e5156faaa52 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { faSave } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { ButtonType } from 'app/shared/components/button.component'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts index 714ef84c2beb..ae956e5b7a96 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { faCopy } from '@fortawesome/free-solid-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingTab } from 'app/exercises/programming/manage/grading/programming-exercise-configure-grading.component'; import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts index a40d38cade8e..bd491c999df9 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts @@ -1,12 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { ProgrammingExerciseTaskService } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { faAngleDown, faAngleRight, faAsterisk, faMedal, faQuestionCircle, faScaleUnbalanced, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { ProgrammingExerciseTask } from './programming-exercise-task'; import { Observable, Subject } from 'rxjs'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { isExamExercise } from 'app/shared/util/utils'; import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts index 508c1e4d21bf..5f45d551b476 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts @@ -4,11 +4,11 @@ import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programmi import { Observable, catchError, of, tap } from 'rxjs'; import { Exercise } from 'app/entities/exercise.model'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; -import { ProgrammingExerciseGradingStatistics, TestCaseStats } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseGradingStatistics, TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExerciseGradingService, ProgrammingExerciseTestCaseUpdate } from '../../services/programming-exercise-grading.service'; import { AlertService } from 'app/core/util/alert.service'; import { map, mergeMap } from 'rxjs/operators'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts index cff99aac5cc2..3a5844ddbb9e 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.ts @@ -1,6 +1,6 @@ import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; -import { TestCaseStats } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; export class ProgrammingExerciseTask extends ProgrammingExerciseServerSideTask { declare testCases: ProgrammingExerciseTestCase[]; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts index d8c77b50c8df..837c0210f76d 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExerciseTaskService } from '../programming-exercise-task.service'; import { Subject } from 'rxjs'; diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts index b20fb2d6e149..e194057bfd89 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts @@ -2,11 +2,11 @@ import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnChanges, import { AlertService } from 'app/core/util/alert.service'; import { Observable, Subject, Subscription, of, throwError } from 'rxjs'; import { catchError, map as rxMap, switchMap, tap } from 'rxjs/operators'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProblemStatementAnalysis } from 'app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.model'; import { Participation } from 'app/entities/participation/participation.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { hasExerciseChanged } from 'app/exercises/shared/exercise/exercise.utils'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts index 5ec55e54f894..9c842a025b85 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts @@ -4,7 +4,7 @@ import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service' import { faFileImport, faKeyboard, faPlus } from '@fortawesome/free-solid-svg-icons'; import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Router } from '@angular/router'; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts index a2f1e2174227..1a1a95462530 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts @@ -1,11 +1,12 @@ import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { SafeHtml } from '@angular/platform-browser'; +import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; import { Subject, Subscription } from 'rxjs'; -import { ProgrammingExercise, ProgrammingExerciseBuildConfig, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { AlertService, AlertType } from 'app/core/util/alert.service'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { AccountService } from 'app/core/auth/account.service'; import { HttpErrorResponse } from '@angular/common/http'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; @@ -332,12 +333,17 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy { { type: DetailType.Boolean, title: 'artemisApp.programmingExercise.allowOfflineIde.title', - data: { boolean: exercise.allowOfflineIde }, + data: { boolean: exercise.allowOfflineIde ?? false }, }, { type: DetailType.Boolean, title: 'artemisApp.programmingExercise.allowOnlineEditor.title', - data: { boolean: exercise.allowOnlineEditor }, + data: { boolean: exercise.allowOnlineEditor ?? false }, + }, + { + type: DetailType.Boolean, + title: 'artemisApp.programmingExercise.allowOnlineIde.title', + data: { boolean: exercise.allowOnlineIde ?? false }, }, ], }; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts index 5dd785d95005..287e50b11913 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts @@ -3,7 +3,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { AlertService, AlertType } from 'app/core/util/alert.service'; import { faSave } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts index 39ed8888464a..54c277c9bd7d 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts @@ -3,7 +3,7 @@ import { UserRouteAccessService } from 'app/core/auth/user-route-access-service' import { Injectable, NgModule } from '@angular/core'; import { ProgrammingExerciseDetailComponent } from 'app/exercises/programming/manage/programming-exercise-detail.component'; import { ProgrammingExerciseUpdateComponent } from 'app/exercises/programming/manage/update/programming-exercise-update.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { map } from 'rxjs/operators'; import { HttpResponse } from '@angular/common/http'; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html index 89d72f95b7ef..39f4a28bbff0 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html @@ -139,12 +139,16 @@ }
- {{ 'artemisApp.programmingExercise.offlineIde' | artemisTranslate }}: - {{ programmingExercise.allowOfflineIde ? ('artemisApp.exercise.yes' | artemisTranslate) : ('artemisApp.exercise.no' | artemisTranslate) }} + : +
- {{ 'artemisApp.programmingExercise.onlineEditor' | artemisTranslate }}: - {{ programmingExercise.allowOnlineEditor ? ('artemisApp.exercise.yes' | artemisTranslate) : ('artemisApp.exercise.no' | artemisTranslate) }} + : + +
+
+ : +
@if (course.presentationScore !== 0) { diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts index b28fbed3d6b9..e40533f526cc 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts @@ -1,7 +1,7 @@ import { Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { merge } from 'rxjs'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseInstructorRepositoryType, ProgrammingExerciseService } from './services/programming-exercise.service'; import { ActivatedRoute } from '@angular/router'; import { ExerciseComponent } from 'app/exercises/shared/exercise/exercise.component'; @@ -15,7 +15,7 @@ import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service' import { CourseManagementService } from 'app/course/manage/course-management.service'; import { SortService } from 'app/shared/service/sort.service'; import { ProgrammingExerciseEditSelectedComponent } from 'app/exercises/programming/manage/programming-exercise-edit-selected.component'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; import { createBuildPlanUrl } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; diff --git a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts index a16d439806db..f249959c63b5 100644 --- a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts +++ b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts @@ -1,7 +1,7 @@ import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; import { ProgrammingExerciseResetDialogComponent } from 'app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; @Directive({ selector: '[jhiProgrammingExerciseResetButton]', diff --git a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.ts b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.ts index 2ffe3935810a..f064e177d1ff 100644 --- a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.ts @@ -3,7 +3,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseResetOptions, ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { faBan, faCircleNotch, faSpinner, faUndo } from '@fortawesome/free-solid-svg-icons'; import { PROFILE_AEOLUS, PROFILE_LOCALCI } from 'app/app.constants'; diff --git a/src/main/webapp/app/exercises/programming/manage/services/build-plan.service.ts b/src/main/webapp/app/exercises/programming/manage/services/build-plan.service.ts index ebafd624ea66..49f0aa9dcbff 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/build-plan.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/build-plan.service.ts @@ -1,7 +1,7 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { BuildPlan } from 'app/entities/build-plan.model'; +import { BuildPlan } from 'app/entities/programming/build-plan.model'; export type EntityResponseType = HttpResponse; diff --git a/src/main/webapp/app/exercises/programming/manage/services/code-analysis-paging.service.ts b/src/main/webapp/app/exercises/programming/manage/services/code-analysis-paging.service.ts index a6bbcc6931cf..39fe777826e6 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/code-analysis-paging.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/code-analysis-paging.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExercisePagingService } from 'app/exercises/programming/manage/services/programming-exercise-paging.service'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; diff --git a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-grading.service.ts b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-grading.service.ts index 134da4e5abe5..f2ebc62a33f7 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-grading.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-grading.service.ts @@ -3,9 +3,9 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; -import { StaticCodeAnalysisCategory } from 'app/entities/static-code-analysis-category.model'; -import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; +import { StaticCodeAnalysisCategory } from 'app/entities/programming/static-code-analysis-category.model'; +import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; export class ProgrammingExerciseTestCaseUpdate { constructor( diff --git a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-paging.service.ts b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-paging.service.ts index 29c311cfd44b..08012217fb18 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-paging.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; @Injectable({ providedIn: 'root' }) diff --git a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-participation.service.ts b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-participation.service.ts index 7af098d1a5a7..5953571a4190 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-participation.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-participation.service.ts @@ -7,7 +7,7 @@ import { Result } from 'app/entities/result.model'; import { EntityTitleService, EntityType } from 'app/shared/layouts/navbar/entity-title.service'; import { createRequestOption } from 'app/shared/util/request.util'; import { Observable, map, tap } from 'rxjs'; -import { CommitInfo } from 'app/entities/programming-submission.model'; +import { CommitInfo } from 'app/entities/programming/programming-submission.model'; export interface IProgrammingExerciseParticipationService { getLatestResultWithFeedback: (participationId: number, withSubmission: boolean) => Observable; diff --git a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-websocket.service.ts b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-websocket.service.ts index a73a3bc5c715..30d8f11507b7 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-websocket.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise-websocket.service.ts @@ -3,7 +3,7 @@ import { HttpResponse } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export type EntityResponseType = HttpResponse; export type EntityArrayResponseType = HttpResponse; diff --git a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts index 39fa4800ecb6..82077ff4ffd6 100644 --- a/src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts @@ -7,7 +7,7 @@ import { omit as _omit } from 'lodash-es'; import { createRequestOption } from 'app/shared/util/request.util'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { TextPlagiarismResult } from 'app/exercises/shared/plagiarism/types/text/TextPlagiarismResult'; @@ -19,14 +19,14 @@ import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programmin import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; import { convertDateFromClient, convertDateFromServer } from 'app/utils/date.utils'; import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; -import { BuildLogStatisticsDTO } from 'app/entities/build-log-statistics-dto'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; +import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; import { SortService } from 'app/shared/service/sort.service'; import { Result } from 'app/entities/result.model'; import { Participation } from 'app/entities/participation/participation.model'; import { PlagiarismResultDTO } from 'app/exercises/shared/plagiarism/types/PlagiarismResultDTO'; import { ImportOptions } from 'app/types/programming-exercises'; -import { CheckoutDirectoriesDto } from 'app/entities/checkout-directories-dto'; +import { CheckoutDirectoriesDto } from 'app/entities/programming/checkout-directories-dto'; export type EntityResponseType = HttpResponse; export type EntityArrayResponseType = HttpResponse; diff --git a/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-exercise-status.component.ts b/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-exercise-status.component.ts index 85bf8287074f..f9bd6758fd23 100644 --- a/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-exercise-status.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-exercise-status.component.ts @@ -3,7 +3,7 @@ import { Subscription } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { Participation } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { findLatestResult } from 'app/shared/util/utils'; import { faCheckCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { hasSolutionParticipationChanged, hasTemplateParticipationChanged } from 'app/exercises/shared/participation/participation.utils'; diff --git a/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-status.component.ts b/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-status.component.ts index 66638d6d712f..9ad6970303a1 100644 --- a/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-status.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/status/programming-exercise-instructor-status.component.ts @@ -4,8 +4,8 @@ import { filter } from 'rxjs/operators'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { Result } from 'app/entities/result.model'; import { Participation } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { findLatestResult } from 'app/shared/util/utils'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/add-auxiliary-repository-button.component.ts b/src/main/webapp/app/exercises/programming/manage/update/add-auxiliary-repository-button.component.ts index 2b9ece99aff9..06d5d6940c83 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/add-auxiliary-repository-button.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/add-auxiliary-repository-button.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { faPlus } from '@fortawesome/free-solid-svg-icons'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts index 6f7e4b3332c3..36c9b8df3a8c 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-creation-config.ts @@ -1,8 +1,8 @@ -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { ModePickerOption } from 'app/exercises/shared/mode-picker/mode-picker.component'; import { Observable } from 'rxjs'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; export type ProgrammingExerciseCreationConfig = { titleNamePattern: string; @@ -44,6 +44,7 @@ export type ProgrammingExerciseCreationConfig = { hasUnsavedChanges: boolean; rerenderSubject: Observable; validIdeSelection: () => boolean | undefined; + validOnlineIdeSelection: () => boolean | undefined; inProductionEnvironment: boolean; recreateBuildPlans: boolean; onRecreateBuildPlanOrUpdateTemplateChange: () => void; diff --git a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts index e48803fc9c81..e1257c9c467f 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.component.ts @@ -2,9 +2,10 @@ import { ActivatedRoute, Params } from '@angular/router'; import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService, AlertType } from 'app/core/util/alert.service'; +import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; import { Observable, Subject, Subscription } from 'rxjs'; import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { ProgrammingExercise, ProgrammingExerciseBuildConfig, ProgrammingLanguage, ProjectType, resetProgrammingForImport } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType, resetProgrammingForImport } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from '../services/programming-exercise.service'; import { FileService } from 'app/shared/http/file.service'; import { TranslateService } from '@ngx-translate/core'; @@ -22,14 +23,14 @@ import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { cloneDeep } from 'lodash-es'; import { ExerciseUpdateWarningService } from 'app/exercises/shared/exercise-update-warning/exercise-update-warning.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { faExclamationCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ModePickerOption } from 'app/exercises/shared/mode-picker/mode-picker.component'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { loadCourseExerciseCategories } from 'app/exercises/shared/course-exercises/course-utils'; -import { PROFILE_AEOLUS, PROFILE_LOCALCI } from 'app/app.constants'; +import { PROFILE_AEOLUS, PROFILE_LOCALCI, PROFILE_THEIA } from 'app/app.constants'; import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.service'; import { FormSectionStatus } from 'app/forms/form-status-bar/form-status-bar.component'; import { ProgrammingExerciseInformationComponent } from 'app/exercises/programming/manage/update/update-components/programming-exercise-information.component'; @@ -134,6 +135,7 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest public auxiliaryRepositoriesSupported = false; public auxiliaryRepositoriesValid = true; public customBuildPlansSupported: string = ''; + public theiaEnabled = false; // Additional options for import // This is a wrapper to allow modifications from the other subcomponents @@ -402,50 +404,52 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest }); // If it is an import from this instance, just get the course, otherwise handle the edit and new cases - this.activatedRoute.url - .pipe( - tap((segments) => { - this.isImportFromExistingExercise = segments.some((segment) => segment.path === 'import'); - this.isImportFromFile = segments.some((segment) => segment.path === 'import-from-file'); - }), - switchMap(() => this.activatedRoute.params), - tap((params) => { - if (this.isImportFromFile) { - this.createProgrammingExerciseForImportFromFile(); - } - if (this.isImportFromExistingExercise) { - this.createProgrammingExerciseForImport(params); - } else { - if (params['courseId'] && params['examId'] && params['exerciseGroupId']) { - this.isExamMode = true; - this.exerciseGroupService.find(params['courseId'], params['examId'], params['exerciseGroupId']).subscribe((res) => { - this.programmingExercise.exerciseGroup = res.body!; - if (!params['exerciseId'] && this.programmingExercise.exerciseGroup.exam?.course?.defaultProgrammingLanguage && !this.isImportFromFile) { - this.selectedProgrammingLanguage = this.programmingExercise.exerciseGroup.exam.course.defaultProgrammingLanguage; + if (this.activatedRoute && this.activatedRoute.url) { + this.activatedRoute.url + .pipe( + tap((segments) => { + this.isImportFromExistingExercise = segments.some((segment) => segment.path === 'import'); + this.isImportFromFile = segments.some((segment) => segment.path === 'import-from-file'); + }), + switchMap(() => this.activatedRoute.params), + tap((params) => { + if (this.isImportFromFile) { + this.createProgrammingExerciseForImportFromFile(); + } + if (this.isImportFromExistingExercise) { + this.createProgrammingExerciseForImport(params); + } else { + if (params['courseId'] && params['examId'] && params['exerciseGroupId']) { + this.isExamMode = true; + this.exerciseGroupService.find(params['courseId'], params['examId'], params['exerciseGroupId']).subscribe((res) => { + this.programmingExercise.exerciseGroup = res.body!; + if (!params['exerciseId'] && this.programmingExercise.exerciseGroup.exam?.course?.defaultProgrammingLanguage && !this.isImportFromFile) { + this.selectedProgrammingLanguage = this.programmingExercise.exerciseGroup.exam.course.defaultProgrammingLanguage; + } + }); + // we need the course id to make the request to the server if it's an import from file + if (this.isImportFromFile) { + this.courseId = params['courseId']; + this.loadCourseExerciseCategories(params['courseId']); } - }); - // we need the course id to make the request to the server if it's an import from file - if (this.isImportFromFile) { + } else if (params['courseId']) { this.courseId = params['courseId']; - this.loadCourseExerciseCategories(params['courseId']); + this.isExamMode = false; + this.courseService.find(this.courseId).subscribe((res) => { + this.programmingExercise.course = res.body!; + if (!params['exerciseId'] && this.programmingExercise.course?.defaultProgrammingLanguage && !this.isImportFromFile) { + this.selectedProgrammingLanguage = this.programmingExercise.course.defaultProgrammingLanguage!; + } + this.exerciseCategories = this.programmingExercise.categories || []; + + this.loadCourseExerciseCategories(this.programmingExercise.course!.id!); + }); } - } else if (params['courseId']) { - this.courseId = params['courseId']; - this.isExamMode = false; - this.courseService.find(this.courseId).subscribe((res) => { - this.programmingExercise.course = res.body!; - if (!params['exerciseId'] && this.programmingExercise.course?.defaultProgrammingLanguage && !this.isImportFromFile) { - this.selectedProgrammingLanguage = this.programmingExercise.course.defaultProgrammingLanguage!; - } - this.exerciseCategories = this.programmingExercise.categories || []; - - this.loadCourseExerciseCategories(this.programmingExercise.course!.id!); - }); } - } - }), - ) - .subscribe(); + }), + ) + .subscribe(); + } // If an exercise is created, load our readme template so the problemStatement is not empty this.selectedProgrammingLanguage = this.programmingExercise.programmingLanguage!; @@ -470,6 +474,9 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest if (profileInfo?.activeProfiles.includes(PROFILE_AEOLUS)) { this.customBuildPlansSupported = PROFILE_AEOLUS; } + if (profileInfo?.activeProfiles.includes(PROFILE_THEIA)) { + this.theiaEnabled = true; + } }); this.defineSupportedProgrammingLanguages(); } @@ -500,13 +507,9 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest }, { title: 'artemisApp.programmingExercise.wizardMode.detailedSteps.languageStepTitle', - valid: this.exerciseLanguageComponent?.formValid ?? false, - }, - { - title: 'artemisApp.programmingExercise.wizardMode.detailedSteps.problemStepTitle', - valid: true, - empty: !this.programmingExercise.problemStatement, + valid: (this.exerciseLanguageComponent?.formValid && this.validOnlineIdeSelection()) ?? false, }, + { title: 'artemisApp.programmingExercise.wizardMode.detailedSteps.problemStepTitle', valid: true, empty: !this.programmingExercise.problemStatement }, { title: 'artemisApp.programmingExercise.wizardMode.detailedSteps.gradingStepTitle', valid: Boolean(this.exerciseGradingComponent?.formValid && (this.isExamMode || this.exercisePlagiarismComponent?.formValid)), @@ -676,8 +679,12 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest private subscribeToSaveResponse(result: Observable>) { result.subscribe({ - next: (response: HttpResponse) => this.onSaveSuccess(response.body!), - error: (error: HttpErrorResponse) => this.onSaveError(error), + next: (response: HttpResponse) => { + this.onSaveSuccess(response.body!); + }, + error: (error: HttpErrorResponse) => { + this.onSaveError(error); + }, }); } @@ -821,10 +828,21 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest } /** - * checking if at least one of Online Editor or Offline Ide is selected + * checking if at least one of Online Editor, Offline Ide, or Online Ide is selected */ validIdeSelection = () => { - return this.programmingExercise?.allowOnlineEditor || this.programmingExercise?.allowOfflineIde; + if (this.theiaEnabled) { + return this.programmingExercise?.allowOnlineEditor || this.programmingExercise?.allowOfflineIde || this.programmingExercise?.allowOnlineIde; + } else { + return this.programmingExercise?.allowOnlineEditor || this.programmingExercise?.allowOfflineIde; + } + }; + + /** + * Checking if the online IDE is selected and a valid image is selected + */ + validOnlineIdeSelection = () => { + return !this.programmingExercise?.allowOnlineIde || this.programmingExercise?.buildConfig!.theiaImage !== undefined; }; isEventInsideTextArea(event: Event): boolean { @@ -846,6 +864,7 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest this.validateExerciseAuxiliaryRepositories(validationErrorReasons); this.validateExercisePackageName(validationErrorReasons); this.validateExerciseIdeSelection(validationErrorReasons); + this.validateExerciseOnlineIdeSelection(validationErrorReasons); this.validateExercisePoints(validationErrorReasons); this.validateExerciseBonusPoints(validationErrorReasons); this.validateExerciseSCAMaxPenalty(validationErrorReasons); @@ -1044,8 +1063,18 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest private validateExerciseIdeSelection(validationErrorReasons: ValidationReason[]): void { if (!this.validIdeSelection()) { + const translateKey = this.theiaEnabled ? 'artemisApp.programmingExercise.allowOnlineEditor.alert' : 'artemisApp.programmingExercise.allowOnlineEditor.alertNoTheia'; + validationErrorReasons.push({ + translateKey: translateKey, + translateValues: {}, + }); + } + } + + private validateExerciseOnlineIdeSelection(validationErrorReasons: ValidationReason[]): void { + if (!this.validOnlineIdeSelection()) { validationErrorReasons.push({ - translateKey: 'artemisApp.programmingExercise.allowOnlineEditor.alert', + translateKey: 'artemisApp.programmingExercise.theiaImage.alert', translateValues: {}, }); } @@ -1110,6 +1139,7 @@ export class ProgrammingExerciseUpdateComponent implements AfterViewInit, OnDest hasUnsavedChanges: this.hasUnsavedChanges, rerenderSubject: this.rerenderSubject.asObservable(), validIdeSelection: this.validIdeSelection, + validOnlineIdeSelection: this.validOnlineIdeSelection, inProductionEnvironment: this.inProductionEnvironment, recreateBuildPlans: this.importOptions.recreateBuildPlans, onRecreateBuildPlanOrUpdateTemplateChange: this.onRecreateBuildPlanOrUpdateTemplateChange, diff --git a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.module.ts b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.module.ts index 8c5fa972cd92..7ac399d6daeb 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/programming-exercise-update.module.ts @@ -31,6 +31,7 @@ import { ProgrammingExerciseDockerImageComponent } from 'app/exercises/programmi import { FormsModule } from 'app/forms/forms.module'; import { ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component'; import { ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component'; +import { ProgrammingExerciseTheiaComponent } from 'app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component'; import { MonacoEditorModule } from 'app/shared/monaco-editor/monaco-editor.module'; @NgModule({ @@ -57,6 +58,7 @@ import { MonacoEditorModule } from 'app/shared/monaco-editor/monaco-editor.modul ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent, ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent, MonacoEditorModule, + ProgrammingExerciseTheiaComponent, ], declarations: [ ProgrammingExerciseUpdateComponent, diff --git a/src/main/webapp/app/exercises/programming/manage/update/remove-auxiliary-repository-button.component.ts b/src/main/webapp/app/exercises/programming/manage/update/remove-auxiliary-repository-button.component.ts index dcd21a30e18c..2ce643767155 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/remove-auxiliary-repository-button.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/remove-auxiliary-repository-button.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component.ts index 4f51ccd37432..e350d8c201b9 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; -import { BuildAction, ProgrammingExercise, ProgrammingLanguage, ProjectType, ScriptAction } from 'app/entities/programming-exercise.model'; +import { BuildAction, ScriptAction } from 'app/entities/programming/build.action'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.service'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-build-plan.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-build-plan.component.ts index 1a314af06cfd..437c22fc0f40 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-build-plan.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-build-plan.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; -import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.service'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.html index addc823095a9..8f66c32f99dd 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.html +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.html @@ -27,12 +27,21 @@ /> @if (!programmingExerciseCreationConfig.validIdeSelection()) { - + @if (theiaEnabled) { + + } @else { + + } }
@@ -49,12 +58,46 @@ (ngModelChange)="triggerValidation.emit()" /> + @if (!programmingExerciseCreationConfig.validIdeSelection()) { + @if (theiaEnabled) { + + } @else { + + } + } + +
+ } + + @if (!programmingExercise.exerciseGroup && theiaEnabled) { +
+ diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.ts index 77e30693bff9..9b90e235dcec 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-difficulty.component.ts @@ -1,15 +1,17 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { ProgrammingExercise, ProjectType } from 'app/entities/programming-exercise.model'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { ProgrammingExercise, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; +import { PROFILE_THEIA } from 'app/app.constants'; +import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; @Component({ selector: 'jhi-programming-exercise-difficulty', templateUrl: './programming-exercise-difficulty.component.html', styleUrls: ['../../programming-exercise-form.scss'], }) -export class ProgrammingExerciseDifficultyComponent { +export class ProgrammingExerciseDifficultyComponent implements OnInit { @Input() programmingExercise: ProgrammingExercise; @Input() programmingExerciseCreationConfig: ProgrammingExerciseCreationConfig; @ViewChild(TeamConfigFormGroupComponent) teamConfigComponent: TeamConfigFormGroupComponent; @@ -18,5 +20,15 @@ export class ProgrammingExerciseDifficultyComponent { protected readonly ProjectType = ProjectType; + theiaEnabled: boolean = false; + + constructor(private profileService: ProfileService) {} + + ngOnInit(): void { + this.profileService.getProfileInfo().subscribe((profileInfo) => { + this.theiaEnabled = profileInfo.activeProfiles?.includes(PROFILE_THEIA); + }); + } + faQuestionCircle = faQuestionCircle; } diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-grading.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-grading.component.ts index b82813fa9f06..5a5c6a17c6d3 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-grading.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-grading.component.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Component, Input, OnDestroy, ViewChild } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts index b1bd35bca30a..f60b0948a4c6 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, Input, OnDestroy, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { NgModel } from '@angular/forms'; -import { ProgrammingExercise, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; import { Subject, Subscription } from 'rxjs'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.html index 1e4c258dfd16..e801f1a04d85 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.html +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.html @@ -111,6 +111,13 @@ }
} + + + @if (programmingExercise.allowOnlineIde && programmingExercise.programmingLanguage) { + + + } + @if (programmingExercise.programmingLanguage && programmingExerciseCreationConfig.staticCodeAnalysisAllowed) {
diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.ts index 51bb90eb46d8..696d75ce185f 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-language.component.ts @@ -1,5 +1,5 @@ import { AfterViewChecked, AfterViewInit, Component, EventEmitter, Input, OnDestroy, ViewChild } from '@angular/core'; -import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { PROFILE_AEOLUS, PROFILE_LOCALCI } from 'app/app.constants'; @@ -7,6 +7,7 @@ import { NgModel } from '@angular/forms'; import { Subject, Subscription } from 'rxjs'; import { ProgrammingExerciseCustomAeolusBuildPlanComponent } from 'app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component'; import { ProgrammingExerciseCustomBuildPlanComponent } from 'app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-build-plan.component'; +import { ProgrammingExerciseTheiaComponent } from 'app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component'; @Component({ selector: 'jhi-programming-exercise-language', @@ -24,6 +25,7 @@ export class ProgrammingExerciseLanguageComponent implements AfterViewChecked, A @ViewChild('packageName') packageNameField?: NgModel; @ViewChild(ProgrammingExerciseCustomAeolusBuildPlanComponent) programmingExerciseCustomAeolusBuildPlanComponent?: ProgrammingExerciseCustomAeolusBuildPlanComponent; @ViewChild(ProgrammingExerciseCustomBuildPlanComponent) programmingExerciseCustomBuildPlanComponent?: ProgrammingExerciseCustomBuildPlanComponent; + @ViewChild(ProgrammingExerciseTheiaComponent) programmingExerciseTheiaComponent?: ProgrammingExerciseTheiaComponent; formValid: boolean; formValidChanges = new Subject(); diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.ts index 74eba35715a5..803b06558f72 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.html new file mode 100644 index 000000000000..96d23fa998e8 --- /dev/null +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.html @@ -0,0 +1,21 @@ +
+ +
diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.ts new file mode 100644 index 000000000000..b516009846ed --- /dev/null +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component.ts @@ -0,0 +1,82 @@ +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; +import { TheiaService } from 'app/exercises/programming/shared/service/theia.service'; +import { ArtemisSharedLibsModule } from 'app/shared/shared-libs.module'; + +@Component({ + selector: 'jhi-programming-exercise-theia', + templateUrl: './programming-exercise-theia.component.html', + styleUrls: ['../../../programming-exercise-form.scss'], + standalone: true, + imports: [ArtemisSharedLibsModule], +}) +export class ProgrammingExerciseTheiaComponent implements OnChanges { + @Input() programmingExercise: ProgrammingExercise; + @Input() programmingExerciseCreationConfig: ProgrammingExerciseCreationConfig; + + programmingLanguage?: ProgrammingLanguage; + theiaImages = {}; + + constructor(private theiaService: TheiaService) {} + + ngOnChanges(changes: SimpleChanges) { + if ((changes.programmingExerciseCreationConfig || changes.programmingExercise) && this.shouldReloadTemplate()) { + this.loadTheiaImages(); + } + } + + onTheiaImageChange(theiaImage: string) { + if (this.programmingExercise.buildConfig) { + this.programmingExercise.buildConfig.theiaImage = theiaImage; + } + } + + shouldReloadTemplate(): boolean { + return this.programmingExercise.programmingLanguage !== this.programmingLanguage; + } + + /** + * In case the programming language or project type changes, we need to reset the template and the build plan + * @private + */ + resetImageSelection() { + if (this.programmingExercise.buildConfig) { + this.programmingExercise.buildConfig.theiaImage = undefined; + } + } + + /** + * Loads the predefined template for the selected programming language if there is one available. + * @private + */ + loadTheiaImages() { + if (!this.programmingExercise || !this.programmingExercise.programmingLanguage) { + return; + } + + this.programmingLanguage = this.programmingExercise.programmingLanguage; + + this.theiaService.getTheiaImages(this.programmingLanguage).subscribe({ + next: (images) => { + if (!images) { + // Remove selection if no image is available + this.theiaImages = {}; + this.resetImageSelection(); + return; + } + + this.theiaImages = images; + + // Set the first image as default if none is selected + if (this.programmingExercise && this.programmingExercise.buildConfig && !this.programmingExercise.buildConfig.theiaImage && Object.values(images).length > 0) { + this.programmingExercise.buildConfig.theiaImage = Object.values(images).first() as string; + } + }, + error: () => { + this.theiaImages = {}; + this.resetImageSelection(); + }, + }); + } +} diff --git a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts index 826902c2058a..34aa3e0b0d5a 100644 --- a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts +++ b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts @@ -10,7 +10,7 @@ import { DomainService } from 'app/exercises/programming/shared/code-editor/serv import { ExerciseType, IncludedInOverallScore, getCourseFromExercise } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; import { Feedback, FeedbackType, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { ActivatedRoute } from '@angular/router'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; diff --git a/src/main/webapp/app/exercises/programming/participate/programming-submission-policy-status.ts b/src/main/webapp/app/exercises/programming/participate/programming-submission-policy-status.ts index e6162da1afd9..826437a4ce28 100644 --- a/src/main/webapp/app/exercises/programming/participate/programming-submission-policy-status.ts +++ b/src/main/webapp/app/exercises/programming/participate/programming-submission-policy-status.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; @Component({ diff --git a/src/main/webapp/app/exercises/programming/participate/programming-submission.service.ts b/src/main/webapp/app/exercises/programming/participate/programming-submission.service.ts index c13539943903..0fd3d43a0535 100644 --- a/src/main/webapp/app/exercises/programming/participate/programming-submission.service.ts +++ b/src/main/webapp/app/exercises/programming/participate/programming-submission.service.ts @@ -7,7 +7,7 @@ import { Result } from 'app/entities/result.model'; import { createRequestOption } from 'app/shared/util/request.util'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionType, getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { findLatestResult } from 'app/shared/util/utils'; diff --git a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-instructor-submission-state.component.ts b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-instructor-submission-state.component.ts index afa3062da5b8..60081cc274f3 100644 --- a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-instructor-submission-state.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-instructor-submission-state.component.ts @@ -3,7 +3,7 @@ import { debounceTime, map, tap } from 'rxjs/operators'; import { ExerciseSubmissionState, ProgrammingSubmissionService, ProgrammingSubmissionState } from 'app/exercises/programming/participate/programming-submission.service'; import { Subscription } from 'rxjs'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { hasExerciseChanged } from 'app/exercises/shared/exercise/exercise.utils'; import { ButtonType } from 'app/shared/components/button.component'; import { faCircleNotch, faClock, faRedo } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-re-evaluate-button.component.ts b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-re-evaluate-button.component.ts index 06c2447d5248..2d1710b1b692 100644 --- a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-re-evaluate-button.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-re-evaluate-button.component.ts @@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ButtonType } from 'app/shared/components/button.component'; import { faRedo } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component.ts b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component.ts index 3dc928117229..28454d8c0e72 100644 --- a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component.ts @@ -6,7 +6,7 @@ import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { hasDueDatePassed } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; import { BuildRunState, ProgrammingBuildRunService } from 'app/exercises/programming/participate/programming-build-run.service'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ButtonType } from 'app/shared/components/button.component'; import { faBan, faRedo, faTimes } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts index 5efd0b6ea087..52089b28c991 100644 --- a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts @@ -10,7 +10,7 @@ import { hasDueDatePassed } from 'app/exercises/programming/shared/utils/program import { Result } from 'app/entities/result.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { SubmissionType } from 'app/entities/submission.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AlertService } from 'app/core/util/alert.service'; import { hasParticipationChanged } from 'app/exercises/shared/participation/participation.utils'; diff --git a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts index 4bcffecaa5af..d1932dc53ff3 100644 --- a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -import { BuildPlanCheckoutDirectoriesDTO } from 'app/entities/build-plan-checkout-directories-dto'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { BuildPlanCheckoutDirectoriesDTO } from 'app/entities/programming/build-plan-checkout-directories-dto'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; @Component({ selector: 'jhi-programming-exercise-build-plan-checkout-directories', diff --git a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts index 30021b146f48..f44d22e06ff7 100644 --- a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts @@ -1,9 +1,9 @@ import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'; import { getCourseFromExercise } from 'app/entities/exercise.model'; -import type { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import type { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { Subscription } from 'rxjs'; -import type { CheckoutDirectoriesDto } from 'app/entities/checkout-directories-dto'; +import type { CheckoutDirectoriesDto } from 'app/entities/programming/checkout-directories-dto'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component'; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts index d4506dbc256b..1c7535cbc9d6 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts @@ -2,7 +2,7 @@ import { ParticipationWebsocketService } from 'app/overview/participation-websoc import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { Observable, Subscription, of } from 'rxjs'; import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; -import { BuildLogEntry, BuildLogEntryArray } from 'app/entities/build-log.model'; +import { BuildLogEntry, BuildLogEntryArray } from 'app/entities/programming/build-log.model'; import { Participation, getExercise } from 'app/entities/participation/participation.model'; import { CodeEditorSubmissionService } from 'app/exercises/programming/shared/code-editor/service/code-editor-submission.service'; import { CodeEditorBuildLogService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; @@ -11,10 +11,10 @@ import { ResultService } from 'app/exercises/shared/result/result.service'; import { Result } from 'app/entities/result.model'; import { Interactable } from '@interactjs/core/Interactable'; import interact from 'interactjs'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { findLatestResult } from 'app/shared/util/utils'; -import { StaticCodeAnalysisIssue } from 'app/entities/static-code-analysis-issue.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { StaticCodeAnalysisIssue } from 'app/entities/programming/static-code-analysis-issue.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { faChevronDown, faCircleNotch, faTerminal } from '@fortawesome/free-solid-svg-icons'; import { hasParticipationChanged } from 'app/exercises/shared/participation/participation.utils'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/model/code-editor.model.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/model/code-editor.model.ts index 15e715b290ef..a3215aa04c48 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/model/code-editor.model.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/model/code-editor.model.ts @@ -1,7 +1,7 @@ import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; /** * Enumeration defining type of the exported file. diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts index 4700569d8c97..64ed91bab723 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import type { CommitInfo } from 'app/entities/programming-submission.model'; +import type { CommitInfo } from 'app/entities/programming/programming-submission.model'; @Component({ selector: 'jhi-commits-info-group', diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts index 51edbcc7652c..a944e452e1b4 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import type { CommitInfo } from 'app/entities/programming-submission.model'; +import type { CommitInfo } from 'app/entities/programming/programming-submission.model'; import { faCircle } from '@fortawesome/free-regular-svg-icons'; import { faAngleDown, faAngleLeft } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts index d3728a48dc46..28a46506caab 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import dayjs from 'dayjs/esm'; import { createCommitUrl } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts index 6b7a28165b4a..2c0d4036369b 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { Subject } from 'rxjs'; import { tap } from 'rxjs/operators'; import { escapeStringForUseInRegex } from 'app/shared/util/global.utils'; diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts index 75e060446c19..f1f669c7fb5c 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts @@ -14,12 +14,12 @@ import { } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { ThemeService } from 'app/core/theme/theme.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; import { ShowdownExtension } from 'showdown'; import { catchError, filter, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { Observable, Subscription, merge, of } from 'rxjs'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { ProgrammingExerciseTaskExtensionWrapper, taskRegex } from './extensions/programming-exercise-task.extension'; import { ProgrammingExercisePlantUmlExtensionWrapper } from 'app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension'; diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service.ts index 3234cae415ee..73cd7de06eba 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { Result } from 'app/entities/result.model'; /** diff --git a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts index 9185f229d2ff..aa2e0e951699 100644 --- a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts @@ -2,7 +2,7 @@ import { AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, QueryLis import dayjs from 'dayjs/esm'; import { TranslateService } from '@ngx-translate/core'; import { AssessmentType } from 'app/entities/assessment-type.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { faCogs, faUserCheck, faUserSlash } from '@fortawesome/free-solid-svg-icons'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; diff --git a/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts b/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts index e13138c55c04..1b468776d570 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts @@ -1,8 +1,10 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { BuildAction, PlatformAction, ScriptAction } from 'app/entities/programming/build.action'; +import { WindFile } from 'app/entities/programming/wind.file'; import { Observable } from 'rxjs'; -import { BuildAction, PlatformAction, ProgrammingLanguage, ProjectType, ScriptAction, WindFile } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; @Injectable({ providedIn: 'root' }) export class AeolusService { diff --git a/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts b/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts index 029e09af8e16..3f749357f188 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient, HttpParams } from '@angular/common/http'; -import { BuildLogEntry } from 'app/entities/build-log.model'; +import { BuildLogEntry } from 'app/entities/programming/build-log.model'; export interface IBuildLogService { getBuildLogs: (participationId: number, resultId?: number) => Observable; diff --git a/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts b/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts index 3c7d1c9eaccd..7f2e95b0f73a 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; /** diff --git a/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts b/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts new file mode 100644 index 000000000000..3165730ddab7 --- /dev/null +++ b/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts @@ -0,0 +1,25 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; + +@Injectable({ providedIn: 'root' }) +export class TheiaService { + private resourceUrl = 'api/theia'; + + constructor(private http: HttpClient) {} + + /** + * Fetches the theia images for the given programming language + * @param {ProgrammingLanguage} language + * @returns the theia images or undefined if no images are available for this language + */ + getTheiaImages(language: ProgrammingLanguage): Observable<{ [key: string]: string } | undefined> { + return this.http.get<{ [key: string]: string }>(`${this.resourceUrl}/images`, { + params: { + language: language, + }, + }); + } +} diff --git a/src/main/webapp/app/exercises/programming/shared/utils/programming-exercise.utils.ts b/src/main/webapp/app/exercises/programming/shared/utils/programming-exercise.utils.ts index 3234ee6a1cf1..5e95bb66dbac 100644 --- a/src/main/webapp/app/exercises/programming/shared/utils/programming-exercise.utils.ts +++ b/src/main/webapp/app/exercises/programming/shared/utils/programming-exercise.utils.ts @@ -1,8 +1,8 @@ import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionType } from 'app/entities/submission.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts index a1cc56e466c1..b6fda2b6e1ee 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts @@ -19,7 +19,7 @@ import { Course } from 'app/entities/course.model'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { cloneDeep } from 'lodash-es'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts index 7714b90a5841..ea04a7e2fe7f 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts @@ -15,7 +15,7 @@ import { QuizQuestionListEditComponent } from 'app/exercises/quiz/manage/quiz-qu import { onError } from 'app/shared/util/global.utils'; import { computeQuizQuestionInvalidReason, isQuizQuestionValid } from 'app/exercises/quiz/shared/quiz-manage-util.service'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; @Component({ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts index a361064be33f..7dc2df7ed5d3 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts @@ -9,7 +9,7 @@ import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { AlertService } from 'app/core/util/alert.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; diff --git a/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts b/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts index f9eefb8f5d7f..c9ea1e512eb3 100644 --- a/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts +++ b/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts @@ -1,9 +1,9 @@ import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { AccountService } from 'app/core/auth/account.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts index 62f371f95057..a5bffcbe5575 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts @@ -9,7 +9,7 @@ import { TutorParticipationService } from 'app/exercises/shared/dashboards/tutor import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { ExampleSubmission } from 'app/entities/example-submission.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { UMLModel } from '@ls1intum/apollon'; import { ComplaintService } from 'app/complaints/complaint.service'; @@ -22,7 +22,7 @@ import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.mod import { TranslateService } from '@ngx-translate/core'; import { FileUploadSubmissionService } from 'app/exercises/file-upload/participate/file-upload-submission.service'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; import { AccountService } from 'app/core/auth/account.service'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; @@ -31,8 +31,8 @@ import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exer import { TutorParticipation, TutorParticipationStatus } from 'app/entities/participation/tutor-participation.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; -import { Exam } from 'app/entities/exam.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { Exam } from 'app/entities/exam/exam.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { SubmissionService, SubmissionWithComplaintDTO } from 'app/exercises/shared/submission/submission.service'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { SortService } from 'app/shared/service/sort.service'; diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts index 6ad8422696dc..96741ee28f37 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; import { Submission } from 'app/entities/submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; @Component({ selector: 'jhi-language-table-cell', diff --git a/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts b/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts index 79f1e755b358..49a62930183d 100644 --- a/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts +++ b/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts @@ -8,7 +8,7 @@ import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.ser import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ModelingExerciseService } from 'app/exercises/modeling/manage/modeling-exercise.service'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { EventManager } from 'app/core/util/event-manager.service'; diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts index 08e1b3eccb6e..5fe845bfe309 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts @@ -6,7 +6,7 @@ import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service' import { map } from 'rxjs/operators'; import { Submission } from 'app/entities/submission.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts index 3d7af78e4a85..b89694846761 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts @@ -2,19 +2,19 @@ import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; import { Exercise, ExerciseType, IncludedInOverallScore, getCourseFromExercise, getIcon, getIconTooltip } from 'app/entities/exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { SubmissionPolicy } from 'app/entities/submission-policy.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { ComplaintService } from 'app/complaints/complaint.service'; import { SubmissionType } from 'app/entities/submission.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; @Component({ diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts index b734c1c628df..c9d222f78ee3 100644 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint-update.component.ts @@ -9,7 +9,7 @@ import { faBan, faCircleNotch, faSave } from '@fortawesome/free-solid-svg-icons' import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; import { ManualSolutionEntryCreationModalComponent } from 'app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; diff --git a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts index 49829c3f3fef..3b542f17643b 100644 --- a/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-hint/manage/exercise-hint.component.ts @@ -11,7 +11,7 @@ import { EventManager } from 'app/core/util/event-manager.service'; import { faArrowsRotate, faCode, faEye, faFont, faPlus, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; import { ExerciseHint, HintType } from 'app/entities/hestia/exercise-hint.model'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; @Component({ selector: 'jhi-exercise-hint', diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts index 62720c9c897f..704737e2b4d0 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts @@ -6,12 +6,12 @@ import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exer import { Component, Input, OnInit } from '@angular/core'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { getTestCaseNamesFromResults, getTestCaseResults } from 'app/exercises/shared/result/result.utils'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { ResultWithPointsPerGradingCriterion } from 'app/entities/result-with-points-per-grading-criterion.model'; import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { download, generateCsv, mkConfig } from 'export-to-csv'; -import { TestCaseResult } from 'app/entities/test-case-result.model'; +import { TestCaseResult } from 'app/entities/programming/test-case-result.model'; @Component({ selector: 'jhi-exercise-scores-export-button', diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts index 1b578e4ecbd4..b518bd05bb5a 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts @@ -14,11 +14,11 @@ import { ResultService } from 'app/exercises/shared/result/result.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Result } from 'app/entities/result.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { formatTeamAsSearchResult } from 'app/exercises/shared/team/team.utils'; import { faCodeBranch, faComment, faDownload, faFilter, faFolderOpen, faListAlt, faSync } from '@fortawesome/free-solid-svg-icons'; import { faFileCode } from '@fortawesome/free-regular-svg-icons'; diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts index dffb26604cc7..a5c6b2febc1a 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts @@ -12,10 +12,10 @@ import { TranslateService } from '@ngx-translate/core'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { convertDateFromClient, convertDateFromServer } from 'app/utils/date.utils'; import { EntityTitleService, EntityType } from 'app/shared/layouts/navbar/entity-title.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { InitializationState } from 'app/entities/participation/participation.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { SafeHtml } from '@angular/platform-browser'; @@ -347,7 +347,7 @@ export class ExerciseService { * @param exercise the exercise */ static stringifyExerciseCategories(exercise: Exercise) { - return exercise.categories?.map((category) => JSON.stringify(category) as ExerciseCategory); + return exercise.categories?.map((category) => JSON.stringify(category) as unknown as ExerciseCategory); } /** @@ -362,12 +362,15 @@ export class ExerciseService { } /** - * Parses the exercise categories JSON string into ExerciseCategory objects. + * Parses the exercise categories JSON string into {@link ExerciseCategory} objects. * @param exercise - the exercise */ static parseExerciseCategories(exercise?: Exercise) { if (exercise?.categories) { - exercise.categories = exercise.categories.map((category) => JSON.parse(category as string) as ExerciseCategory); + exercise.categories = exercise.categories.map((category) => { + const categoryObj = JSON.parse(category as unknown as string); + return new ExerciseCategory(categoryObj.category, categoryObj.color); + }); } } diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.utils.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.utils.ts index 655133c142c3..091f4af5364d 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.utils.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.utils.ts @@ -2,7 +2,7 @@ import { SimpleChanges } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import dayjs from 'dayjs/esm'; import { InitializationState, Participation } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { Observable, from, of } from 'rxjs'; diff --git a/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts b/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts index 8fe19b908551..576445b7831d 100644 --- a/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts @@ -3,14 +3,14 @@ import { HttpErrorResponse } from '@angular/common/http'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { of, throwError } from 'rxjs'; -import { BuildLogEntry, BuildLogEntryArray, BuildLogType } from 'app/entities/build-log.model'; +import { BuildLogEntry, BuildLogEntryArray, BuildLogType } from 'app/entities/programming/build-log.model'; import { Feedback, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; import { Badge, ResultService } from 'app/exercises/shared/result/result.service'; import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TranslateService } from '@ngx-translate/core'; import { createCommitUrl, isProgrammingExerciseParticipation } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/main/webapp/app/exercises/shared/feedback/group/programming-feedback-groups.ts b/src/main/webapp/app/exercises/shared/feedback/group/programming-feedback-groups.ts index 006f24f2fba0..8bdf9ecd30b4 100644 --- a/src/main/webapp/app/exercises/shared/feedback/group/programming-feedback-groups.ts +++ b/src/main/webapp/app/exercises/shared/feedback/group/programming-feedback-groups.ts @@ -1,7 +1,7 @@ import { FeedbackGroup } from 'app/exercises/shared/feedback/group/feedback-group'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; import { Exercise } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; /** * Returns all FeedbackItemGroups for Programming exercises in the order, in which they will be displayed diff --git a/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts b/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts index 03b491da0195..8b231443aa32 100644 --- a/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts +++ b/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts @@ -11,7 +11,7 @@ import { SUBMISSION_POLICY_FEEDBACK_IDENTIFIER, } from 'app/entities/feedback.model'; import { TranslateService } from '@ngx-translate/core'; -import { StaticCodeAnalysisIssue } from 'app/entities/static-code-analysis-issue.model'; +import { StaticCodeAnalysisIssue } from 'app/entities/programming/static-code-analysis-issue.model'; import { getAllFeedbackGroups } from 'app/exercises/shared/feedback/group/programming-feedback-groups'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; import { Exercise } from 'app/entities/exercise.model'; diff --git a/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts b/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts index d0389d076607..23673a0cd2da 100644 --- a/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts +++ b/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; @Component({ selector: 'jhi-exercise-import-wrapper', diff --git a/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts b/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts index 85a408c2fdc1..1fea53eaacbd 100644 --- a/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts +++ b/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts @@ -2,7 +2,7 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { FileUploadExercisePagingService } from 'app/exercises/file-upload/manage/file-upload-exercise-paging.service'; import { ModelingExercisePagingService } from 'app/exercises/modeling/manage/modeling-exercise-paging.service'; import { CodeAnalysisPagingService } from 'app/exercises/programming/manage/services/code-analysis-paging.service'; diff --git a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts index bc6621cf3e64..688b7cd6fb44 100644 --- a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts +++ b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts @@ -1,10 +1,11 @@ import { Component, Input, OnInit } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; +import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; import { MAX_FILE_SIZE } from 'app/shared/constants/input.constants'; import { AlertService } from 'app/core/util/alert.service'; import { faUpload } from '@fortawesome/free-solid-svg-icons'; -import { ProgrammingExercise, ProgrammingExerciseBuildConfig, copyBuildConfigFromExerciseJson } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, copyBuildConfigFromExerciseJson } from 'app/entities/programming/programming-exercise.model'; import JSZip from 'jszip'; @Component({ diff --git a/src/main/webapp/app/exercises/shared/manage/exercise-paging.service.ts b/src/main/webapp/app/exercises/shared/manage/exercise-paging.service.ts index 5d646d031991..1697967842ad 100644 --- a/src/main/webapp/app/exercises/shared/manage/exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/shared/manage/exercise-paging.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Exercise } from 'app/entities/exercise.model'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { PagingService } from 'app/exercises/shared/manage/paging.service'; import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; import { Observable, map } from 'rxjs'; diff --git a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts index 57f529b33147..733059f76d8d 100644 --- a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts +++ b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts @@ -11,8 +11,8 @@ import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service' import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import dayjs from 'dayjs/esm'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TranslateService } from '@ngx-translate/core'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ButtonSize } from 'app/shared/components/button.component'; diff --git a/src/main/webapp/app/exercises/shared/participation/participation.component.ts b/src/main/webapp/app/exercises/shared/participation/participation.component.ts index dfdb90b6d53e..627a33ce8815 100644 --- a/src/main/webapp/app/exercises/shared/participation/participation.component.ts +++ b/src/main/webapp/app/exercises/shared/participation/participation.component.ts @@ -17,7 +17,7 @@ import { ProgrammingExerciseStudentParticipation } from 'app/entities/participat import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { faCircleNotch, faCodeBranch, faEraser, faFilePowerpoint, faTable, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; import { GradingSystemService } from 'app/grading-system/grading-system.service'; import { GradeStepsDTO } from 'app/entities/grade-step.model'; diff --git a/src/main/webapp/app/exercises/shared/participation/participation.utils.ts b/src/main/webapp/app/exercises/shared/participation/participation.utils.ts index 7c7a99d3830c..5fc349f22b27 100644 --- a/src/main/webapp/app/exercises/shared/participation/participation.utils.ts +++ b/src/main/webapp/app/exercises/shared/participation/participation.utils.ts @@ -102,7 +102,11 @@ export const isParticipationInDueTime = (participation: Participation, exercise: * @param participation * @param showUngradedResults */ -export function getLatestResultOfStudentParticipation(participation: StudentParticipation, showUngradedResults: boolean): Result | undefined { +export function getLatestResultOfStudentParticipation(participation: StudentParticipation | undefined, showUngradedResults: boolean): Result | undefined { + if (!participation) { + return undefined; + } + // Sort participation results by completionDate desc. if (participation.results) { participation.results = _orderBy(participation.results, 'completionDate', 'desc'); diff --git a/src/main/webapp/app/exercises/shared/plagiarism/plagiarism-split-view/text-submission-viewer/text-submission-viewer.component.ts b/src/main/webapp/app/exercises/shared/plagiarism/plagiarism-split-view/text-submission-viewer/text-submission-viewer.component.ts index dc76831f9517..ba3fd8301876 100644 --- a/src/main/webapp/app/exercises/shared/plagiarism/plagiarism-split-view/text-submission-viewer/text-submission-viewer.component.ts +++ b/src/main/webapp/app/exercises/shared/plagiarism/plagiarism-split-view/text-submission-viewer/text-submission-viewer.component.ts @@ -1,10 +1,10 @@ import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { PlagiarismSubmission } from 'app/exercises/shared/plagiarism/types/PlagiarismSubmission'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { FromToElement, TextSubmissionElement } from 'app/exercises/shared/plagiarism/types/text/TextSubmissionElement'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseType } from 'app/entities/exercise.model'; import { DomainChange, DomainType, FileType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { CodeEditorRepositoryFileService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; diff --git a/src/main/webapp/app/exercises/shared/result/result.component.ts b/src/main/webapp/app/exercises/shared/result/result.component.ts index 9bad543faa64..b323a640b21e 100644 --- a/src/main/webapp/app/exercises/shared/result/result.component.ts +++ b/src/main/webapp/app/exercises/shared/result/result.component.ts @@ -3,11 +3,11 @@ import { ParticipationService } from 'app/exercises/shared/participation/partici import { MissingResultInformation, ResultTemplateStatus, evaluateTemplateStatus, getResultIconClass, getTextColorClass } from 'app/exercises/shared/result/result.utils'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import dayjs from 'dayjs/esm'; import { isProgrammingExerciseStudentParticipation, isResultPreliminary } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; import { Participation, ParticipationType, getExercise } from 'app/entities/participation/participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Submission, SubmissionExerciseType } from 'app/entities/submission.model'; import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exercise.model'; import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; diff --git a/src/main/webapp/app/exercises/shared/result/result.service.ts b/src/main/webapp/app/exercises/shared/result/result.service.ts index 72390f6979c8..800fe1b4aecd 100644 --- a/src/main/webapp/app/exercises/shared/result/result.service.ts +++ b/src/main/webapp/app/exercises/shared/result/result.service.ts @@ -14,8 +14,8 @@ import { convertDateFromClient, convertDateFromServer } from 'app/utils/date.uti import { TranslateService } from '@ngx-translate/core'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { isResultPreliminary } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { captureException } from '@sentry/angular'; import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index 9a997cd2be69..b8b43ed63c80 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -4,9 +4,9 @@ import { StudentParticipation } from 'app/entities/participation/student-partici import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { MIN_SCORE_GREEN, MIN_SCORE_ORANGE } from 'app/app.constants'; import { isProgrammingExerciseStudentParticipation, isResultPreliminary } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Submission, SubmissionExerciseType } from 'app/entities/submission.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCheckCircle, faQuestionCircle, faTimesCircle } from '@fortawesome/free-regular-svg-icons'; @@ -16,7 +16,7 @@ import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; import dayjs from 'dayjs/esm'; import { ResultWithPointsPerGradingCriterion } from 'app/entities/result-with-points-per-grading-criterion.model'; -import { TestCaseResult } from 'app/entities/test-case-result.model'; +import { TestCaseResult } from 'app/entities/programming/test-case-result.model'; /** * Enumeration object representing the possible options that diff --git a/src/main/webapp/app/exercises/shared/result/updating-result.component.ts b/src/main/webapp/app/exercises/shared/result/updating-result.component.ts index 375582cb5132..55cde780b0ec 100644 --- a/src/main/webapp/app/exercises/shared/result/updating-result.component.ts +++ b/src/main/webapp/app/exercises/shared/result/updating-result.component.ts @@ -6,7 +6,7 @@ import { RepositoryService } from 'app/exercises/shared/result/repository.servic import dayjs from 'dayjs/esm'; import { ProgrammingSubmissionService, ProgrammingSubmissionState } from 'app/exercises/programming/participate/programming-submission.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { Submission, SubmissionType } from 'app/entities/submission.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/main/webapp/app/exercises/shared/submission-policy/submission-policy-update.component.ts b/src/main/webapp/app/exercises/shared/submission-policy/submission-policy-update.component.ts index 58220c431757..08961c9407d6 100644 --- a/src/main/webapp/app/exercises/shared/submission-policy/submission-policy-update.component.ts +++ b/src/main/webapp/app/exercises/shared/submission-policy/submission-policy-update.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { LockRepositoryPolicy, SubmissionPenaltyPolicy, SubmissionPolicyType } from 'app/entities/submission-policy.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ diff --git a/src/main/webapp/app/exercises/shared/submission/submission.service.ts b/src/main/webapp/app/exercises/shared/submission/submission.service.ts index 4ab2d0ccdf10..d2cad659ac83 100644 --- a/src/main/webapp/app/exercises/shared/submission/submission.service.ts +++ b/src/main/webapp/app/exercises/shared/submission/submission.service.ts @@ -5,7 +5,7 @@ import { createRequestOption } from 'app/shared/util/request.util'; import { Result } from 'app/entities/result.model'; import { Submission, getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; import { filter, map, tap } from 'rxjs/operators'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Feedback } from 'app/entities/feedback.model'; import { Complaint } from 'app/entities/complaint.model'; import { ComplaintResponseService } from 'app/complaints/complaint-response.service'; diff --git a/src/main/webapp/app/exercises/text/assess/analytics/text-assesment-analytics.service.ts b/src/main/webapp/app/exercises/text/assess/analytics/text-assesment-analytics.service.ts index d37a8a428dd1..22178ca78f3c 100644 --- a/src/main/webapp/app/exercises/text/assess/analytics/text-assesment-analytics.service.ts +++ b/src/main/webapp/app/exercises/text/assess/analytics/text-assesment-analytics.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; -import { TextAssessmentEvent, TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEvent, TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { AccountService } from 'app/core/auth/account.service'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { Location } from '@angular/common'; diff --git a/src/main/webapp/app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component.ts b/src/main/webapp/app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component.ts index 094acf5431b8..5c9c22ab595f 100644 --- a/src/main/webapp/app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component.ts +++ b/src/main/webapp/app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { wordSelection } from 'app/exercises/text/shared/manual-text-selection/manual-text-selection.component'; diff --git a/src/main/webapp/app/exercises/text/assess/text-assessment-area/text-assessment-area.component.ts b/src/main/webapp/app/exercises/text/assess/text-assessment-area/text-assessment-area.component.ts index f834639f59f0..200f7b69d95a 100644 --- a/src/main/webapp/app/exercises/text/assess/text-assessment-area/text-assessment-area.component.ts +++ b/src/main/webapp/app/exercises/text/assess/text-assessment-area/text-assessment-area.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; diff --git a/src/main/webapp/app/exercises/text/assess/text-assessment-base.component.ts b/src/main/webapp/app/exercises/text/assess/text-assessment-base.component.ts index 69eb2bb1847a..8c1f564182d5 100644 --- a/src/main/webapp/app/exercises/text/assess/text-assessment-base.component.ts +++ b/src/main/webapp/app/exercises/text/assess/text-assessment-base.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlock, TextBlockType } from 'app/entities/text-block.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlock, TextBlockType } from 'app/entities/text/text-block.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Result } from 'app/entities/result.model'; import { AccountService } from 'app/core/auth/account.service'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; diff --git a/src/main/webapp/app/exercises/text/assess/text-assessment.service.ts b/src/main/webapp/app/exercises/text/assess/text-assessment.service.ts index 8e2274337833..d996953e4cf6 100644 --- a/src/main/webapp/app/exercises/text/assess/text-assessment.service.ts +++ b/src/main/webapp/app/exercises/text/assess/text-assessment.service.ts @@ -6,11 +6,11 @@ import { Result } from 'app/entities/result.model'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; import { Feedback } from 'app/entities/feedback.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { TextBlock } from 'app/entities/text-block.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { Submission, getLatestSubmissionResult, getSubmissionResultByCorrectionRound, getSubmissionResultById, setLatestSubmissionResult } from 'app/entities/submission.model'; import { Participation } from 'app/entities/participation/participation.model'; -import { TextAssessmentEvent } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEvent } from 'app/entities/text/text-assesment-event.model'; import { AccountService } from 'app/core/auth/account.service'; import { convertDateFromServer } from 'app/utils/date.utils'; diff --git a/src/main/webapp/app/exercises/text/assess/text-submission-assessment.component.ts b/src/main/webapp/app/exercises/text/assess/text-submission-assessment.component.ts index f35fe3bb044d..875555a1492c 100644 --- a/src/main/webapp/app/exercises/text/assess/text-submission-assessment.component.ts +++ b/src/main/webapp/app/exercises/text/assess/text-submission-assessment.component.ts @@ -6,8 +6,8 @@ import { AlertService } from 'app/core/util/alert.service'; import dayjs from 'dayjs/esm'; import { AccountService } from 'app/core/auth/account.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Result } from 'app/entities/result.model'; import { Complaint } from 'app/entities/complaint.model'; import { ComplaintService } from 'app/complaints/complaint.service'; @@ -34,9 +34,9 @@ import { Course } from 'app/entities/course.model'; import { isAllowedToModifyFeedback } from 'app/assessment/assessment.service'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { AssessmentAfterComplaint } from 'app/complaints/complaints-for-tutor/complaints-for-tutor.component'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { AthenaService } from 'app/assessment/athena.service'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { Subscription } from 'rxjs'; @Component({ diff --git a/src/main/webapp/app/exercises/text/assess/text-submission-assessment.route.ts b/src/main/webapp/app/exercises/text/assess/text-submission-assessment.route.ts index 517009175196..83e63bcd21b1 100644 --- a/src/main/webapp/app/exercises/text/assess/text-submission-assessment.route.ts +++ b/src/main/webapp/app/exercises/text/assess/text-submission-assessment.route.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, Routes } from '@angular/router'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { of } from 'rxjs'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { TextSubmissionAssessmentComponent } from './text-submission-assessment.component'; diff --git a/src/main/webapp/app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component.ts b/src/main/webapp/app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component.ts index 1498affd1415..2a34e2b47136 100644 --- a/src/main/webapp/app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component.ts +++ b/src/main/webapp/app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { TextblockFeedbackEditorComponent } from 'app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component'; import { StructuredGradingCriterionService } from 'app/exercises/shared/structured-grading-criterion/structured-grading-criterion.service'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { ActivatedRoute } from '@angular/router'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; diff --git a/src/main/webapp/app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component.ts b/src/main/webapp/app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component.ts index f99baf73f449..72d8ef60ad19 100644 --- a/src/main/webapp/app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component.ts +++ b/src/main/webapp/app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component.ts @@ -1,11 +1,11 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild } from '@angular/core'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { ConfirmIconComponent } from 'app/shared/confirm-icon/confirm-icon.component'; import { StructuredGradingCriterionService } from 'app/exercises/shared/structured-grading-criterion/structured-grading-criterion.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ActivatedRoute } from '@angular/router'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { faAngleRight, faEdit, faExclamation, faExclamationTriangle, faLightbulb, faQuestionCircle, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; diff --git a/src/main/webapp/app/exercises/text/manage/example-text-submission/example-text-submission.component.ts b/src/main/webapp/app/exercises/text/manage/example-text-submission/example-text-submission.component.ts index 3db47679dc4a..1640da7b1978 100644 --- a/src/main/webapp/app/exercises/text/manage/example-text-submission/example-text-submission.component.ts +++ b/src/main/webapp/app/exercises/text/manage/example-text-submission/example-text-submission.component.ts @@ -11,8 +11,8 @@ import { tutorAssessmentTour } from 'app/guided-tour/tours/tutor-assessment-tour import { ExampleSubmission, ExampleSubmissionMode } from 'app/entities/example-submission.model'; import { Feedback, FeedbackCorrectionError, FeedbackType } from 'app/entities/feedback.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import { setLatestSubmissionResult } from 'app/entities/submission.model'; import { TextAssessmentBaseComponent } from 'app/exercises/text/assess/text-assessment-base.component'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-detail.component.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-detail.component.ts index 72a0414b70fe..4199461314b2 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-detail.component.ts @@ -3,7 +3,7 @@ import { SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { HttpResponse } from '@angular/common/http'; import { Subscription } from 'rxjs'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextExerciseService } from './text-exercise.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-paging.service.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-paging.service.ts index df3c7f767a84..ed167d578dd0 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-paging.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; @Injectable({ providedIn: 'root' }) diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-row-buttons.component.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-row-buttons.component.ts index fff2f2a799f8..00e2973a5cc6 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-row-buttons.component.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-row-buttons.component.ts @@ -2,7 +2,7 @@ import { Component, Input } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Subject } from 'rxjs'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { EventManager } from 'app/core/util/event-manager.service'; import { faBook, faTable, faTrash, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.ts index e55e5fb28db2..a9304c422e89 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextExerciseService } from './text-exercise.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.component.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.component.ts index 7714fe2a28eb..ac46f89650d7 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.component.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextExerciseService } from './text-exercise.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.route.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.route.ts index 67c3f64a6dd4..2e3198bc4186 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.route.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.route.ts @@ -2,7 +2,7 @@ import { ActivatedRouteSnapshot, Resolve, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { TextExerciseDetailComponent } from './text-exercise-detail.component'; import { TextExerciseUpdateComponent } from './text-exercise-update.component'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Injectable } from '@angular/core'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.service.ts b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.service.ts index 6cdfb5c91e95..994f1ac75eeb 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.service.ts +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise.service.ts @@ -3,7 +3,7 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { createRequestOption } from 'app/shared/util/request.util'; import { ExerciseServicable, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { TextPlagiarismResult } from 'app/exercises/shared/plagiarism/types/text/TextPlagiarismResult'; diff --git a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts index 08be104f935c..82d8de5b26cf 100644 --- a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts +++ b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts @@ -14,10 +14,10 @@ import { TextSubmissionService } from 'app/exercises/text/participate/text-submi import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { Feedback } from 'app/entities/feedback.model'; import { hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ButtonType } from 'app/shared/components/button.component'; import { Result } from 'app/entities/result.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; import { AccountService } from 'app/core/auth/account.service'; import { getFirstResultWithComplaint, getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; diff --git a/src/main/webapp/app/exercises/text/participate/text-result/text-result-block.ts b/src/main/webapp/app/exercises/text/participate/text-result/text-result-block.ts index 2ed52a5002fa..33960c213bcc 100644 --- a/src/main/webapp/app/exercises/text/participate/text-result/text-result-block.ts +++ b/src/main/webapp/app/exercises/text/participate/text-result/text-result-block.ts @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCheck, faCheckCircle, faCircle, faDotCircle, faTimes, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; import { Feedback } from 'app/entities/feedback.model'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { convertToHtmlLinebreaks, escapeString } from 'app/utils/text.utils'; enum FeedbackType { diff --git a/src/main/webapp/app/exercises/text/participate/text-result/text-result.component.ts b/src/main/webapp/app/exercises/text/participate/text-result/text-result.component.ts index 681266e5ef85..c63aaece1335 100644 --- a/src/main/webapp/app/exercises/text/participate/text-result/text-result.component.ts +++ b/src/main/webapp/app/exercises/text/participate/text-result/text-result.component.ts @@ -1,10 +1,10 @@ import { Component, Input } from '@angular/core'; import { Feedback, buildFeedbackTextForReview, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import { TextResultBlock } from './text-result-block'; import { TranslateService } from '@ngx-translate/core'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { LocaleConversionService } from 'app/shared/service/locale-conversion.service'; import { Course } from 'app/entities/course.model'; diff --git a/src/main/webapp/app/exercises/text/participate/text-submission.service.ts b/src/main/webapp/app/exercises/text/participate/text-submission.service.ts index 26d493d36b75..2c90d0c7188c 100644 --- a/src/main/webapp/app/exercises/text/participate/text-submission.service.ts +++ b/src/main/webapp/app/exercises/text/participate/text-submission.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { createRequestOption } from 'app/shared/util/request.util'; import { stringifyCircular } from 'app/shared/util/utils'; import { getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; diff --git a/src/main/webapp/app/exercises/text/shared/manual-text-selection/manual-text-selection.component.ts b/src/main/webapp/app/exercises/text/shared/manual-text-selection/manual-text-selection.component.ts index d4cd37b3b8a6..1a846ba58988 100644 --- a/src/main/webapp/app/exercises/text/shared/manual-text-selection/manual-text-selection.component.ts +++ b/src/main/webapp/app/exercises/text/shared/manual-text-selection/manual-text-selection.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { ActivatedRoute } from '@angular/router'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { TextBlockRefGroup } from 'app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component'; export type wordSelection = { diff --git a/src/main/webapp/app/grading-system/base-grading-system/base-grading-system.component.ts b/src/main/webapp/app/grading-system/base-grading-system/base-grading-system.component.ts index a53bc9c26f75..21e2a3aef781 100644 --- a/src/main/webapp/app/grading-system/base-grading-system/base-grading-system.component.ts +++ b/src/main/webapp/app/grading-system/base-grading-system/base-grading-system.component.ts @@ -9,7 +9,7 @@ import { HttpResponse } from '@angular/common/http'; import { catchError, finalize } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { download, generateCsv, mkConfig } from 'export-to-csv'; diff --git a/src/main/webapp/app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component.ts b/src/main/webapp/app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component.ts index 05bb0f2689c8..074c1e704695 100644 --- a/src/main/webapp/app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component.ts +++ b/src/main/webapp/app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component.ts @@ -1,10 +1,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BuildAgent } from 'app/entities/build-agent.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; import { BuildAgentsService } from 'app/localci/build-agents/build-agents.service'; import { Subscription } from 'rxjs'; import { faCircleCheck, faExclamationCircle, faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; -import { TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { ActivatedRoute } from '@angular/router'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { BuildQueueService } from 'app/localci/build-queue/build-queue.service'; diff --git a/src/main/webapp/app/localci/build-agents/build-agent-summary/build-agent-summary.component.ts b/src/main/webapp/app/localci/build-agents/build-agent-summary/build-agent-summary.component.ts index cac89931b3be..b7f232bf0fc4 100644 --- a/src/main/webapp/app/localci/build-agents/build-agent-summary/build-agent-summary.component.ts +++ b/src/main/webapp/app/localci/build-agents/build-agent-summary/build-agent-summary.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { BuildAgent } from 'app/entities/build-agent.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { BuildAgentsService } from 'app/localci/build-agents/build-agents.service'; import { Subscription } from 'rxjs'; diff --git a/src/main/webapp/app/localci/build-agents/build-agents.service.ts b/src/main/webapp/app/localci/build-agents/build-agents.service.ts index 1acca8c34375..99905ad80c51 100644 --- a/src/main/webapp/app/localci/build-agents/build-agents.service.ts +++ b/src/main/webapp/app/localci/build-agents/build-agents.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; -import { BuildAgent } from 'app/entities/build-agent.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) diff --git a/src/main/webapp/app/localci/build-queue/build-queue.component.ts b/src/main/webapp/app/localci/build-queue/build-queue.component.ts index 2ab4ae9bc7f8..79ca4931786c 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.component.ts +++ b/src/main/webapp/app/localci/build-queue/build-queue.component.ts @@ -1,11 +1,11 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { BuildJob, BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/build-job.model'; +import { BuildJob, BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/programming/build-job.model'; import { faAngleDown, faAngleRight, faCircleCheck, faExclamationCircle, faExclamationTriangle, faFilter, faSort, faSync, faTimes } from '@fortawesome/free-solid-svg-icons'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { BuildQueueService } from 'app/localci/build-queue/build-queue.service'; import { debounceTime, distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators'; -import { TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants'; import { SortingOrder } from 'app/shared/table/pageable-table'; import { onError } from 'app/shared/util/global.utils'; diff --git a/src/main/webapp/app/localci/build-queue/build-queue.service.ts b/src/main/webapp/app/localci/build-queue/build-queue.service.ts index c0a958f84715..fdc9bec66e2f 100644 --- a/src/main/webapp/app/localci/build-queue/build-queue.service.ts +++ b/src/main/webapp/app/localci/build-queue/build-queue.service.ts @@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { throwError } from 'rxjs'; -import { BuildJob, BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/build-job.model'; +import { BuildJob, BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/programming/build-job.model'; import { createNestedRequestOption } from 'app/shared/util/request.util'; import { HttpResponse } from '@angular/common/http'; import { FinishedBuildJobFilter } from 'app/localci/build-queue/build-queue.component'; diff --git a/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts b/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts index 617797f048c1..98ced261dc32 100644 --- a/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts +++ b/src/main/webapp/app/localvc/commit-details-view/commit-details-view.component.ts @@ -4,7 +4,7 @@ import { ProgrammingExerciseService } from 'app/exercises/programming/manage/ser import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { Subscription, throwError } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; -import { CommitInfo } from 'app/entities/programming-submission.model'; +import { CommitInfo } from 'app/entities/programming/programming-submission.model'; import dayjs from 'dayjs/esm'; import { catchError, map, tap } from 'rxjs/operators'; diff --git a/src/main/webapp/app/localvc/commit-history/commit-history.component.ts b/src/main/webapp/app/localvc/commit-history/commit-history.component.ts index 056104710066..fe98dd02cb53 100644 --- a/src/main/webapp/app/localvc/commit-history/commit-history.component.ts +++ b/src/main/webapp/app/localvc/commit-history/commit-history.component.ts @@ -3,11 +3,11 @@ import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import dayjs from 'dayjs/esm'; import { ExerciseType } from 'app/entities/exercise.model'; -import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { tap } from 'rxjs/operators'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; diff --git a/src/main/webapp/app/localvc/repository-view/repository-view.component.ts b/src/main/webapp/app/localvc/repository-view/repository-view.component.ts index 8125fbbcb1d6..22ee9747edbb 100644 --- a/src/main/webapp/app/localvc/repository-view/repository-view.component.ts +++ b/src/main/webapp/app/localvc/repository-view/repository-view.component.ts @@ -3,7 +3,7 @@ import { Observable, Subscription } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { ExerciseType, getCourseFromExercise } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/orion/assessment/orion-assessment.service.ts b/src/main/webapp/app/orion/assessment/orion-assessment.service.ts index 27a068b37f2e..9463fc7f3e32 100644 --- a/src/main/webapp/app/orion/assessment/orion-assessment.service.ts +++ b/src/main/webapp/app/orion/assessment/orion-assessment.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingAssessmentRepoExportService, RepositoryExportOptions } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; diff --git a/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts b/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts index 5728cd2c99be..2854122be21c 100644 --- a/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts +++ b/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { ExerciseView, OrionState } from 'app/shared/orion/orion'; import { Router } from '@angular/router'; diff --git a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts index aa415ecd9432..30f6b585e32c 100644 --- a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts +++ b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { ExerciseView, OrionState } from 'app/shared/orion/orion'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; diff --git a/src/main/webapp/app/overview/course-exams/course-exams.component.ts b/src/main/webapp/app/overview/course-exams/course-exams.component.ts index 916f669790e3..1b78c89b712e 100644 --- a/src/main/webapp/app/overview/course-exams/course-exams.component.ts +++ b/src/main/webapp/app/overview/course-exams/course-exams.component.ts @@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { StudentExam } from 'app/entities/student-exam.model'; diff --git a/src/main/webapp/app/overview/course-exercises/course-exercises.component.html b/src/main/webapp/app/overview/course-exercises/course-exercises.component.html index 2de6ec14a991..6be118bf7475 100644 --- a/src/main/webapp/app/overview/course-exercises/course-exercises.component.html +++ b/src/main/webapp/app/overview/course-exercises/course-exercises.component.html @@ -1,7 +1,7 @@
@if (course) {
- +
@if (exerciseSelected) { diff --git a/src/main/webapp/app/overview/course-overview.service.ts b/src/main/webapp/app/overview/course-overview.service.ts index ab2f9f32744d..0432dc2e361b 100644 --- a/src/main/webapp/app/overview/course-overview.service.ts +++ b/src/main/webapp/app/overview/course-overview.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Exercise, getIcon } from 'app/entities/exercise.model'; import { Lecture } from 'app/entities/lecture.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils'; diff --git a/src/main/webapp/app/overview/courses.component.ts b/src/main/webapp/app/overview/courses.component.ts index 73ce68512b8c..0d5218bb21b0 100644 --- a/src/main/webapp/app/overview/courses.component.ts +++ b/src/main/webapp/app/overview/courses.component.ts @@ -8,7 +8,7 @@ import { courseOverviewTour } from 'app/guided-tour/tours/course-overview-tour'; import { TeamService } from 'app/exercises/shared/team/team.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import dayjs from 'dayjs/esm'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Router } from '@angular/router'; import { faPenAlt } from '@fortawesome/free-solid-svg-icons'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts index 260428c680e3..d0b7264723b6 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts @@ -16,7 +16,7 @@ import { StudentParticipation } from 'app/entities/participation/student-partici import { ExampleSolutionInfo, ExerciseDetailsType, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { AlertService } from 'app/core/util/alert.service'; import { TeamAssignmentPayload } from 'app/entities/team.model'; diff --git a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html index ea54cf40b8c2..b2d119d0cd2a 100644 --- a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html +++ b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html @@ -126,6 +126,7 @@ [participations]="exercise.studentParticipations!" [exercise]="exercise" [routerLinkForRepositoryView]="repositoryLink + '/repository/' + exercise.studentParticipations![0].id" + [useParticipationVcsAccessToken]="true" /> } @if (theiaEnabled) { diff --git a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts index f2e251a81804..26e3bec0a7b0 100644 --- a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts +++ b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts @@ -7,7 +7,7 @@ import { InitializationState } from 'app/entities/participation/participation.mo import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { hasExerciseDueDatePassed, isResumeExerciseAvailable, isStartExerciseAvailable, isStartPracticeAvailable } from 'app/exercises/shared/exercise/exercise.utils'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ArtemisQuizService } from 'app/shared/quiz/quiz.service'; import { finalize } from 'rxjs/operators'; @@ -61,14 +61,14 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges theiaPortalURL: string; // Icons - faFolderOpen = faFolderOpen; - faUsers = faUsers; - faEye = faEye; - faPlayCircle = faPlayCircle; - faRedo = faRedo; - faCodeBranch = faCodeBranch; - faDesktop = faDesktop; - faPenSquare = faPenSquare; + readonly faFolderOpen = faFolderOpen; + readonly faUsers = faUsers; + readonly faEye = faEye; + readonly faPlayCircle = faPlayCircle; + readonly faRedo = faRedo; + readonly faCodeBranch = faCodeBranch; + readonly faDesktop = faDesktop; + readonly faPenSquare = faPenSquare; private feedbackSent = false; @@ -109,7 +109,7 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges this.athenaEnabled = profileInfo.activeProfiles?.includes(PROFILE_ATHENA); // The online IDE is only available with correct SpringProfile and if it's enabled for this exercise - if (profileInfo.activeProfiles?.includes(PROFILE_THEIA)) { + if (profileInfo.activeProfiles?.includes(PROFILE_THEIA) && this.programmingExercise) { this.theiaEnabled = true; // Set variables now, sanitize later on @@ -119,6 +119,16 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges if (this.theiaPortalURL === '') { this.theiaEnabled = false; } + + // Verify that the exercise allows the online IDE + if (!this.programmingExercise.allowOnlineIde) { + this.theiaEnabled = false; + } + + // Verify that the exercise has a theia blueprint configured + if (!this.programmingExercise.buildConfig?.theiaImage) { + this.theiaEnabled = false; + } } }); } else if (this.exercise.type === ExerciseType.MODELING) { diff --git a/src/main/webapp/app/overview/participation-websocket.service.ts b/src/main/webapp/app/overview/participation-websocket.service.ts index 97e802f2350c..3836e7a81209 100644 --- a/src/main/webapp/app/overview/participation-websocket.service.ts +++ b/src/main/webapp/app/overview/participation-websocket.service.ts @@ -9,7 +9,7 @@ import { ParticipationService } from 'app/exercises/shared/participation/partici import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import dayjs from 'dayjs/esm'; import { cloneDeep } from 'lodash-es'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; const PERSONAL_PARTICIPATION_TOPIC = `/user/topic/newResults`; const EXERCISE_PARTICIPATION_TOPIC = (exerciseId: number) => `/topic/exercise/${exerciseId}/newResults`; diff --git a/src/main/webapp/app/shared/category-selector/category-selector.component.ts b/src/main/webapp/app/shared/category-selector/category-selector.component.ts index 03b24cac6ca1..d899a7b034b3 100644 --- a/src/main/webapp/app/shared/category-selector/category-selector.component.ts +++ b/src/main/webapp/app/shared/category-selector/category-selector.component.ts @@ -1,13 +1,12 @@ import { Component, ElementRef, EventEmitter, Input, OnChanges, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { ColorSelectorComponent } from 'app/shared/color-selector/color-selector.component'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; -import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; +import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { COMMA, ENTER, TAB } from '@angular/cdk/keycodes'; import { FormControl } from '@angular/forms'; import { MatChipInputEvent } from '@angular/material/chips'; import { Observable, map, startWith } from 'rxjs'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; -import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; const DEFAULT_COLORS = ['#6ae8ac', '#9dca53', '#94a11c', '#691b0b', '#ad5658', '#1b97ca', '#0d3cc2', '#0ab84f']; @@ -44,8 +43,7 @@ export class CategorySelectorComponent implements OnChanges { separatorKeysCodes = [ENTER, COMMA, TAB]; categoryCtrl = new FormControl(undefined); - // Icons - faTimes = faTimes; + readonly faTimes = faTimes; ngOnChanges() { this.uniqueCategoriesForAutocomplete = this.categoryCtrl.valueChanges.pipe( @@ -134,10 +132,7 @@ export class CategorySelectorComponent implements OnChanges { } private createCategory(categoryString: string): ExerciseCategory { - const category = new ExerciseCategory(); - category.category = categoryString; - category.color = this.chooseRandomColor(); - return category; + return new ExerciseCategory(categoryString, this.chooseRandomColor()); } private chooseRandomColor(): string { diff --git a/src/main/webapp/app/shared/components/code-button/code-button.component.html b/src/main/webapp/app/shared/components/code-button/code-button.component.html index 89d1b5aa952f..c9d791edac00 100644 --- a/src/main/webapp/app/shared/components/code-button/code-button.component.html +++ b/src/main/webapp/app/shared/components/code-button/code-button.component.html @@ -14,8 +14,14 @@ container="body" > - @if (useSsh && (!user.sshPublicKey || gitlabVCEnabled)) { -
+ @if (useSsh && !copyEnabled) { +
+ } + @if (useToken && !copyEnabled && tokenMissing) { +
+ } + @if (useToken && !copyEnabled && tokenExpired) { +
} @if (participations && participations.length > 1) {
@@ -34,7 +40,7 @@
{{ cloneHeadline | artemisTranslate }}
@if (showCloneUrlWithoutToken) { HTTPS } - @if (useVersionControlAccessToken && participations) { + @if (accessTokensEnabled && (!useParticipationVcsAccessToken || participations)) { Token } SSH @@ -50,7 +56,7 @@
{{ cloneHeadline | artemisTranslate }}
'url-box-remove-line-right': !localVCEnabled || !!routerLinkForRepositoryView, }" [cdkCopyToClipboard]="getHttpOrSshRepositoryUri(false)" - (cdkCopyToClipboardCopied)="onCopyFinished($event)" + (cdkCopyToClipboardCopied)="copyEnabled ? onCopyFinished($event) : null" >{{ getHttpOrSshRepositoryUri() }}
@@ -86,6 +92,7 @@
{{ cloneHeadline | artemisTranslate }}
+ } +
+ diff --git a/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.scss b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.scss new file mode 100644 index 000000000000..43a2d70cca34 --- /dev/null +++ b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.scss @@ -0,0 +1,18 @@ +.remove-button { + color: var(--white); + background: transparent !important; + border: none !important; + + &:hover { + opacity: 0.8; + } +} + +/** category options should be consistent with the type CategoryFontSize */ +.category-small { + font-size: 0.85rem; +} + +.category-default { + font-size: 1rem; +} diff --git a/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts new file mode 100644 index 000000000000..8ba41f96ba8b --- /dev/null +++ b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts @@ -0,0 +1,23 @@ +import { Component, Input } from '@angular/core'; +import type { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { CommonModule } from '@angular/common'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; + +type CategoryFontSize = 'default' | 'small'; + +@Component({ + selector: 'jhi-custom-exercise-category-badge', + templateUrl: './custom-exercise-category-badge.component.html', + styleUrls: ['custom-exercise-category-badge.component.scss'], + standalone: true, + imports: [CommonModule, FontAwesomeModule], +}) +export class CustomExerciseCategoryBadgeComponent { + protected readonly faTimes = faTimes; + + @Input({ required: true }) category: ExerciseCategory; + @Input() displayRemoveButton: boolean = false; + @Input() onClick: () => void = () => {}; + @Input() fontSize: CategoryFontSize = 'default'; +} diff --git a/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.html b/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.html index dd757a2d5b36..f811a3f4c4e7 100644 --- a/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.html +++ b/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.html @@ -11,9 +11,7 @@

} @for (category of exercise.categories; track category) { -

- {{ category.category }} -

+ } @if (exercise.difficulty && showTags.difficulty) {

diff --git a/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts b/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts index f56611d32021..f0f8877ffdec 100644 --- a/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts +++ b/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts @@ -3,9 +3,10 @@ import { RouterModule } from '@angular/router'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule, ArtemisSharedComponentModule], + imports: [ArtemisSharedModule, RouterModule, ArtemisSharedComponentModule, CustomExerciseCategoryBadgeComponent], declarations: [ExerciseCategoriesComponent], exports: [ExerciseCategoriesComponent], }) diff --git a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.html b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.html new file mode 100644 index 000000000000..1a87237a4d46 --- /dev/null +++ b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.html @@ -0,0 +1,145 @@ +
+ + + +
diff --git a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.scss b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.scss new file mode 100644 index 000000000000..c2db4383039b --- /dev/null +++ b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.scss @@ -0,0 +1,20 @@ +/* +ensures that the category dropdown selection does not overflow the screen to the bottom +(instead a scrollbar will be displayed) + */ +:host ::ng-deep ngb-typeahead-window.dropdown-menu { + max-height: 25rem; + overflow-y: auto; +} + +/* align the first checkbox to the left */ +.no-left-margin-padding:first-child { + margin-left: 0; + padding-left: 0; +} + +/* otherwise the dropdown changes the color on hover if no further options can be selected */ +.form-control:disabled, +.form-control[disabled] { + pointer-events: none; +} diff --git a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts new file mode 100644 index 000000000000..8b1bf7c58488 --- /dev/null +++ b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts @@ -0,0 +1,231 @@ +import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; +import { NgbActiveModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; +import { faBackward, faFilter } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { SidebarCardElement, SidebarData } from 'app/types/sidebar'; +import { Observable, OperatorFunction, Subject, merge } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators'; +import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component'; +import { RangeSliderComponent } from 'app/shared/range-slider/range-slider.component'; +import { + DifficultyFilterOption, + ExerciseCategoryFilterOption, + ExerciseFilterOptions, + ExerciseFilterResults, + ExerciseTypeFilterOption, + FilterDetails, + FilterOption, + RangeFilter, +} from 'app/types/exercise-filter'; +import { satisfiesFilters } from 'app/shared/exercise-filter/exercise-filter-modal.helper'; +import { DifficultyLevel, ExerciseType } from 'app/entities/exercise.model'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { isRangeFilterApplied } from 'app/shared/sidebar/sidebar.helper'; + +@Component({ + selector: 'jhi-exercise-filter-modal', + templateUrl: './exercise-filter-modal.component.html', + styleUrls: ['./exercise-filter-modal.component.scss'], + standalone: true, + imports: [ + FormsModule, + ReactiveFormsModule, + FontAwesomeModule, + ArtemisSharedCommonModule, + ArtemisSharedComponentModule, + CustomExerciseCategoryBadgeComponent, + RangeSliderComponent, + ], +}) +export class ExerciseFilterModalComponent implements OnInit { + readonly faFilter = faFilter; + readonly faBackward = faBackward; + + @Output() filterApplied = new EventEmitter(); + + @ViewChild('categoriesFilterSelection', { static: false }) instance: NgbTypeahead; + + selectedCategoryOptions: ExerciseCategoryFilterOption[] = []; + selectableCategoryOptions: ExerciseCategoryFilterOption[] = []; + + noFiltersAvailable: boolean = false; + + focus$ = new Subject(); + click$ = new Subject(); + + form: FormGroup; + + model?: string; + + sidebarData?: SidebarData; + + categoryFilter?: FilterOption; + typeFilter?: FilterOption; + difficultyFilter?: FilterOption; + achievablePoints?: RangeFilter; + achievedScore?: RangeFilter; + + exerciseFilters?: ExerciseFilterOptions; + + constructor(private activeModal: NgbActiveModal) {} + + ngOnInit() { + this.categoryFilter = this.exerciseFilters?.categoryFilter; + this.typeFilter = this.exerciseFilters?.exerciseTypesFilter; + this.difficultyFilter = this.exerciseFilters?.difficultyFilter; + this.achievablePoints = this.exerciseFilters?.achievablePoints; + this.achievedScore = this.exerciseFilters?.achievedScore; + + this.noFiltersAvailable = !( + this.categoryFilter?.isDisplayed || + this.typeFilter?.isDisplayed || + this.difficultyFilter?.isDisplayed || + this.achievedScore?.isDisplayed || + this.achievablePoints?.isDisplayed + ); + + this.updateCategoryOptionsStates(); + } + + closeModal(): void { + this.activeModal.close(); + } + + search: OperatorFunction = (text$: Observable) => { + const debouncedText$ = text$.pipe(debounceTime(200), distinctUntilChanged()); + const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen())); + const inputFocus$ = this.focus$; + + return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$).pipe( + map((term) => + term === '' + ? this.selectableCategoryOptions + : this.selectableCategoryOptions.filter((categoryFilter: ExerciseCategoryFilterOption) => { + if (categoryFilter.category.category !== undefined) { + return categoryFilter.category.category?.toLowerCase().indexOf(term.toLowerCase()) > -1; + } + + return false; + }), + ), + ); + }; + resultFormatter = (exerciseCategory: ExerciseCategoryFilterOption) => exerciseCategory.category.category ?? ''; + + onSelectItem(event: any) { + const isEnterPressedForNotExistingItem = !event.item; + if (isEnterPressedForNotExistingItem) { + event.preventDefault(); + event.stopPropagation(); + return; + } + + event.preventDefault(); // otherwise clearing the input field will not work https://stackoverflow.com/questions/39783936/how-to-clear-the-typeahead-input-after-a-result-is-selected + const filterOption: ExerciseCategoryFilterOption = event.item; + filterOption.searched = true; + this.updateCategoryOptionsStates(); + this.model = undefined; // Clear the input field after selection + } + + removeItem(item: ExerciseCategoryFilterOption): () => void { + return () => { + item.searched = false; + this.updateCategoryOptionsStates(); + }; + } + + applyFilter(): void { + if (!this.sidebarData?.groupedData) { + return; + } + + const appliedFilterDetails = this.getAppliedFilterDetails(); + for (const groupedDataKey in this.sidebarData.groupedData) { + this.sidebarData.groupedData[groupedDataKey].entityData = this.sidebarData.groupedData[groupedDataKey].entityData.filter((sidebarElement) => + satisfiesFilters(sidebarElement, appliedFilterDetails), + ); + } + this.sidebarData.ungroupedData = this.sidebarData.ungroupedData?.filter((sidebarElement: SidebarCardElement) => satisfiesFilters(sidebarElement, appliedFilterDetails)); + + this.filterApplied.emit({ + filteredSidebarData: this.sidebarData, + appliedExerciseFilters: this.exerciseFilters, + isFilterActive: this.isFilterActive(appliedFilterDetails), + }); + + this.closeModal(); + } + + private getAppliedFilterDetails(): FilterDetails { + return { + searchedTypes: this.getSearchedTypes(), + selectedCategories: this.getSelectedCategories(), + searchedDifficulties: this.getSearchedDifficulties(), + isScoreFilterApplied: isRangeFilterApplied(this.achievedScore), + isPointsFilterApplied: isRangeFilterApplied(this.achievablePoints), + achievedScore: this.achievedScore, + achievablePoints: this.achievablePoints, + }; + } + + private getSearchedTypes(): ExerciseType[] | undefined { + return this.typeFilter?.options.filter((type) => type.checked).map((type) => type.value); + } + + private getSelectedCategories(): ExerciseCategory[] { + return this.selectedCategoryOptions + .filter((categoryOption: ExerciseCategoryFilterOption) => categoryOption.searched) + .map((categoryOption: ExerciseCategoryFilterOption) => categoryOption.category); + } + + private getSearchedDifficulties(): DifficultyLevel[] | undefined { + return this.difficultyFilter?.options.filter((difficulty) => difficulty.checked).map((difficulty) => difficulty.value); + } + + private isFilterActive(filterDetails: FilterDetails): boolean { + return ( + !!filterDetails.selectedCategories.length || + !!filterDetails.searchedTypes?.length || + !!filterDetails.searchedDifficulties?.length || + filterDetails.isScoreFilterApplied || + filterDetails.isPointsFilterApplied + ); + } + + clearFilter() { + this.categoryFilter?.options.forEach((categoryOption) => (categoryOption.searched = false)); + this.typeFilter?.options.forEach((typeOption) => (typeOption.checked = false)); + this.difficultyFilter?.options.forEach((difficultyOption) => (difficultyOption.checked = false)); + + this.resetRangeFilter(this.achievedScore); + this.resetRangeFilter(this.achievablePoints); + + this.applyFilter(); + } + + private resetRangeFilter(rangeFilter?: RangeFilter) { + if (!rangeFilter?.filter) { + return; + } + + const filter = rangeFilter.filter; + filter.selectedMin = filter.generalMin; + filter.selectedMax = filter.generalMax; + } + + private updateCategoryOptionsStates() { + this.selectedCategoryOptions = this.getUpdatedSelectedCategoryOptions(); + this.selectableCategoryOptions = this.getSelectableCategoryOptions(); + } + + private getUpdatedSelectedCategoryOptions(): ExerciseCategoryFilterOption[] { + return this.categoryFilter?.options.filter((categoryFilter) => categoryFilter.searched) ?? []; + } + + private getSelectableCategoryOptions(): ExerciseCategoryFilterOption[] { + return this.categoryFilter?.options.filter((categoryFilter) => !categoryFilter.searched) ?? []; + } +} diff --git a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.helper.ts b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.helper.ts new file mode 100644 index 000000000000..de922fce195d --- /dev/null +++ b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.helper.ts @@ -0,0 +1,84 @@ +import { SidebarCardElement } from 'app/types/sidebar'; +import { DifficultyLevel, ExerciseType } from 'app/entities/exercise.model'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { FilterDetails, RangeFilter } from 'app/types/exercise-filter'; +import { getLatestResultOfStudentParticipation } from 'app/exercises/shared/participation/participation.utils'; + +export function satisfiesDifficultyFilter(sidebarElement: SidebarCardElement, searchedDifficulties?: DifficultyLevel[]): boolean { + if (!searchedDifficulties?.length) { + return true; + } + if (!sidebarElement.difficulty) { + return false; + } + + return searchedDifficulties.includes(sidebarElement.difficulty); +} + +export function satisfiesTypeFilter(sidebarElement: SidebarCardElement, searchedTypes?: ExerciseType[]): boolean { + if (!searchedTypes?.length) { + return true; + } + if (!sidebarElement.exercise?.type) { + return false; + } + + return searchedTypes.includes(sidebarElement.exercise.type); +} + +export function satisfiesCategoryFilter(sidebarElement: SidebarCardElement, selectedCategories: ExerciseCategory[]): boolean { + if (!selectedCategories.length) { + return true; + } + if (!sidebarElement?.exercise?.categories) { + return false; + } + + // noinspection UnnecessaryLocalVariableJS: not inlined because the variable name improves readability + const isAnyExerciseCategoryMatchingASelectedCategory = sidebarElement.exercise.categories.some((category) => + selectedCategories.some((selectedCategory) => selectedCategory.equals(category)), + ); + return isAnyExerciseCategoryMatchingASelectedCategory; +} + +export function satisfiesScoreFilter(sidebarElement: SidebarCardElement, isFilterApplied: boolean, achievedScoreFilter?: RangeFilter): boolean { + if (!isFilterApplied || !achievedScoreFilter) { + return true; + } + + const latestResult = getLatestResultOfStudentParticipation(sidebarElement.studentParticipation, true); + if (!latestResult?.score) { + return achievedScoreFilter.filter.selectedMin === 0; + } + + const isScoreInSelectedMinRange = latestResult.score >= achievedScoreFilter.filter.selectedMin; + const isScoreInSelectedMaxRange = latestResult.score <= achievedScoreFilter.filter.selectedMax; + + return isScoreInSelectedMinRange && isScoreInSelectedMaxRange; +} + +export function satisfiesPointsFilter(sidebarElement: SidebarCardElement, isPointsFilterApplied: boolean, achievablePointsFilter?: RangeFilter): boolean { + if (!isPointsFilterApplied || !achievablePointsFilter) { + return true; + } + + /** {@link Exercise.maxPoints} must be in the range 1 - 9999 */ + if (!sidebarElement.exercise?.maxPoints) { + return false; + } + + const isAchievablePointsInSelectedMinRange = sidebarElement.exercise.maxPoints >= achievablePointsFilter.filter.selectedMin; + const isAchievablePointsInSelectedMaxRange = sidebarElement.exercise.maxPoints <= achievablePointsFilter.filter.selectedMax; + + return isAchievablePointsInSelectedMinRange && isAchievablePointsInSelectedMaxRange; +} + +export function satisfiesFilters(sidebarElement: SidebarCardElement, filterDetails: FilterDetails) { + return ( + satisfiesCategoryFilter(sidebarElement, filterDetails.selectedCategories) && + satisfiesDifficultyFilter(sidebarElement, filterDetails.searchedDifficulties) && + satisfiesTypeFilter(sidebarElement, filterDetails.searchedTypes) && + satisfiesScoreFilter(sidebarElement, filterDetails.isScoreFilterApplied, filterDetails.achievedScore) && + satisfiesPointsFilter(sidebarElement, filterDetails.isPointsFilterApplied, filterDetails.achievablePoints) + ); +} diff --git a/src/main/webapp/app/shared/http/file.service.ts b/src/main/webapp/app/shared/http/file.service.ts index 330d449bc4f6..c14136a6688d 100644 --- a/src/main/webapp/app/shared/http/file.service.ts +++ b/src/main/webapp/app/shared/http/file.service.ts @@ -4,7 +4,7 @@ import { lastValueFrom } from 'rxjs'; import { v4 as uuid } from 'uuid'; import { Observable } from 'rxjs'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; @Injectable({ providedIn: 'root' }) export class FileService { diff --git a/src/main/webapp/app/shared/orion/orion-build-and-test.service.ts b/src/main/webapp/app/shared/orion/orion-build-and-test.service.ts index fd3e57b80ddd..d46ee941b24a 100644 --- a/src/main/webapp/app/shared/orion/orion-build-and-test.service.ts +++ b/src/main/webapp/app/shared/orion/orion-build-and-test.service.ts @@ -5,12 +5,12 @@ import { filter, map, tap } from 'rxjs/operators'; import { Observable, Subject, Subscription } from 'rxjs'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; import { Participation } from 'app/entities/participation/participation.model'; -import { BuildLogEntryArray } from 'app/entities/build-log.model'; -import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { BuildLogEntryArray } from 'app/entities/programming/build-log.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { Result } from 'app/entities/result.model'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { Feedback } from 'app/entities/feedback.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; /** * Notifies the IDE about a result, that is currently building and forwards incoming test results. diff --git a/src/main/webapp/app/shared/orion/orion-connector.service.ts b/src/main/webapp/app/shared/orion/orion-connector.service.ts index b49f8745e8db..61c8a0933cec 100644 --- a/src/main/webapp/app/shared/orion/orion-connector.service.ts +++ b/src/main/webapp/app/shared/orion/orion-connector.service.ts @@ -4,7 +4,7 @@ import { ExerciseView, OrionState } from 'app/shared/orion/orion'; import { Router } from '@angular/router'; import { REPOSITORY } from 'app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component'; import { stringifyCircular } from 'app/shared/util/utils'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Feedback } from 'app/entities/feedback.model'; import { OrionTutorAssessmentComponent } from 'app/orion/assessment/orion-tutor-assessment.component'; import { AlertService } from 'app/core/util/alert.service'; diff --git a/src/main/webapp/app/shared/range-slider/range-slider.component.html b/src/main/webapp/app/shared/range-slider/range-slider.component.html new file mode 100644 index 000000000000..cf6170502703 --- /dev/null +++ b/src/main/webapp/app/shared/range-slider/range-slider.component.html @@ -0,0 +1,32 @@ +
+
+
+ {{ selectedMinValue }}{{ labelSymbol }} + {{ selectedMaxValue }}{{ labelSymbol }} + + + +
+
diff --git a/src/main/webapp/app/shared/range-slider/range-slider.component.scss b/src/main/webapp/app/shared/range-slider/range-slider.component.scss new file mode 100644 index 000000000000..2342957d2e6d --- /dev/null +++ b/src/main/webapp/app/shared/range-slider/range-slider.component.scss @@ -0,0 +1,65 @@ +$height: 0.5rem; +$border-radius: 0.5rem; + +$slider-thumb-size: 1.2rem; +$slider-thumb-border-radius: 50%; +$slider-thumb-color: var(--primary); + +.slider { + height: $height; + border-radius: $border-radius; + background: var(--gray-400); +} + +.slider .progress { + height: $height; + border-radius: $border-radius; + position: absolute; + background: var(--primary); +} + +.range-input { + position: relative; +} + +.range-input input { + height: $height; + position: absolute; + width: 100%; + background: none; + pointer-events: none; + -webkit-appearance: none; +} + +/* Covers Chrome and Safari */ +[type='range']::-webkit-slider-thumb { + height: $slider-thumb-size; + width: $slider-thumb-size; + border-radius: $slider-thumb-border-radius; + background: $slider-thumb-color; + pointer-events: auto; + -webkit-appearance: none; + cursor: pointer; +} + +/* covers Mozilla Firefox */ +[type='range']::-moz-range-thumb { + height: $slider-thumb-size; + width: $slider-thumb-size; + border-radius: $slider-thumb-border-radius; + background: $slider-thumb-color; + pointer-events: auto; + -moz-appearance: none; + cursor: pointer; +} + +.slider-value { + position: absolute; + bottom: -2.2rem; + pointer-events: none; // we do not want to click the label but the range-thumb + color: var(--secondary); + text-align: center; + justify-content: center; + align-content: center; + display: flex; +} diff --git a/src/main/webapp/app/shared/range-slider/range-slider.component.ts b/src/main/webapp/app/shared/range-slider/range-slider.component.ts new file mode 100644 index 000000000000..1082ee793ca3 --- /dev/null +++ b/src/main/webapp/app/shared/range-slider/range-slider.component.ts @@ -0,0 +1,141 @@ +import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +const DEFAULT_STEP = 1; + +@Component({ + selector: 'jhi-range-slider', + templateUrl: './range-slider.component.html', + styleUrls: ['./range-slider.component.scss'], + standalone: true, + imports: [FormsModule, ReactiveFormsModule], +}) +export class RangeSliderComponent implements OnInit, OnDestroy { + @Input() generalMaxValue: number; + @Input() generalMinValue: number; + @Input() step: number = DEFAULT_STEP; + + /** When extending the supported label symbols you might have to adjust the logic for */ + @Input() labelSymbol?: '%'; + + @Input() selectedMinValue: number; + @Input() selectedMaxValue: number; + @Output() selectedMinValueChange: EventEmitter = new EventEmitter(); + @Output() selectedMaxValueChange: EventEmitter = new EventEmitter(); + + rangeInputElements?: NodeList; + eventListeners: { element: HTMLInputElement; listener: (event: Event) => void }[] = []; + + sliderMinPercentage: number; + sliderMaxPercentage: number; + + valueRange: number; + + /** Ensures that the label is placed centered underneath the range thumb */ + LABEL_MARGIN = 0.4; + + /** + * By trial and error it was found out that the slider thumbs are moving on + * 97% of the width compared to the colored bar that is displayed between the two thumbs. + * + * This issue is resolved with this factor when multiplied to {@link sliderMinPercentage} and {@link sliderMaxPercentage} + * to calculate the position of the label, as it is not the exact same position as the thumbs. + * + * + * To reproduce: + * If you inspect the progress bar in the initial state you will see that it is 100% wide and ends at the left end of + * the minimum range thumb. + * However, if you move the minimum thumb to the right (as far as possible), you will notice that the progress bar + * ends at the right end of the range thumb. - This is the problem that we address with this factor. + */ + SLIDER_THUMB_LABEL_POSITION_ADJUSTMENT_FACTOR = 0.97; + + constructor(private elRef: ElementRef) {} + + ngOnInit() { + this.rangeInputElements = this.elRef.nativeElement.querySelectorAll('.range-input input'); + + this.rangeInputElements?.forEach((input: HTMLInputElement) => { + const listener = (event: InputEvent) => { + this.ensureMinValueIsSmallerThanMaxValueViceVersa(event); + }; + input.addEventListener('input', listener); + this.eventListeners.push({ element: input, listener }); + }); + this.valueRange = this.generalMaxValue - this.generalMinValue; + + this.LABEL_MARGIN = this.getLabelMargin(); + + this.updateMinPercentage(); + this.updateMaxPercentage(); + } + + ngOnDestroy() { + this.eventListeners.forEach(({ element, listener }) => { + element.removeEventListener('input', listener); + }); + } + + updateMinPercentage() { + let newMinSelection = this.selectedMinValue; + + const tryingToSelectInvalidValue = this.selectedMinValue >= this.selectedMaxValue; + if (tryingToSelectInvalidValue) { + newMinSelection = this.selectedMaxValue - this.step; + } + + // noinspection UnnecessaryLocalVariableJS: not inlined because the variable name improves readability + const newMinPercentage = ((newMinSelection - this.generalMinValue) / this.valueRange) * 100; + this.sliderMinPercentage = newMinPercentage; + } + + updateMaxPercentage() { + let newMaxSelection = this.selectedMaxValue; + + const tryingToSelectInvalidValue = this.selectedMaxValue <= this.selectedMinValue; + if (tryingToSelectInvalidValue) { + newMaxSelection = this.selectedMinValue + this.step; + } + + // noinspection UnnecessaryLocalVariableJS: not inlined because the variable name improves readability + const newMaxPercentage = 100 - ((newMaxSelection - this.generalMinValue) / this.valueRange) * 100; + this.sliderMaxPercentage = newMaxPercentage; + } + + onSelectedMinValueChanged(event: Event): void { + const updatedMinValue = this.ensureMinValueIsSmallerThanMaxValueViceVersa(event); + this.selectedMinValueChange.emit(updatedMinValue); + } + + onSelectedMaxValueChanged(event: Event): void { + const updatedMaxValue = this.ensureMinValueIsSmallerThanMaxValueViceVersa(event); + this.selectedMaxValueChange.emit(updatedMaxValue); + } + + private ensureMinValueIsSmallerThanMaxValueViceVersa(event: Event): number { + const input = event.target as HTMLInputElement; + const minSliderIsUpdated = input.className.includes('range-min'); + + if (minSliderIsUpdated) { + if (this.selectedMinValue >= this.selectedMaxValue) { + this.selectedMinValue = this.selectedMaxValue - this.step; + } + return this.selectedMinValue; + } + + if (this.selectedMaxValue <= this.selectedMinValue) { + this.selectedMaxValue = this.selectedMinValue + this.step; + } + return this.selectedMaxValue; + } + + /** + * @return margin to labels considering the adjustments needed by the added {@link labelSymbol} + */ + private getLabelMargin() { + const BASE_LABEL_MARGIN = 0.4; // should be approximately the width of 1 symbol + const shiftToTheLeftDueToAddedSymbols = BASE_LABEL_MARGIN * (this.labelSymbol?.length ?? 0); + + return BASE_LABEL_MARGIN - shiftToTheLeftDueToAddedSymbols; + } +} diff --git a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts index 5fef73b00b99..5cd59c0d3b8e 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts @@ -24,10 +24,10 @@ export class SidebarAccordionComponent implements OnChanges, OnInit { @Input() showAddOption?: ChannelAccordionShowAdd; @Input() channelTypeIcon?: ChannelTypeIcons; @Input() collapseState: CollapseState; + @Input() isFilterActive: boolean = false; - //icon - faChevronRight = faChevronRight; - faFile = faFile; + readonly faChevronRight = faChevronRight; + readonly faFile = faFile; ngOnInit() { this.expandGroupWithSelectedItem(); @@ -35,7 +35,7 @@ export class SidebarAccordionComponent implements OnChanges, OnInit { } ngOnChanges() { - if (this.searchValue) { + if (this.searchValue || this.isFilterActive) { this.expandAll(); } else { this.setStoredCollapseState(); diff --git a/src/main/webapp/app/shared/sidebar/sidebar.component.html b/src/main/webapp/app/shared/sidebar/sidebar.component.html index c9ff6c8a64ae..ea513b48174e 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar.component.html @@ -4,12 +4,28 @@ @if (searchFieldEnabled) { } @if (!sidebarData?.ungroupedData || !(sidebarData?.ungroupedData | searchFilter: ['title', 'type'] : searchValue)?.length) {
} @else { @@ -36,6 +52,7 @@ [channelTypeIcon]="channelTypeIcon" [collapseState]="collapseState" (onUpdateSidebar)="onUpdateSidebar.emit()" + [isFilterActive]="isFilterActive" /> } @else { @for (sidebarItem of sidebarData?.ungroupedData | searchFilter: ['title', 'type'] : searchValue; track sidebarItem; let last = $last) { diff --git a/src/main/webapp/app/shared/sidebar/sidebar.component.ts b/src/main/webapp/app/shared/sidebar/sidebar.component.ts index fdb0d9adcaaa..de47ff395f76 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar.component.ts @@ -1,10 +1,20 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; -import { faChevronRight, faFilter, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'; +import { faFilter, faFilterCircleXmark } from '@fortawesome/free-solid-svg-icons'; import { ActivatedRoute, Params } from '@angular/router'; import { Subscription, distinctUntilChanged } from 'rxjs'; import { ProfileService } from '../layouts/profiles/profile.service'; import { ChannelAccordionShowAdd, ChannelTypeIcons, CollapseState, SidebarCardSize, SidebarData, SidebarTypes } from 'app/types/sidebar'; import { SidebarEventService } from './sidebar-event.service'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { cloneDeep } from 'lodash-es'; +import { ExerciseFilterOptions, ExerciseFilterResults } from 'app/types/exercise-filter'; +import { + getAchievablePointsAndAchievedScoreFilterOptions, + getExerciseCategoryFilterOptions, + getExerciseDifficultyFilterOptions, + getExerciseTypeFilterOptions, +} from 'app/shared/sidebar/sidebar.helper'; +import { ExerciseFilterModalComponent } from 'app/shared/exercise-filter/exercise-filter-modal.component'; @Component({ selector: 'jhi-sidebar', @@ -22,6 +32,7 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { @Input() showAddOption?: ChannelAccordionShowAdd; @Input() channelTypeIcon?: ChannelTypeIcons; @Input() collapseState: CollapseState; + @Input() showFilter: boolean = false; searchValue = ''; isCollapsed: boolean = false; @@ -32,19 +43,26 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { profileSubscription?: Subscription; sidebarEventSubscription?: Subscription; sidebarAccordionEventSubscription?: Subscription; + routeParams: Params; isProduction = true; isTestServer = false; - // icons - faMagnifyingGlass = faMagnifyingGlass; - faChevronRight = faChevronRight; - faFilter = faFilter; + private modalRef?: NgbModalRef; + + readonly faFilter = faFilter; + readonly faFilterCurrentlyApplied = faFilterCircleXmark; + + sidebarDataBeforeFiltering: SidebarData; + + exerciseFilters?: ExerciseFilterOptions; + isFilterActive: boolean = false; constructor( private route: ActivatedRoute, private profileService: ProfileService, private sidebarEventService: SidebarEventService, + private modalService: NgbModal, ) {} ngOnInit(): void { @@ -110,4 +128,43 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { }; return this.sidebarData.sidebarType ? size[this.sidebarData.sidebarType] : 'M'; } + + openFilterExercisesDialog() { + this.initializeFilterOptions(); + + if (!this.sidebarDataBeforeFiltering) { + this.sidebarDataBeforeFiltering = cloneDeep(this.sidebarData); + } + + this.modalRef = this.modalService.open(ExerciseFilterModalComponent, { + size: 'lg', + backdrop: 'static', + animation: true, + }); + + this.modalRef.componentInstance.sidebarData = cloneDeep(this.sidebarDataBeforeFiltering); + this.modalRef.componentInstance.exerciseFilters = cloneDeep(this.exerciseFilters); + + this.modalRef.componentInstance.filterApplied.subscribe((exerciseFilterResults: ExerciseFilterResults) => { + this.sidebarData = exerciseFilterResults.filteredSidebarData!; + this.exerciseFilters = exerciseFilterResults.appliedExerciseFilters; + this.isFilterActive = exerciseFilterResults.isFilterActive; + }); + } + + initializeFilterOptions() { + if (this.exerciseFilters) { + return; + } + + const scoreAndPointsFilterOptions = getAchievablePointsAndAchievedScoreFilterOptions(this.sidebarData, this.exerciseFilters); + + this.exerciseFilters = { + categoryFilter: getExerciseCategoryFilterOptions(this.sidebarData, this.exerciseFilters), + exerciseTypesFilter: getExerciseTypeFilterOptions(this.sidebarData, this.exerciseFilters), + difficultyFilter: getExerciseDifficultyFilterOptions(this.sidebarData, this.exerciseFilters), + achievedScore: scoreAndPointsFilterOptions?.achievedScore, + achievablePoints: scoreAndPointsFilterOptions?.achievablePoints, + }; + } } diff --git a/src/main/webapp/app/shared/sidebar/sidebar.helper.ts b/src/main/webapp/app/shared/sidebar/sidebar.helper.ts new file mode 100644 index 000000000000..c77e558b5390 --- /dev/null +++ b/src/main/webapp/app/shared/sidebar/sidebar.helper.ts @@ -0,0 +1,269 @@ +import { DifficultyFilterOption, ExerciseCategoryFilterOption, ExerciseFilterOptions, ExerciseTypeFilterOption, FilterOption, RangeFilter } from 'app/types/exercise-filter'; +import { SidebarCardElement, SidebarData } from 'app/types/sidebar'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { DifficultyLevel, ExerciseType, getIcon } from 'app/entities/exercise.model'; +import { getLatestResultOfStudentParticipation } from 'app/exercises/shared/participation/participation.utils'; +import { roundToNextMultiple } from 'app/shared/util/utils'; + +const POINTS_STEP = 1; +const SCORE_THRESHOLD_TO_INCREASE_STEP = 20; +const SMALL_SCORE_STEP = 1; +const SCORE_STEP = 5; + +const DEFAULT_DIFFICULTIES_FILTER: DifficultyFilterOption[] = [ + { name: 'artemisApp.exercise.easy', value: DifficultyLevel.EASY, checked: false }, + { name: 'artemisApp.exercise.medium', value: DifficultyLevel.MEDIUM, checked: false }, + { name: 'artemisApp.exercise.hard', value: DifficultyLevel.HARD, checked: false }, +]; + +const DEFAULT_EXERCISE_TYPES_FILTER: ExerciseTypeFilterOption[] = [ + { name: 'artemisApp.courseStatistics.programming', value: ExerciseType.PROGRAMMING, checked: false, icon: getIcon(ExerciseType.PROGRAMMING) }, + { name: 'artemisApp.courseStatistics.quiz', value: ExerciseType.QUIZ, checked: false, icon: getIcon(ExerciseType.QUIZ) }, + { name: 'artemisApp.courseStatistics.modeling', value: ExerciseType.MODELING, checked: false, icon: getIcon(ExerciseType.MODELING) }, + { name: 'artemisApp.courseStatistics.text', value: ExerciseType.TEXT, checked: false, icon: getIcon(ExerciseType.TEXT) }, + { name: 'artemisApp.courseStatistics.file-upload', value: ExerciseType.FILE_UPLOAD, checked: false, icon: getIcon(ExerciseType.FILE_UPLOAD) }, +]; + +function getAvailableCategoriesAsFilterOptions(sidebarData?: SidebarData): ExerciseCategoryFilterOption[] | undefined { + const sidebarElementsWithExerciseCategory: SidebarCardElement[] | undefined = sidebarData?.ungroupedData?.filter( + (sidebarElement: SidebarCardElement) => sidebarElement.exercise?.categories !== undefined, + ); + const availableCategories: ExerciseCategory[] | undefined = sidebarElementsWithExerciseCategory?.flatMap( + (sidebarElement: SidebarCardElement) => sidebarElement.exercise?.categories || [], + ); + + // noinspection UnnecessaryLocalVariableJS: not inlined because the variable name improves readability + const availableCategoriesAsFilterOptions: ExerciseCategoryFilterOption[] | undefined = availableCategories?.map((category: ExerciseCategory) => ({ + category: category, + searched: false, + })); + return availableCategoriesAsFilterOptions; +} + +function getExerciseCategoryFilterOptionsWithoutDuplicates(exerciseCategoryFilterOptions?: ExerciseCategoryFilterOption[]): ExerciseCategoryFilterOption[] | undefined { + return exerciseCategoryFilterOptions?.reduce((unique: ExerciseCategoryFilterOption[], item: ExerciseCategoryFilterOption) => { + if (!unique.some((uniqueItem) => uniqueItem.category.equals(item.category))) { + unique.push(item); + } + return unique; + }, []); +} + +function sortExerciseCategoryFilterOptionsSortedByName(exerciseCategoryFilterOptions?: ExerciseCategoryFilterOption[]): ExerciseCategoryFilterOption[] { + return exerciseCategoryFilterOptions?.sort((categoryFilterOptionsA, categoryFilterOptionB) => categoryFilterOptionsA.category.compare(categoryFilterOptionB.category)) ?? []; +} + +/** + * @param exerciseFilters that might already be defined for the course sidebar + * @param sidebarData that contains the exercises of a course and their information + * + * @returns already defined category filter options if they exist, otherwise the category filter options based on the sidebar data + */ +export function getExerciseCategoryFilterOptions(sidebarData?: SidebarData, exerciseFilters?: ExerciseFilterOptions): FilterOption { + if (exerciseFilters?.categoryFilter) { + return exerciseFilters?.categoryFilter; + } + + const availableCategoriesAsFilterOptions = getAvailableCategoriesAsFilterOptions(sidebarData); + const selectableCategoryFilterOptions = getExerciseCategoryFilterOptionsWithoutDuplicates(availableCategoriesAsFilterOptions); + const sortedCategoryFilterOptions = sortExerciseCategoryFilterOptionsSortedByName(selectableCategoryFilterOptions); + + const isDisplayed = !!sortedCategoryFilterOptions.length; + return { isDisplayed: isDisplayed, options: selectableCategoryFilterOptions ?? [] }; +} + +/** + * @param exerciseFilters that might already be defined for the course sidebar + * @param sidebarData that contains the exercises of a course and their information + * + * @returns already defined exercise type filter options if they exist, otherwise the exercise type filter options based on the sidebar data + */ +export function getExerciseTypeFilterOptions(sidebarData?: SidebarData, exerciseFilters?: ExerciseFilterOptions): FilterOption { + if (exerciseFilters?.exerciseTypesFilter) { + return exerciseFilters?.exerciseTypesFilter; + } + + const existingExerciseTypes = sidebarData?.ungroupedData + ?.filter((sidebarElement: SidebarCardElement) => sidebarElement.type !== undefined) + .map((sidebarElement: SidebarCardElement) => sidebarElement.type); + + const availableTypeFilters = DEFAULT_EXERCISE_TYPES_FILTER?.filter((exerciseType) => existingExerciseTypes?.includes(exerciseType.value)); + + return { isDisplayed: availableTypeFilters.length > 1, options: availableTypeFilters }; +} + +/** + * @param exerciseFilters that might already be defined for the course sidebar + * @param sidebarData that contains the exercises of a course and their information + * + * @returns already defined difficulty filter options if they exist, otherwise the difficulty filter options based on the sidebar data + */ +export function getExerciseDifficultyFilterOptions(sidebarData?: SidebarData, exerciseFilters?: ExerciseFilterOptions): FilterOption { + if (exerciseFilters?.difficultyFilter) { + return exerciseFilters.difficultyFilter; + } + + const existingDifficulties = sidebarData?.ungroupedData + ?.filter((sidebarElement: SidebarCardElement) => sidebarElement.difficulty !== undefined) + .map((sidebarElement: SidebarCardElement) => sidebarElement.difficulty); + + const availableDifficultyFilters = DEFAULT_DIFFICULTIES_FILTER?.filter((difficulty) => existingDifficulties?.includes(difficulty.value)); + + return { isDisplayed: !!availableDifficultyFilters.length, options: availableDifficultyFilters }; +} + +export function isRangeFilterApplied(rangeFilter?: RangeFilter): boolean { + if (!rangeFilter?.filter) { + return false; + } + + const filter = rangeFilter.filter; + const isExcludingMinValues = filter.selectedMin !== filter.generalMin; + const isExcludingMaxValues = filter.selectedMax !== filter.generalMax; + return isExcludingMinValues || isExcludingMaxValues; +} + +function getUpdatedMinAndMaxValues(minValue: number, maxValue: number, currentMaxValue: number) { + let updatedMinValue = minValue; + let updatedMaxValue = maxValue; + + if (currentMaxValue < minValue) { + updatedMinValue = currentMaxValue; + } + if (currentMaxValue > maxValue) { + updatedMaxValue = currentMaxValue; + } + + return { updatedMinValue, updatedMaxValue }; +} + +/** + * The calculation for points and score are intentionally mixed into one method to reduce the number of iterations over the sidebar data. + * @param sidebarData + */ +function calculateMinAndMaxForPointsAndScore(sidebarData: SidebarData) { + let minAchievablePoints = Infinity; + let maxAchievablePoints = -Infinity; + + let minAchievedScore = Infinity; + let maxAchievedScore = -Infinity; + + sidebarData.ungroupedData?.forEach((sidebarElement: SidebarCardElement) => { + if (sidebarElement.exercise?.maxPoints) { + const currentExerciseMaxPoints = sidebarElement.exercise.maxPoints; + + const { updatedMinValue, updatedMaxValue } = getUpdatedMinAndMaxValues(minAchievablePoints, maxAchievablePoints, currentExerciseMaxPoints); + minAchievablePoints = updatedMinValue; + maxAchievablePoints = updatedMaxValue; + + if (sidebarElement.studentParticipation) { + const currentExerciseAchievedScore = getLatestResultOfStudentParticipation(sidebarElement.studentParticipation, true)?.score; + + if (currentExerciseAchievedScore !== undefined) { + const { updatedMinValue, updatedMaxValue } = getUpdatedMinAndMaxValues(minAchievedScore, maxAchievedScore, currentExerciseAchievedScore); + minAchievedScore = updatedMinValue; + maxAchievedScore = updatedMaxValue; + } + } + } + }); + + return { minAchievablePoints, maxAchievablePoints, minAchievedScore, maxAchievedScore }; +} + +/** + * **Rounds the min and max values for achievable points and achieved score to the next multiple of the step. + * The step {@link POINTS_STEP}, and {@link SCORE_STEP} or {@link SMALL_SCORE_STEP} are the selectable values for the range filter.** + *
+ * For the **score filter**, the step is increased if we have more than 20 values between the min and max value, + * as up to 100 values are theoretically possible.
+ * For the **achievable points filter**, the step is always 1 as exercises usually have between 1 and 15 points, + * so we do not need to increase the step and thereby limit accuracy of filter options.
+ * + * @param minAchievablePoints + * @param maxAchievablePoints + * @param minAchievedScore + * @param maxAchievedScore + */ +function roundRangeFilterMinAndMaxValues(minAchievablePoints: number, maxAchievablePoints: number, minAchievedScore: number, maxAchievedScore: number) { + const roundUp = true; + const roundDown = false; + const minAchievablePointsRounded = roundToNextMultiple(minAchievablePoints, POINTS_STEP, roundDown); + const maxAchievablePointsRounded = roundToNextMultiple(maxAchievablePoints, POINTS_STEP, roundUp); + + let minAchievedScoreRounded; + let maxAchievedScoreRounded; + + if (maxAchievedScore > SCORE_THRESHOLD_TO_INCREASE_STEP) { + minAchievedScoreRounded = roundToNextMultiple(minAchievedScore, SCORE_STEP, roundDown); + maxAchievedScoreRounded = roundToNextMultiple(maxAchievedScore, SCORE_STEP, roundUp); + } else { + minAchievedScoreRounded = roundToNextMultiple(minAchievedScore, SMALL_SCORE_STEP, roundDown); + maxAchievedScoreRounded = roundToNextMultiple(maxAchievedScore, SMALL_SCORE_STEP, roundUp); + } + + return { minAchievablePointsRounded, maxAchievablePointsRounded, minAchievedScoreRounded, maxAchievedScoreRounded }; +} + +function calculateAchievablePointsFilterOptions(sidebarData: SidebarData): { achievablePoints?: RangeFilter; achievedScore?: RangeFilter } { + const { minAchievablePoints, maxAchievablePoints, minAchievedScore, maxAchievedScore } = calculateMinAndMaxForPointsAndScore(sidebarData); + + const { minAchievablePointsRounded, maxAchievablePointsRounded, minAchievedScoreRounded, maxAchievedScoreRounded } = roundRangeFilterMinAndMaxValues( + minAchievablePoints, + maxAchievablePoints, + minAchievedScore, + maxAchievedScore, + ); + + return { + achievablePoints: { + isDisplayed: minAchievablePointsRounded < maxAchievablePointsRounded, + filter: { + generalMin: minAchievablePointsRounded, + generalMax: maxAchievablePointsRounded, + selectedMin: minAchievablePointsRounded, + selectedMax: maxAchievablePointsRounded, + step: POINTS_STEP, + }, + }, + achievedScore: { + isDisplayed: minAchievedScoreRounded < maxAchievedScoreRounded && minAchievedScoreRounded !== Infinity, + filter: { + generalMin: minAchievedScoreRounded, + generalMax: maxAchievedScoreRounded, + selectedMin: minAchievedScoreRounded, + selectedMax: maxAchievedScoreRounded, + step: maxAchievedScoreRounded <= SCORE_THRESHOLD_TO_INCREASE_STEP ? SMALL_SCORE_STEP : SCORE_STEP, + }, + }, + }; +} + +/** + * @param exerciseFilters that might already be defined for the course sidebar + * @param sidebarData that contains the exercises of a course and their information + * + * @returns already defined achievable points and achieved score filter options if they exist, otherwise the achievable points and achieved score filter options based on the sidebar data + */ +export function getAchievablePointsAndAchievedScoreFilterOptions( + sidebarData?: SidebarData, + exerciseFilters?: ExerciseFilterOptions, +): { + achievablePoints?: RangeFilter; + achievedScore?: RangeFilter; +} { + if (!sidebarData?.ungroupedData) { + return { achievablePoints: undefined, achievedScore: undefined }; + } + + const isPointsFilterApplied = isRangeFilterApplied(exerciseFilters?.achievablePoints); + const isScoreFilterApplied = isRangeFilterApplied(exerciseFilters?.achievedScore); + + const isRecalculatingFilterOptionsRequired = isPointsFilterApplied || isScoreFilterApplied || !exerciseFilters?.achievablePoints || !exerciseFilters?.achievedScore; + if (!isRecalculatingFilterOptionsRequired) { + // the scores might change when we work on exercises, so we re-calculate the filter options (but only if the filter is actually applied) + return { achievablePoints: exerciseFilters?.achievablePoints, achievedScore: exerciseFilters?.achievedScore }; + } + + return calculateAchievablePointsFilterOptions(sidebarData); +} diff --git a/src/main/webapp/app/shared/statistics-graph/statistics.service.ts b/src/main/webapp/app/shared/statistics-graph/statistics.service.ts index 27b83a0eac33..233a10bfd8d8 100644 --- a/src/main/webapp/app/shared/statistics-graph/statistics.service.ts +++ b/src/main/webapp/app/shared/statistics-graph/statistics.service.ts @@ -48,7 +48,7 @@ export class StatisticsService { const params = new HttpParams().set('courseId', '' + courseId); return this.http.get(`${this.resourceUrl}course-statistics`, { params }).pipe( map((res: CourseManagementStatisticsDTO) => { - StatisticsService.convertExerciseCategoriesOfrCourseManagementStatisticsFromServer(res); + StatisticsService.convertExerciseCategoriesOfCourseManagementStatisticsFromServer(res); return StatisticsService.convertCourseManagementStatisticDatesFromServer(res); }), ); @@ -78,9 +78,9 @@ export class StatisticsService { return dto; } - private static convertExerciseCategoriesOfrCourseManagementStatisticsFromServer(res: CourseManagementStatisticsDTO): CourseManagementStatisticsDTO { + private static convertExerciseCategoriesOfCourseManagementStatisticsFromServer(res: CourseManagementStatisticsDTO): CourseManagementStatisticsDTO { res.averageScoresOfExercises.forEach((avgScoresOfExercise) => { - avgScoresOfExercise.categories = avgScoresOfExercise.categories?.map((category) => JSON.parse(category as string) as ExerciseCategory); + avgScoresOfExercise.categories = avgScoresOfExercise.categories?.map((category) => new ExerciseCategory(category.category, category.color)); }); return res; } diff --git a/src/main/webapp/app/shared/user-import/users-import-button.component.ts b/src/main/webapp/app/shared/user-import/users-import-button.component.ts index 8056a4dbf2d1..88cd5df1b505 100644 --- a/src/main/webapp/app/shared/user-import/users-import-button.component.ts +++ b/src/main/webapp/app/shared/user-import/users-import-button.component.ts @@ -3,7 +3,7 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { UsersImportDialogComponent } from 'app/shared/user-import/users-import-dialog.component'; import { CourseGroup } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { faFileImport } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; diff --git a/src/main/webapp/app/shared/user-import/users-import-dialog.component.ts b/src/main/webapp/app/shared/user-import/users-import-dialog.component.ts index cb8287d7b74d..147cf3a2e661 100644 --- a/src/main/webapp/app/shared/user-import/users-import-dialog.component.ts +++ b/src/main/webapp/app/shared/user-import/users-import-dialog.component.ts @@ -3,12 +3,12 @@ import { NgForm } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { HttpResponse } from '@angular/common/http'; -import { ExamUserDTO } from 'app/entities/exam-user-dto.model'; +import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; import { cleanString } from 'app/shared/util/utils'; import { Subject } from 'rxjs'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { StudentDTO } from 'app/entities/student-dto.model'; import { parse } from 'papaparse'; diff --git a/src/main/webapp/app/shared/user-settings/user-settings-container/user-settings-container.component.html b/src/main/webapp/app/shared/user-settings/user-settings-container/user-settings-container.component.html index 87f9a29c5d01..6391b4986739 100644 --- a/src/main/webapp/app/shared/user-settings/user-settings-container/user-settings-container.component.html +++ b/src/main/webapp/app/shared/user-settings/user-settings-container/user-settings-container.component.html @@ -26,7 +26,14 @@ > @if (localVCEnabled) { - + + + }

diff --git a/src/main/webapp/app/shared/user-settings/user-settings.module.ts b/src/main/webapp/app/shared/user-settings/user-settings.module.ts index 98f82ef20bef..c006c7f5e904 100644 --- a/src/main/webapp/app/shared/user-settings/user-settings.module.ts +++ b/src/main/webapp/app/shared/user-settings/user-settings.module.ts @@ -8,9 +8,19 @@ import { userSettingsState } from 'app/shared/user-settings/user-settings.route' import { ScienceSettingsComponent } from 'app/shared/user-settings/science-settings/science-settings.component'; import { SshUserSettingsComponent } from 'app/shared/user-settings/ssh-settings/ssh-user-settings.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { VcsAccessTokensSettingsComponent } from 'app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component'; +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; @NgModule({ - imports: [RouterModule.forChild(userSettingsState), ArtemisSharedModule, ArtemisSharedComponentModule], - declarations: [UserSettingsContainerComponent, AccountInformationComponent, NotificationSettingsComponent, ScienceSettingsComponent, SshUserSettingsComponent], + imports: [RouterModule.forChild(userSettingsState), ArtemisSharedModule, ArtemisSharedComponentModule, ClipboardModule, FormDateTimePickerModule], + declarations: [ + UserSettingsContainerComponent, + AccountInformationComponent, + NotificationSettingsComponent, + ScienceSettingsComponent, + SshUserSettingsComponent, + VcsAccessTokensSettingsComponent, + ], }) export class UserSettingsModule {} diff --git a/src/main/webapp/app/shared/user-settings/user-settings.route.ts b/src/main/webapp/app/shared/user-settings/user-settings.route.ts index 1e60f8d264c0..f27183c487cf 100644 --- a/src/main/webapp/app/shared/user-settings/user-settings.route.ts +++ b/src/main/webapp/app/shared/user-settings/user-settings.route.ts @@ -6,6 +6,7 @@ import { UserRouteAccessService } from 'app/core/auth/user-route-access-service' import { Authority } from 'app/shared/constants/authority.constants'; import { ScienceSettingsComponent } from 'app/shared/user-settings/science-settings/science-settings.component'; import { SshUserSettingsComponent } from 'app/shared/user-settings/ssh-settings/ssh-user-settings.component'; +import { VcsAccessTokensSettingsComponent } from 'app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component'; export const userSettingsState: Routes = [ { @@ -44,12 +45,19 @@ export const userSettingsState: Routes = [ }, }, { - path: 'sshSettings', + path: 'ssh', component: SshUserSettingsComponent, data: { pageTitle: 'artemisApp.userSettings.categories.SSH_SETTINGS', }, }, + { + path: 'vcs-token', + component: VcsAccessTokensSettingsComponent, + data: { + pageTitle: 'artemisApp.userSettings.categories.VCS_TOKEN_SETTINGS', + }, + }, ], }, ]; diff --git a/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.html b/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.html new file mode 100644 index 000000000000..5dc7ee5c55a7 --- /dev/null +++ b/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.html @@ -0,0 +1,130 @@ +

+ +

+@if (currentUser) { +
+
+
+ +
+
+ @if (!edit) { +
+
+ @if (this.currentUser?.vcsAccessToken) { + + + + + + + + + + + + + + + + + + +
+ + + + + +
*************** + + {{ this.currentUser?.vcsAccessTokenExpiryDate | artemisDate }} + +
+ } @else { +
+ } +
+
+ @if (!this.currentUser?.vcsAccessToken) { +
+
+
+ +
+
+
+ } + } + @if (edit) { +
+
+

+ +
+
+ +
+
+ +
+ + +
+
+
+ } +
+} diff --git a/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.ts b/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.ts new file mode 100644 index 000000000000..9214784ea8f6 --- /dev/null +++ b/src/main/webapp/app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component.ts @@ -0,0 +1,125 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { User } from 'app/core/user/user.model'; +import { AccountService } from 'app/core/auth/account.service'; +import { Subject, Subscription, tap } from 'rxjs'; +import dayjs from 'dayjs/esm'; +import { faBan, faCopy, faEdit, faSave, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; +import { AlertService } from 'app/core/util/alert.service'; + +@Component({ + selector: 'jhi-account-information', + templateUrl: './vcs-access-tokens-settings.component.html', + styleUrls: ['../user-settings.scss'], +}) +export class VcsAccessTokensSettingsComponent implements OnInit, OnDestroy { + currentUser?: User; + + readonly faEdit = faEdit; + readonly faSave = faSave; + readonly faTrash = faTrash; + readonly faCopy = faCopy; + readonly faBan = faBan; + private authStateSubscription: Subscription; + expiryDate?: dayjs.Dayjs; + validExpiryDate = false; + wasCopied = false; + edit = false; + + private dialogErrorSource = new Subject(); + + dialogError$ = this.dialogErrorSource.asObservable(); + + protected readonly ButtonType = ButtonType; + protected readonly ButtonSize = ButtonSize; + + constructor( + private accountService: AccountService, + private alertService: AlertService, + ) {} + + ngOnInit() { + this.authStateSubscription = this.accountService + .getAuthenticationState() + .pipe( + tap((user: User) => { + this.currentUser = user; + return this.currentUser; + }), + ) + .subscribe(); + } + + ngOnDestroy(): void { + this.authStateSubscription.unsubscribe(); + } + + deleteVcsAccessToken() { + this.accountService.deleteUserVcsAccessToken().subscribe({ + next: () => { + if (this.currentUser) { + this.currentUser.vcsAccessTokenExpiryDate = undefined; + this.currentUser.vcsAccessToken = undefined; + } + this.alertService.success('artemisApp.userSettings.vcsAccessTokensSettingsPage.deleteSuccess'); + }, + error: () => { + this.alertService.error('artemisApp.userSettings.vcsAccessTokensSettingsPage.deleteFailure'); + }, + }); + this.dialogErrorSource.next(''); + } + + addNewVcsAccessToken() { + this.edit = true; + } + + sendTokenCreationRequest() { + if (!this.expiryDate || this.expiryDate.isBefore(dayjs()) || this.expiryDate.isAfter(dayjs().add(1, 'year'))) { + this.alertService.error('artemisApp.userSettings.vcsAccessTokensSettingsPage.addFailure'); + return; + } + this.accountService.addNewVcsAccessToken(this.expiryDate.toISOString()).subscribe({ + next: (res) => { + if (this.currentUser) { + const user = res.body as User; + this.currentUser.vcsAccessToken = user.vcsAccessToken; + this.currentUser.vcsAccessTokenExpiryDate = user.vcsAccessTokenExpiryDate; + this.edit = false; + } + this.alertService.success('artemisApp.userSettings.vcsAccessTokensSettingsPage.addSuccess'); + }, + error: () => { + this.alertService.error('artemisApp.userSettings.vcsAccessTokensSettingsPage.addFailure'); + }, + }); + } + + /** + * set wasCopied for 3 seconds on success + */ + onCopyFinished(successful: boolean) { + if (successful) { + this.wasCopied = true; + setTimeout(() => { + this.wasCopied = false; + }, 3000); + } + } + + /** + * Validates if the expiry date is after current time + */ + validateDate() { + this.validExpiryDate = !!this.expiryDate?.isAfter(dayjs()) && !!this.expiryDate?.isBefore(dayjs().add(1, 'year')); + } + + /** + * Cancel creation of a new token + */ + cancelTokenCreation() { + this.edit = false; + this.expiryDate = undefined; + this.validExpiryDate = false; + } +} diff --git a/src/main/webapp/app/shared/util/utils.ts b/src/main/webapp/app/shared/util/utils.ts index a9ecd05f46d7..ed3df211540a 100644 --- a/src/main/webapp/app/shared/util/utils.ts +++ b/src/main/webapp/app/shared/util/utils.ts @@ -156,3 +156,18 @@ export function scrollToTopOfPage() { export function isExamExercise(exercise: Exercise) { return exercise.course === undefined; } + +/** + * Rounds a value up to the nearest multiple + * + * @param value that shall be rounded + * @param multiple to which we round up + * @param roundUp if true, we round up, otherwise we round down + */ +export function roundToNextMultiple(value: number, multiple: number, roundUp: boolean) { + if (roundUp) { + return Math.ceil(value / multiple) * multiple; + } + + return Math.floor(value / multiple) * multiple; +} diff --git a/src/main/webapp/app/types/exercise-filter.ts b/src/main/webapp/app/types/exercise-filter.ts new file mode 100644 index 000000000000..053dbb8d089c --- /dev/null +++ b/src/main/webapp/app/types/exercise-filter.ts @@ -0,0 +1,43 @@ +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { DifficultyLevel, ExerciseType } from 'app/entities/exercise.model'; +import { SidebarData } from 'app/types/sidebar'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; + +/** + * isDisplayed - whether the filter is in the filter modal (e.g. for no sidebar element the difficulty is defined, so the difficulty filter is not displayed) + */ +export type FilterOption = { isDisplayed: boolean; options: T[] }; +export type ExerciseCategoryFilterOption = { category: ExerciseCategory; searched: boolean }; +export type ExerciseTypeFilterOption = { name: string; value: ExerciseType; checked: boolean; icon: IconProp }; +export type DifficultyFilterOption = { name: string; value: DifficultyLevel; checked: boolean }; + +export type RangeFilter = { + isDisplayed: boolean; + filter: { + generalMin: number; + generalMax: number; + selectedMin: number; + selectedMax: number; + step: number; + }; +}; + +export type ExerciseFilterOptions = { + categoryFilter?: FilterOption; + exerciseTypesFilter?: FilterOption; + difficultyFilter?: FilterOption; + achievedScore?: RangeFilter; + achievablePoints?: RangeFilter; +}; + +export type ExerciseFilterResults = { filteredSidebarData?: SidebarData; appliedExerciseFilters?: ExerciseFilterOptions; isFilterActive: boolean }; + +export type FilterDetails = { + searchedTypes?: ExerciseType[]; + selectedCategories: ExerciseCategory[]; + searchedDifficulties?: DifficultyLevel[]; + isScoreFilterApplied: boolean; + isPointsFilterApplied: boolean; + achievedScore?: RangeFilter; + achievablePoints?: RangeFilter; +}; diff --git a/src/main/webapp/i18n/de/exercise-actions.json b/src/main/webapp/i18n/de/exercise-actions.json index f45e3097bf3e..928496b308b8 100644 --- a/src/main/webapp/i18n/de/exercise-actions.json +++ b/src/main/webapp/i18n/de/exercise-actions.json @@ -69,6 +69,8 @@ "uploadFile": "Datei hochladen", "viewTeam": "Team", "sshKeyTip": "Um SSH zu nutzen, musst du {link:hier} einen SSH Schlüssel zu deinem Konto hinzufügen.", + "vcsTokenTip": "Um mit einem VCS-Zugriffstoken auf das Repository zuzugreifen, musst du {link:hier} ein neues hinzufügen.", + "vcsTokenExpiredTip": "Dein VCS-Zugriffstoken ist abgelaufen. Erneuere es {link:hier}.", "startExerciseBeforeStartDate": "Du kannst vor dem Startdatum nicht an der Aufgabe teilnehmen.", "deleteMultipleExercisesQuestion": "Sollen die ausgewählten Aufgaben wirklich dauerhaft gelöscht werden?" } diff --git a/src/main/webapp/i18n/de/exercise.json b/src/main/webapp/i18n/de/exercise.json index e198497deaa9..332f39024ff4 100644 --- a/src/main/webapp/i18n/de/exercise.json +++ b/src/main/webapp/i18n/de/exercise.json @@ -140,6 +140,7 @@ }, "categories": "Kategorien", "noCategory": "Keine Kategorie", + "selectCategories": "Kategorien auswählen", "participation": "Teilnahme", "participations": "Teilnahmen", "submissions": "Einreichungen", diff --git a/src/main/webapp/i18n/de/programmingExercise.json b/src/main/webapp/i18n/de/programmingExercise.json index ca4d45bb1486..4e493f7d4daf 100644 --- a/src/main/webapp/i18n/de/programmingExercise.json +++ b/src/main/webapp/i18n/de/programmingExercise.json @@ -118,14 +118,22 @@ "workdir": "Verzeichnis", "allowOnlineEditor": { "title": "Online-Editor erlauben", - "alert": "Es muss mindestens eine Option (Offline-IDE oder Online-Editor) ausgewählt sein" + "alert": "Es muss mindestens eine Option (Offline-IDE, Online-Editor oder Online-IDE) ausgewählt sein", + "alertNoTheia": "Es muss mindestens eine Option (Offline-IDE oder Online-Editor) ausgewählt sein" }, "onlineEditor": "Online", "allowOfflineIde": { "title": "Offline-IDE erlauben", - "alert": "Es muss mindestens eine Option (Offline-IDE oder Online-Editor) ausgewählt sein" + "alert": "Es muss mindestens eine Option (Offline-IDE, Online-Editor oder Online-IDE) ausgewählt sein", + "alertNoTheia": "Es muss mindestens eine Option (Offline-IDE oder Online-Editor) ausgewählt sein" }, "offlineIde": "IDE", + "allowOnlineIde": { + "title": "Online-IDE erlauben", + "alert": "Es muss mindestens eine Option (Offline-IDE, Online-Editor oder Online-IDE) ausgewählt sein.", + "alertNoTheia": "Es muss mindestens eine Option (Offline-IDE oder Online-Editor) ausgewählt sein" + }, + "onlineIde": "Online IDE", "showTestNamesToStudents": "Zeige die Test Namen den Studierenden", "showTestNamesToStudentsTooltip": "Durch Aktivierung dieser Option werden die Namen der automatischen Tests den Studierenden angezeigt. Lasse die Option deaktiviert, um keine visuelle Unterscheidung zwischen manuellem und automatischem Feedback für die Studierenden vorzunehmen.", "participationMode": "Teilnahmemodus", @@ -170,6 +178,11 @@ "projectType": "Projekttyp", "testRepositoryProjectType": "Projekttyp des Test-Repository", "packageName": "Package-Name", + "theiaImage": { + "title": "Konfiguration für Online IDE", + "noImageAvailable": "Die Online IDE ist für diese Programmiersprache noch nicht verfügbar.", + "alert": "Es muss eine gültige Konfiguration für die Online IDE ausgewählt werden." + }, "appName": "App-Name", "templateResult": "Ergebnis der Vorlage", "solutionResult": "Ergebnis der Musterlösung", diff --git a/src/main/webapp/i18n/de/student-dashboard.json b/src/main/webapp/i18n/de/student-dashboard.json index 38820f89e17b..89729023ff10 100644 --- a/src/main/webapp/i18n/de/student-dashboard.json +++ b/src/main/webapp/i18n/de/student-dashboard.json @@ -33,7 +33,8 @@ "manage": "Verwalten", "studentView": "Studentenansicht", "general": { - "noDataFound": "Keine Einträge gefunden." + "noDataFound": "Keine Einträge gefunden.", + "noElementFoundWithAppliedFilter": "Keine Einträge für die angewendeten Filtereinstellungen gefunden." }, "sidebar": { "past": "Vorangegangen", @@ -89,6 +90,17 @@ "plagiarismCases": "Plagiatsfälle", "gradingSystem": "Notenschlüssel" }, + "exerciseFilter": { + "filter": "Filter", + "modalTitle": "Aufgaben Filtern", + "dueDateRange": "Zeitraum der Einreichungsfrist", + "achievedScore": "Erreichtes Ergebnis", + "achievablePoints": "Erreichbare Punktzahl", + "applyFilter": "Filter anwenden", + "resetFilter": "Filter zurücksetzen", + "noFilterAvailable": "Für die bisherigen Aufgaben gibt es keine unterscheidenden Filteroptionen", + "noMoreOptions": "Keine weiteren Auswahlmöglichkeiten" + }, "exerciseList": { "filter": "Filter ({{ num }})", "releaseDate": "Veröffentlichungsdatum", diff --git a/src/main/webapp/i18n/de/userSettings.json b/src/main/webapp/i18n/de/userSettings.json index 4898166be418..d7e1c3de5180 100644 --- a/src/main/webapp/i18n/de/userSettings.json +++ b/src/main/webapp/i18n/de/userSettings.json @@ -5,7 +5,8 @@ "saveChanges": "Änderungen speichern", "saveSettingsSuccessAlert": "Die Einstellungen wurden erfolgreich gespeichert.", "userSettings": "Benutzereinstellungen", - "sshSettings": "SSH Einstellungen", + "sshSettings": "SSH", + "vcsAccessTokenSettings": "VCS Token", "accountInformation": "Account Informationen", "notificationSettings": "Benachrichtigungen", "notificationSettingsFilterInfo": "Diese Einstellungen filtern auch die Seitenleiste für Mitteilungen", @@ -27,6 +28,21 @@ "sshKeyDisplayedInformation": "Das ist dein aktuell konfigurierter SSH-Schlüssel:", "key": "SSH Schlüssel" }, + "vcsAccessTokensSettingsPage": { + "addTokenTitle": "Neues Zugriffstoken erzeugen", + "infoText": "Du kannst ein persönliches Zugriffstoken generieren, um mit dem Artemis Local Version Control System zu interagieren. Verwende es um dich über HTTP bei Git zu authentifizieren.", + "deleteVcsAccessTokenQuestion": "Möchtest du dein Zugriffstoken für das Versionskontrollsystem wirklich löschen? Du kannst dich nicht mehr bei lokalen Repositories authentifizieren, die mit diesem Token geklont wurden.", + "createAccessToken": "Neues VCS Zugriffstoken generieren", + "noTokenSet": "Du hast kein Zugriffstoken", + "addToken": "Neues Token hinzufügen", + "deleteFailure": "Das Löschen des VCS-Zugriffstokens ist fehlgeschlagen", + "deleteSuccess": "Das VCS-Zugriffstoken wurde erfolgreich gelöscht", + "addFailure": "Neues VCS-Zugriffstoken konnte nicht gespeichert werden", + "addSuccess": "Neues VCS-Zugriffstoken erfolgreich erstellt", + "vcsAccessToken": "VCS-Zugriffstoken", + "expiryDate": "Ablaufdatum", + "actions": "Aktionen" + }, "accountInformationPage": { "registrationNumber": "Matrikelnummer", "fullName": "Vollständiger Name", @@ -38,7 +54,8 @@ "categories": { "NOTIFICATION_SETTINGS": "Benachrichtigungseinstellungen", "SCIENCE_SETTINGS": "Forschungseinstellungen", - "SSH_SETTINGS": "SSH" + "SSH_SETTINGS": "SSH Einstellungen", + "VCS_TOKEN_SETTINGS": "VCS Tokeneinstellungen" }, "settingGroupNames": { "weeklySummary": "Wöchentliche Zusammenfassung", diff --git a/src/main/webapp/i18n/en/exercise-actions.json b/src/main/webapp/i18n/en/exercise-actions.json index b32d1e5be4d1..ba4ac935244d 100644 --- a/src/main/webapp/i18n/en/exercise-actions.json +++ b/src/main/webapp/i18n/en/exercise-actions.json @@ -70,6 +70,8 @@ "uploadFile": "Upload a file", "viewTeam": "Team", "sshKeyTip": "To use ssh, you need to add an ssh key to your account {link:here}.", + "vcsTokenTip": "To access the repository with a VCS access token, you need to add a new one to your account {link:here}.", + "vcsTokenExpiredTip": "Your VCS access token has expired. Renew it {link:here}.", "startExerciseBeforeStartDate": "You cannot participate before the start date of the exercise.", "deleteMultipleExercisesQuestion": "Are you sure you want to delete the selected exercises?" } diff --git a/src/main/webapp/i18n/en/exercise.json b/src/main/webapp/i18n/en/exercise.json index fbc47e4093dc..ca2f0fdde23c 100644 --- a/src/main/webapp/i18n/en/exercise.json +++ b/src/main/webapp/i18n/en/exercise.json @@ -140,6 +140,7 @@ }, "categories": "Categories", "noCategory": "No Category", + "selectCategories": "Select categories", "participation": "Participation", "participations": "Participations", "submissions": "Submissions", diff --git a/src/main/webapp/i18n/en/programmingExercise.json b/src/main/webapp/i18n/en/programmingExercise.json index 570879bbe125..1883d8294abc 100644 --- a/src/main/webapp/i18n/en/programmingExercise.json +++ b/src/main/webapp/i18n/en/programmingExercise.json @@ -130,14 +130,22 @@ "customizeDockerImage": "You can customize the Docker image. Make sure to provide it in amd64 and arm64 and include all build dependencies to guarantee a short build duration.", "allowOnlineEditor": { "title": "Allow Online Editor", - "alert": "At least one option (Offline IDE or Online Editor) must be selected" + "alert": "At least one option (Offline IDE, Online Editor, or Online IDE) must be selected", + "alertNoTheia": "At least one option (Offline IDE or Online Editor) must be selected" }, "onlineEditor": "Online", "allowOfflineIde": { "title": "Allow Offline IDE", - "alert": "At least one option (Offline IDE or Online Editor) must be selected" + "alert": "At least one option (Offline IDE, Online Editor, or Online IDE) must be selected", + "alertNoTheia": "At least one option (Offline IDE or Online Editor) must be selected" }, "offlineIde": "IDE", + "allowOnlineIde": { + "title": "Allow Online IDE", + "alert": "At least one option (Offline IDE, Online Editor, or Online IDE) must be selected.", + "alertNoTheia": "At least one option (Offline IDE or Online Editor) must be selected" + }, + "onlineIde": "Online IDE", "showTestNamesToStudents": "Show Test Names to Students", "showTestNamesToStudentsTooltip": "Activate this option to show the names of the automated test cases to the students. Leave the option disabled to make no visual distinction between manual and automated feedback for the students.", "participationMode": "Participation Mode", @@ -172,6 +180,11 @@ "projectType": "Project Type", "testRepositoryProjectType": "Test Repository Project Type", "packageName": "Package Name", + "theiaImage": { + "title": "Configuration for Online IDE", + "noImageAvailable": "The Online IDE is not yet available for this programming language.", + "alert": "A valid configuration for the Online IDE must be selected." + }, "appName": "App Name", "templateResult": "Template Result", "solutionResult": "Solution Result", diff --git a/src/main/webapp/i18n/en/student-dashboard.json b/src/main/webapp/i18n/en/student-dashboard.json index 586cdf840ce1..ae6d54800cd6 100644 --- a/src/main/webapp/i18n/en/student-dashboard.json +++ b/src/main/webapp/i18n/en/student-dashboard.json @@ -33,7 +33,8 @@ "manage": "Manage", "studentView": "Student view", "general": { - "noDataFound": "No data found." + "noDataFound": "No data found.", + "noElementFoundWithAppliedFilter": "No element matches the applied filter." }, "sidebar": { "past": "Past", @@ -89,6 +90,17 @@ "plagiarismCases": "Plagiarism Cases", "gradingSystem": "Grading System" }, + "exerciseFilter": { + "filter": "Filter", + "modalTitle": "Filter Exercises", + "dueDateRange": "Due Date Range", + "achievedScore": "Achieved Score", + "achievablePoints": "Achievable Points", + "applyFilter": "Apply filter", + "clearFilter": "Clear filter", + "noFilterAvailable": "There are no distinguishing filter options for the existing exercises", + "noMoreOptions": "No more options" + }, "exerciseList": { "filter": "Filter ({{ num }})", "releaseDate": "Release Date", diff --git a/src/main/webapp/i18n/en/userSettings.json b/src/main/webapp/i18n/en/userSettings.json index 839dd249b260..4230ddab9506 100644 --- a/src/main/webapp/i18n/en/userSettings.json +++ b/src/main/webapp/i18n/en/userSettings.json @@ -5,7 +5,8 @@ "saveChanges": "Save Changes", "saveSettingsSuccessAlert": "Your Settings have been successfully saved.", "userSettings": "User Settings", - "sshSettings": "SSH Settings", + "sshSettings": "SSH", + "vcsAccessTokenSettings": "VCS Token", "accountInformation": "Account Information", "notificationSettings": "Notifications", "notificationSettingsFilterInfo": "These settings also filter the notification sidebar", @@ -27,6 +28,21 @@ "sshKeyDisplayedInformation": "This is your currently configured SSH key:", "key": "SSH Key" }, + "vcsAccessTokensSettingsPage": { + "addTokenTitle": "Add personal access token", + "infoText": "You can generate a personal access token to interact with the Artemis Local Version Control System. You can use it to authenticate to Git over HTTP.", + "deleteVcsAccessTokenQuestion": "Do you really want to delete your version control system access token? You will not be able to authenticate to local repositories cloned with this token any more.", + "createAccessToken": "Create new VCS access token", + "noTokenSet": "You do not have any user tokens", + "addToken": "Add new token", + "deleteFailure": "Deleting the VCS access token failed", + "deleteSuccess": "Successfully deleted the VCS access token", + "addFailure": "Failed to set new VCS access token", + "addSuccess": "Successfully created a new VCS access token", + "vcsAccessToken": "VCS Access Token", + "expiryDate": "Expiry Date", + "actions": "Actions" + }, "accountInformationPage": { "registrationNumber": "Registration Number", "fullName": "Full Name", @@ -38,7 +54,8 @@ "categories": { "NOTIFICATION_SETTINGS": "Notification Settings", "SCIENCE_SETTINGS": "Science Settings", - "SSH_SETTINGS": "SSH" + "SSH_SETTINGS": "SSH Settings", + "VCS_TOKEN_SETTINGS": "VCS Token Settings" }, "settingGroupNames": { "weeklySummary": "Weekly Summary", diff --git a/src/test/cypress/README.md b/src/test/cypress/README.md deleted file mode 100644 index b59bc697c404..000000000000 --- a/src/test/cypress/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Artemis Cypress test suite -This folder contains the End-to-End test suite for Artemis. -The test suite only contains End-to-End tests. Therefore it cannot be run out of the box and has some requirements. Those will be listed in the following. - -# Requirements -1. Running (and reachable) test environment: The Artemis Server and Client have to be deployed somewhere, where the tests can access them. This can be a local setup or a remote one (the Artemis test servers for example) -2. Prepared user accounts on the test environment: See the [corresponding section](#prepared-users) below -3. Node and Chrome installed on the machine, which executes the test suite - -## Prepared users -Currently the test suite does not automatically create the required users with their roles, but expects existing ones. There are several user accounts the test suite requires: -| ROLE | ID(s) | AMOUNT | -|:------------------:|:-------------:|:------:| -| `ADMIN` | - | 1 | -| `USER` | 100, 102, 104 | 3 | -| `TEACHING ASSISTANT` | 101 | 1 | -| `INSTRUCTOR` | 103 | 1 | - -The test suite expects two templates (one for usernames and one for passwords) in its configuration (see the [general configuration section](#general-configuration)), which contain the text `USERID`. When authenticating as one of the non-admin users a test will substitute the `USERID` text in the username and password templates with the required ID (seen in the table) of the user. - -For example, a basic template for a username and password could be `user_USERID` and `password_USERID`. Hence, a test which requires the authentication of an `INSTRUCTOR` would then use `user_103`:`password_103` as credentials. - -An `ADMIN` user is required to create and delete courses. The credentials for an `ADMIN` user are not generated by the templates. Therefore no `ID` is specified in the table above. The `ADMIN` credentials are configured separately. See the [General configuration section](#general-configuration) for details. - -:warning: **The guided tour should be disabled for every user required by the test suite. Otherwise some/all tests in the suite will fail!** - -## Test suite configuration -### General configuration -Once the test environment is running and the required users are added in the test environment the test suite needs to be configured. The configuration is done via two files: -1. `cypress.json`: Contains general settings for Cypress -2. `cypress.env.json`: Contains settings specific for Artemis - -Both configuration files can be found in the cypress subfolder (`src/test/cypress`). - -In the following we will explain what setting in the configuration files has to be adjusted to be able to execute the test suite. Example configurations are shown in the [Example configuration section](#example-configurations) below. - -For `cypress.json`: -* `baseUrl`: The url pointing to the test environment here (make sure that there is no trailing slash) - -For `cypress.env.json`: -* `username`: The username template (as described in the [prepared users section](#prepared-users)) -* `password`: The password template -* `adminUsername`: The admin username (no template) -* `adminPassword`: The admin password (no template) - -### Example configurations -In the following we show example configurations of the test suite for imaginary Artemis setups. For readability we will leave out settings in the configuration files, which do not require the user to adjust them in order to run the tests. -#### Test environment using Gitlab + Jenkins -`cypress.json`: -```json -{ - "baseUrl": "https://imaginary-artemis-server.com" -} -``` -`cypress.env.json`: -```json -{ - "username": "username_USERID", - "password": "password_USERID", - "adminUsername": "admin_username", - "adminPassword": "admin_password" -} - -``` - -# Executing the test suite -Before executing the test suite Cypress with all of its dependencies has to be installed via `npm`. Make sure that the command is executed in the Cypress subfolder of Artemis (`src/test/cypress`). -```bash -npm install - -``` -The test suite can be executed with via the predefined commands in the `package.json`: -1. `npm run cypress:open`: This opens a Cypress Dashboard from where all or individual tests can be executed -2. `npm run cypress:run`: This executes the complete test suite in headless mode - -Individual test specs in the test suite can be executed by passing the `--spec` parameter like so: -```bash -npm run cypress:run -- --spec integration/path/to/spec/file.spec.ts - -``` - -For more information about Cypress and its configuration see the [Cypress documentation](https://docs.cypress.io/guides/getting-started/installing-cypress) - diff --git a/src/test/cypress/certs/artemis-nginx+4-key.pem b/src/test/cypress/certs/artemis-nginx+4-key.pem deleted file mode 100644 index eb2d95da1e9c..000000000000 --- a/src/test/cypress/certs/artemis-nginx+4-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCuCJSHStSQd02f -j+IlQFes7pVUcYv2r0qm5qicGwPcKQf1/nmsy6k4WhE9HQV9VO9LQ4doSNp9NuYX -P/JQqdYLZYYQvxHS+fR7ofIPjirsrbQYAkG5F6imM8H7MkkueG3HGqaKD54PBmC4 -BgBJDFWiF8jSNSYNKOE2L5SaYG/g3LLIkWBlhBQHgrprkio4pv5Y44+nf+hGWkSj -bkRo2+PmIsNmQrpDB2o0O7uoyswa71HE967n9K17SWZ7Hi4kP6BGUWn65P5JB10a -6kz0y8183Uzz99bx8hzxLPg6VNiJZQ+dH4M1Jn6kysKiyV4x24JsM9s6t+Vhln9E -KX5ktosdAgMBAAECggEBAJs3ddkwqWLrtOSR/H2C5G+NHsyAtPdgIfG3mTwZcBjk -03/X5gdyYUusMOHTx3ifzwjOgq9FAvFYjGDCHMlKoGfrtWWsNCZ53k6CApVTE/+h -cRVUte9yJW2Ojf0PPWvf5vEEWPKbuTnnU03ttEVyZdG66tZoprZn9m1QhHYnesEO -PMPvYMd3Oyko8MD/Rr1A/KS/rmc0yfUvgLsqF6PLxq3NKxyVD/8Tp4u9aXbPMnd2 -vugVxjjvt5ubscF1Owi8EjqjVkXlw94JzLcy70XfBzsS2EvUtX/hmHgBEsViXUOQ -KGVyeFTvuReq0RvLQi1LA8vs2q6UC0ZYX75wGDfWWnUCgYEAyP6FY6xdP+N83qEM -TzAf2a33bBCcD5zbrfsvYwHwdzcAz9HBdf3TN1ZcbgfIzIWvuo+hFdjZd32E2+b7 -tSGpcs21iZ3dn1aWxngNs/h94h6cNak/02iCbOsmMX9rHfKZd1ODnQyA8q0s9PQY -uWWWMUfqPse7mSYbgU0aYOVFraMCgYEA3ak3N2mTgTVsUqhNyZCJlmtafp0tsT6b -/7GKSqkl741wokM6un3wx1eo6Q95mngxOlY2xxq9OChnNSEa9ZQnzdUDtQ0YE4QD -09awTIMHNCeSqpV2n3Yv2fT3C5Ya5/WEtYGpVAtqgxwWPij8+VMOa8MVzy+/v6Hg -N1Tpww+Y8D8CgYEAhbEGeK4FuKFQRaVJ0sJn7RrSIIdLxvbHCIqzkl+P2zwyxgj3 -bcxP2dcP1ABJiADESouO0kFTJS/QV5TkiC7DzyEVR1xCNeIamBjyxGrdELLbpLXX -Rn+VgW1IElR2o4zil4RtXuEaRFD8PlK+v1La/ByhqvCfz9aRJQhsK1dVaZECgYEA -jRYR0TFf89P/OLVrnapkCNwX45ND7Bc/0AY/UbpMLSfH02AbV2yl/xvqpT12Vz29 -h7Ysc5qvabk9x/FkaX99vmOhUnIdKv7SONnjqS+VPDsb/XvY3zKozoA/Zp6KTa5W -Y/k9wALsLruH5NTOABw/h5PKo+9uixkLz+w6Ri/9Vp0CgYEAqfkZJe7vCOIwtIwj -Mq5knkJgR+Vq30i4jRoFU0yxIcWA1hODVBnK39+mtA++/3+r5DY5fGRTc9mMyXU/ -y2N2nfSnvPMAUaRmisB7NhmvinEgymlrX+WE+7S9/+nOQADxzWSc6Hxg/ub6mTYV -k2/hv9uG1gbm2+OBP/EBOr48jz0= ------END PRIVATE KEY----- diff --git a/src/test/cypress/certs/artemis-nginx+4.pem b/src/test/cypress/certs/artemis-nginx+4.pem deleted file mode 100644 index 6492786845eb..000000000000 --- a/src/test/cypress/certs/artemis-nginx+4.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIERjCCAq6gAwIBAgIQSQ2vfdquHAQcrzbEKx46mzANBgkqhkiG9w0BAQsFADBf -MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RA -MWY0ZmQzNzYzNmMyMSEwHwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIw -HhcNMjIxMjA1MDk0NTEzWhcNMjUwMzA1MDk0NTEzWjBFMScwJQYDVQQKEx5ta2Nl -cnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxGjAYBgNVBAsMEXJvb3RAMWY0ZmQz -NzYzNmMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgiUh0rUkHdN -n4/iJUBXrO6VVHGL9q9KpuaonBsD3CkH9f55rMupOFoRPR0FfVTvS0OHaEjafTbm -Fz/yUKnWC2WGEL8R0vn0e6HyD44q7K20GAJBuReopjPB+zJJLnhtxxqmig+eDwZg -uAYASQxVohfI0jUmDSjhNi+UmmBv4NyyyJFgZYQUB4K6a5IqOKb+WOOPp3/oRlpE -o25EaNvj5iLDZkK6QwdqNDu7qMrMGu9RxPeu5/Ste0lmex4uJD+gRlFp+uT+SQdd -GupM9MvNfN1M8/fW8fIc8Sz4OlTYiWUPnR+DNSZ+pMrCosleMduCbDPbOrflYZZ/ -RCl+ZLaLHQIDAQABo4GXMIGUMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr -BgEFBQcDATAfBgNVHSMEGDAWgBSpuKALkiwfLnQmm7+JNG2bxGAIgzBMBgNVHREE -RTBDgg1hcnRlbWlzLW5naW54gg9hcnRlbWlzLmV4YW1wbGWCCWxvY2FsaG9zdIcE -fwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAYEApG8ZADQe -SsH/nqH9WpR3ZkYg0rm8pw+YquBNUdDFG2/4IQtaaxrgsvNPrEEMXfCO4vvnC0cH -6Tgay8LzFZxU9D1F06VZ9S1C7KNnYSsjgwhW7wxem1JXgauoutA8D0uHLr/2bVnz -rTShQT7gRp9SRunqDylaSkgpXlfZQRlEANrYT8Jh6LIHRjkxLh/etw7VdFA6Tywh -iQGBE/EbQcGpmqHBoMytblku0D8H+pcFHZ03AZq0FTMbByM9GekQ8HJV88epqvqJ -7pWyQPX9lr7yC6n121dPoA0ylP8D7jIBCmlFeF+QWCiRAgdeb1w+JONHMgI97IR+ -9HBm6gGE+Da/TRq82w02tUN/F7NHdzqwKGx/GKLrEsdNlfP6D9iiVtfBGBoAUm+C -2t3jbQEgqYHA+mzadS75RGJsRnVdY24IHvNjEnESW6KCaSfQyMmp3trRH6JeOttU -2JeqRPjmOzNvzIcB76w1/hB2ljhimyfoxB8Gbrts+GFPRZE+AXg1mvCn ------END CERTIFICATE----- diff --git a/src/test/cypress/certs/rootCA.pem b/src/test/cypress/certs/rootCA.pem deleted file mode 100644 index df27e7384ca9..000000000000 --- a/src/test/cypress/certs/rootCA.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEjTCCAvWgAwIBAgIQU1RC58hYfDrlnkOa5hWT9TANBgkqhkiG9w0BAQsFADBf -MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RA -MWY0ZmQzNzYzNmMyMSEwHwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIw -HhcNMjIxMjA1MDk0NTEzWhcNMzIxMjA1MDk0NTEzWjBfMR4wHAYDVQQKExVta2Nl -cnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RAMWY0ZmQzNzYzNmMyMSEw -HwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIwggGiMA0GCSqGSIb3DQEB -AQUAA4IBjwAwggGKAoIBgQC6XajL1Rz5RQW0D+TxZ16msa5xAcKt5hEclOUB+oK+ -ychkUiDDSeh4EpkA2txu823n8BLqdNrIZ2A/TW3OVRXcD/hYFTbODI+FZ4DFEHoP -b8QnMlbZfo1AVNOR5ZFAVuneytaauhznnd1+Y49ieeX2v81hmBUKTuwDGVwp69rS -WUqANAKuGim7yOyBX6oRaUPg42xIBVHdDXkWr06zcFC6Z1O5zUYmFYHyn/sP2ZRN -BzMM+lVHCbZlw9Yi5TGLDC4wUAmMS9rColE5E76UNuOvfUERZffdLCzkmbS+VqOI -28fWvO/C8qEB10lcjxxplah4JZSyveH3Q5aFzLK2FbnXfLa/ypr3Mn9rBm06CtaL -Moy6h5gxNFDDB4RTpP7kjz32RRbl5xxqgkTEEAGDr/bk/C3DT6pyvGX5k6cj0zWd -o2bAO42Uk4ehMEHEUhnmvJ1yeOLSabgzfTJ3dM1NkdPI3Ss7dJGfraPx0sGet4SB -KMKw3mqJIizd1vV19TA/oRECAwEAAaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1Ud -EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFKm4oAuSLB8udCabv4k0bZvEYAiDMA0G -CSqGSIb3DQEBCwUAA4IBgQBSPmSTP6Twe1T5UNqxfLSG/aKtT9yR1gAOW1zmM8KN -IZEwa1k6AH+y+dBT1MdFtSORVRagKNBwNsqqUMgPlXp9INpo+a5GFT4oAv73CrWu -bmXq7oiKkfk9UcdBNp3n8J+h+urXm4jEcUzTdNt8R631+seKOjYZz1SKOzpV5iz5 -20Mjk36RkiaiH3sxYvVo9ayXATz4Yq+0jWOHuQpYmFe2mH6wlle8hT/bRQb6/2z0 -wG/kKZl/CYbgRhc39np06mQ7FmAyXTtQORfjfhAUEu568JzPl/r2jYX1YgtEP4IL -GZXOdSYdtVC1vfpoWIo1YY+xqg3WRA/qgPFW33pZBk/qTaWE4ISMr0ewv613JDdK -ZOttGRDm2EDmh/FoeRoO5niydjb9DP915xObGMrYXKkPBpoIsQCfcmmFNQBAN4e4 -OQwSP9r/hh1cS8Usk6Ch0/iRhq7lv2Z5irLl/nQ4tPUZDVbBOhJ936t0gySVHbCl -GVOog28PQ8wI3fRZ2FZg6IM= ------END CERTIFICATE----- diff --git a/src/test/cypress/currents.config.js b/src/test/cypress/currents.config.js deleted file mode 100644 index c5cadf96dc1c..000000000000 --- a/src/test/cypress/currents.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - projectId: process.env.SORRY_CYPRESS_PROJECT_ID, - recordKey: process.env.SORRY_CYPRESS_KEY, - cloudServiceUrl: process.env.SORRY_CYPRESS_URL, -}; diff --git a/src/test/cypress/cypress.config.ts b/src/test/cypress/cypress.config.ts deleted file mode 100644 index fa5c941d1de5..000000000000 --- a/src/test/cypress/cypress.config.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { defineConfig } from 'cypress'; -import { cloudPlugin } from 'cypress-cloud/plugin'; -import fs from 'fs'; - -export default defineConfig({ - clientCertificates: [ - { - url: 'https://artemis-nginx', - ca: ['certs/rootCA.pem'], - certs: [ - { - cert: 'certs/artemis-nginx+4.pem', - key: 'certs/artemis-nginx+4-key.pem', - }, - ], - }, - { - url: 'https://artemis.example', - ca: ['certs/rootCA.pem'], - certs: [ - { - cert: 'certs/artemis-nginx+4.pem', - key: 'certs/artemis-nginx+4-key.pem', - }, - ], - }, - { - url: 'https://localhost', - ca: ['certs/rootCA.pem'], - certs: [ - { - cert: 'certs/artemis-nginx+4.pem', - key: 'certs/artemis-nginx+4-key.pem', - }, - ], - }, - ], - fixturesFolder: 'fixtures', - screenshotsFolder: 'screenshots', - videosFolder: 'videos', - video: true, - screenshotOnRunFailure: true, - viewportWidth: 1920, - viewportHeight: 1080, - defaultCommandTimeout: 20000, - responseTimeout: 120000, - reporter: 'junit', - reporterOptions: { - mochaFile: 'build/cypress/test-reports/test-results.[hash].xml', - toConsole: true, - }, - e2e: { - setupNodeEvents(on, config) { - on('task', { - error(message: string) { - console.error('\x1b[31m', 'ERROR: ', message, '\x1b[0m'); - return null; - }, - warn(message: string) { - console.error('\x1b[33m', 'WARNING: ', message, '\x1b[0m'); - return null; - }, - log(message: string) { - console.log('\x1b[37m', 'LOG: ', message, '\x1b[0m'); - return null; - }, - }); - on('after:spec', (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => { - if (results && results.video) { - const failures = results.tests.some((test) => test.attempts.some((attempt) => attempt.state === 'failed')); - if (!failures) { - fs.unlinkSync(results.video); - } - } - }); - on('before:browser:launch', (browser, launchOptions) => { - launchOptions.args.push('--lang=en'); - return launchOptions; - }); - return cloudPlugin(on, config); - }, - specPattern: ['init/ImportUsers.cy.ts', 'e2e/**/*.cy.ts'], - supportFile: 'support/index.ts', - baseUrl: 'http://localhost:8080', - testIsolation: false, - }, -}); diff --git a/src/test/cypress/cypress.env.json b/src/test/cypress/cypress.env.json deleted file mode 100644 index 0904cff1191c..000000000000 --- a/src/test/cypress/cypress.env.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "username": "", - "password": "", - "adminUsername": "", - "adminPassword": "", - "allowGroupCustomization": false, - "studentGroupName": null, - "tutorGroupName": null, - "editorGroupName": null, - "instructorGroupName": null, - "createUsers": false -} diff --git a/src/test/cypress/e2e/Login.cy.ts b/src/test/cypress/e2e/Login.cy.ts deleted file mode 100644 index c34e9cb687f8..000000000000 --- a/src/test/cypress/e2e/Login.cy.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { loginPage, navigationBar } from '../support/artemis'; -import { studentOne } from '../support/users'; - -describe.skip('Login page tests', () => { - it('Logs in via the UI', () => { - cy.visit('/'); - loginPage.login(studentOne); - cy.url().should('include', '/courses'); - cy.getCookie('jwt').should('exist'); - cy.getCookie('jwt').should('have.property', 'value'); - cy.getCookie('jwt').should('have.property', 'httpOnly', true); - cy.getCookie('jwt').should('have.property', 'sameSite', 'lax'); - // TODO: Uncomment once cypress is using https - cy.getCookie('jwt').should('have.property', 'secure', true); - }); - - it('Logs in programmatically and logs out via the UI', () => { - cy.login(studentOne, '/courses'); - cy.url().should('include', '/courses'); - navigationBar.logout(); - cy.url().should('equal', Cypress.config().baseUrl + '/'); - cy.getCookie('jwt').should('not.exist'); - }); - - it('Displays error messages on wrong password', () => { - cy.visit('/'); - loginPage.login({ username: 'some_user_name', password: 'lorem-ipsum' }); - cy.location('pathname').should('eq', '/'); - cy.get('.alert').should('exist').and('have.text', 'Failed to sign in! Please check your username and password and try again.'); - cy.get('#login-button').click(); - cy.get('#login-button').click(); - }); - - it('Fails to access protected resource without login', () => { - cy.visit('/course-management'); - cy.location('pathname').should('eq', '/'); - }); - - it('Verify footer content', () => { - cy.visit('/'); - loginPage.shouldShowFooter(); - loginPage.shouldShowAboutUsInFooter(); - loginPage.shouldShowRequestChangeInFooter(); - loginPage.shouldShowReleaseNotesInFooter(); - loginPage.shouldShowPrivacyStatementInFooter(); - loginPage.shouldShowImprintInFooter(); - }); -}); diff --git a/src/test/cypress/e2e/Logout.cy.ts b/src/test/cypress/e2e/Logout.cy.ts deleted file mode 100644 index d9c988f2b449..000000000000 --- a/src/test/cypress/e2e/Logout.cy.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; - -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest, modelingExerciseEditor, navigationBar } from '../support/artemis'; -import { admin, studentOne, studentTwo } from '../support/users'; -import { convertModelAfterMultiPart } from '../support/utils'; - -describe.skip('Logout tests', () => { - let course: Course; - let modelingExercise: ModelingExercise; - - before('Login as admin and create a course with a modeling exercise', () => { - cy.login(admin); - - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addStudentToCourse(course, studentTwo); - exerciseAPIRequest.createModelingExercise({ course }).then((resp: Cypress.Response) => { - modelingExercise = resp.body; - }); - }); - }); - - it('Logs out by pressing OK when unsaved changes on exercise mode', () => { - cy.login(studentOne); - - const exerciseID = modelingExercise.id!; - cy.visit(`/courses/${course.id}/exercises`); - courseOverview.startExercise(exerciseID); - courseOverview.openRunningExercise(exerciseID); - modelingExerciseEditor.addComponentToModel(exerciseID, 1); - modelingExerciseEditor.addComponentToModel(exerciseID, 2); - navigationBar.logout(); - - cy.on('window:confirm', (text) => { - expect(text).to.contains('You have unsaved changes'); - return true; - }); - cy.url().should('equal', Cypress.config().baseUrl + '/'); - }); - - it('Stays logged in by pressing cancel when trying to logout during unsaved changes on exercise mode', () => { - cy.login(studentTwo); - - const exerciseID = modelingExercise.id!; - cy.visit(`/courses/${course.id}/exercises`); - courseOverview.startExercise(exerciseID); - courseOverview.openRunningExercise(exerciseID); - modelingExerciseEditor.addComponentToModel(exerciseID, 1); - modelingExerciseEditor.addComponentToModel(exerciseID, 2); - navigationBar.logout(); - - cy.on('window:confirm', (text) => { - expect(text).to.contains('You have unsaved changes'); - return false; - }); - cy.url().should('not.equal', Cypress.config().baseUrl + '/'); - }); - - after(() => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/SystemHealth.cy.ts b/src/test/cypress/e2e/SystemHealth.cy.ts deleted file mode 100644 index 21a8d1b46943..000000000000 --- a/src/test/cypress/e2e/SystemHealth.cy.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { admin } from '../support/users'; - -describe.skip('Check artemis system health', () => { - beforeEach('Login as admin and visit system health page', () => { - cy.login(admin, '/admin/health'); - }); - - it('Checks continuous integration health', () => { - cy.get('#healthCheck #continuousIntegrationServer .status').contains('UP'); - }); - - it('Checks version control health', () => { - cy.get('#healthCheck #versionControlServer .status').contains('UP'); - }); - - it('Checks user management health', () => { - cy.get('#healthCheck #userManagement .status').contains('UP'); - }); - - it('Checks database health', () => { - cy.get('#healthCheck #db .status').contains('UP'); - }); - - it('Checks hazelcast health', () => { - cy.get('#healthCheck #hazelcast .status').contains('UP'); - }); - - it('Checks ping health', () => { - cy.get('#healthCheck #ping .status').contains('UP'); - }); - - it('Checks readiness health', () => { - cy.get('#healthCheck #readinessState .status').contains('UP'); - }); - - it('Checks websocket broker health', () => { - cy.get('#healthCheck #websocketBroker .status').contains('UP'); - }); - - it('Checks websocket connection health', () => { - cy.get('#healthCheck #websocketConnection .status').contains('Connected'); - }); -}); diff --git a/src/test/cypress/e2e/course/CourseExercise.cy.ts b/src/test/cypress/e2e/course/CourseExercise.cy.ts deleted file mode 100644 index f07bb65a0551..000000000000 --- a/src/test/cypress/e2e/course/CourseExercise.cy.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; - -import multipleChoiceQuizTemplate from '../../fixtures/exercise/quiz/multiple_choice/template.json'; -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest } from '../../support/artemis'; -import { admin } from '../../support/users'; -import { convertModelAfterMultiPart } from '../../support/utils'; - -describe.skip('Course Exercise', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - describe('Search Exercise', () => { - let exercise1: QuizExercise; - let exercise2: QuizExercise; - let exercise3: QuizExercise; - - before('Create Exercises', () => { - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate], 'Course Exercise Quiz 1').then((response) => { - exercise1 = convertModelAfterMultiPart(response); - }); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate], 'Course Exercise Quiz 2').then((response) => { - exercise2 = convertModelAfterMultiPart(response); - }); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate], 'Course Exercise 3').then((response) => { - exercise3 = convertModelAfterMultiPart(response); - }); - }); - - it('should filter exercises based on title', () => { - cy.visit(`/courses/${course.id}/exercises`); - courseOverview.getExercise(exercise1.title!).should('be.visible'); - courseOverview.getExercise(exercise2.title!).should('be.visible'); - courseOverview.getExercise(exercise3.title!).should('be.visible'); - courseOverview.search('Course Exercise Quiz'); - courseOverview.getExercise(exercise1.title!).should('be.visible'); - courseOverview.getExercise(exercise2.title!).should('be.visible'); - courseOverview.getExercise(exercise3.title!).should('not.exist'); - }); - - after('Delete Exercises', () => { - exerciseAPIRequest.deleteQuizExercise(exercise1.id!); - exerciseAPIRequest.deleteQuizExercise(exercise2.id!); - exerciseAPIRequest.deleteQuizExercise(exercise3.id!); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/course/CourseManagement.cy.ts b/src/test/cypress/e2e/course/CourseManagement.cy.ts deleted file mode 100644 index 9c565a66a275..000000000000 --- a/src/test/cypress/e2e/course/CourseManagement.cy.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; - -import { courseCreation, courseManagement, courseManagementAPIRequest, navigationBar } from '../../support/artemis'; -import { admin, studentOne } from '../../support/users'; -import { convertBooleanToCheckIconClass, convertModelAfterMultiPart, dayjsToString, generateUUID, trimDate } from '../../support/utils'; - -// Common primitives -const courseData = { - title: '', - shortName: '', - description: 'Lore Impsum', - startDate: dayjs(), - endDate: dayjs().add(1, 'day'), - testCourse: true, - semester: 'SS23', - maxPoints: 40, - programmingLanguage: 'JAVA', - customizeGroupNames: false, - studentGroupName: Cypress.env('studentGroupName'), - tutorGroupName: Cypress.env('tutorGroupName'), - editorGroupName: Cypress.env('editorGroupName'), - instructorGroupName: Cypress.env('instructorGroupName'), - enableComplaints: true, - maxComplaints: 5, - maxTeamComplaints: 3, - maxComplaintTimeDays: 6, - enableMoreFeedback: true, - maxRequestMoreFeedbackTimeDays: 4, - presentationScoreEnabled: true, - presentationScore: 10, -}; - -const editedCourseData = { - title: '', - testCourse: false, -}; - -const allowGroupCustomization: boolean = Cypress.env('allowGroupCustomization'); -const dateFormat = 'MMM D, YYYY HH:mm'; - -describe.skip('Course management', () => { - describe('Manual student selection', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin, '/'); - const uid = generateUUID(); - courseData.title = 'Course ' + uid; - courseData.shortName = 'cypress' + uid; - courseManagementAPIRequest.createCourse({ courseName: courseData.title, courseShortName: courseData.shortName }).then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Manually adds and removes a student', () => { - const username = studentOne.username; - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.addStudentToCourse(studentOne); - courseManagement.getRegisteredStudents().contains(username).should('be.visible'); - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.getCourseStudentGroupName().contains(`artemis-${course.shortName}-students (1)`); - - navigationBar.openCourseManagement(); - courseManagement.openStudentOverviewOfCourse(course.id!); - courseManagement.removeFirstUser(); - courseManagement.getRegisteredStudents().contains(username).should('not.exist'); - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.getCourseStudentGroupName().contains(`artemis-${course.shortName}-students (0)`); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); - }); - - describe('Course creation', () => { - let course: Course; - let course2: Course; - - beforeEach('Set course title and shortname', () => { - cy.login(admin, '/'); - const uid = generateUUID(); - courseData.title = 'Course ' + uid; - courseData.shortName = 'cypress' + uid; - }); - - it('Creates a new course', () => { - navigationBar.openCourseManagement(); - courseManagement.openCourseCreation(); - courseCreation.setTitle(courseData.title); - courseCreation.setShortName(courseData.shortName); - courseCreation.setDescription(courseData.description); - courseCreation.setStartDate(courseData.startDate); - courseCreation.setEndDate(courseData.endDate); - courseCreation.setTestCourse(courseData.testCourse); - courseCreation.setSemester(courseData.semester); - courseCreation.setCourseMaxPoints(courseData.maxPoints); - courseCreation.setProgrammingLanguage(courseData.programmingLanguage); - courseCreation.setCustomizeGroupNames(courseData.customizeGroupNames); - courseCreation.setEnableComplaints(courseData.enableComplaints); - courseCreation.setMaxComplaints(courseData.maxComplaints); - courseCreation.setMaxTeamComplaints(courseData.maxTeamComplaints); - courseCreation.setMaxComplaintsTimeDays(courseData.maxComplaintTimeDays); - courseCreation.setEnableMoreFeedback(courseData.enableMoreFeedback); - courseCreation.setMaxRequestMoreFeedbackTimeDays(courseData.maxRequestMoreFeedbackTimeDays); - courseCreation.submit().then((request: Interception) => { - const courseBody = request.response!.body; - course = courseBody; - expect(courseBody.title).to.eq(courseData.title); - expect(courseBody.shortName).to.eq(courseData.shortName); - expect(courseBody.description).to.eq(courseData.description); - expect(courseBody.testCourse).to.eq(courseData.testCourse); - expect(trimDate(courseBody.startDate)).to.eq(trimDate(dayjsToString(courseData.startDate))); - expect(trimDate(courseBody.endDate)).to.eq(trimDate(dayjsToString(courseData.endDate))); - expect(courseBody.semester).to.eq(courseData.semester); - expect(courseBody.maxPoints).to.eq(courseData.maxPoints); - expect(courseBody.defaultProgrammingLanguage).to.eq(courseData.programmingLanguage); - expect(courseBody.complaintsEnabled).to.eq(courseData.enableComplaints); - expect(courseBody.maxComplaints).to.eq(courseData.maxComplaints); - expect(courseBody.maxTeamComplaints).to.eq(courseData.maxTeamComplaints); - expect(courseBody.maxComplaintTimeDays).to.eq(courseData.maxComplaintTimeDays); - expect(courseBody.requestMoreFeedbackEnabled).to.eq(courseData.enableMoreFeedback); - expect(courseBody.studentGroupName).to.eq(`artemis-${courseData.shortName}-students`); - expect(courseBody.editorGroupName).to.eq(`artemis-${courseData.shortName}-editors`); - expect(courseBody.instructorGroupName).to.eq(`artemis-${courseData.shortName}-instructors`); - expect(courseBody.teachingAssistantGroupName).to.eq(`artemis-${courseData.shortName}-tutors`); - }); - courseManagement.getCourseHeaderTitle().scrollIntoView(); - courseManagement.getCourseHeaderTitle().contains(courseData.title).should('be.visible'); - courseManagement.getCourseHeaderDescription().contains(courseData.description); - courseManagement.getCourseTitle().contains(courseData.title); - courseManagement.getCourseShortName().contains(courseData.shortName); - courseManagement.getCourseStudentGroupName().contains(`artemis-${courseData.shortName}-students (0)`); - courseManagement.getCourseTutorGroupName().contains(`artemis-${courseData.shortName}-tutors (0)`); - courseManagement.getCourseEditorGroupName().contains(`artemis-${courseData.shortName}-editors (0)`); - courseManagement.getCourseInstructorGroupName().contains(`artemis-${courseData.shortName}-instructors (0)`); - courseManagement.getCourseStartDate().contains(courseData.startDate.format(dateFormat)); - courseManagement.getCourseEndDate().contains(courseData.endDate.format(dateFormat)); - courseManagement.getCourseSemester().contains(courseData.semester); - courseManagement.getCourseProgrammingLanguage().contains(courseData.programmingLanguage); - courseManagement.getCourseTestCourse().find(convertBooleanToCheckIconClass(courseData.testCourse)).should('exist'); - courseManagement.getCourseMaxComplaints().contains(courseData.maxComplaints); - courseManagement.getCourseMaxTeamComplaints().contains(courseData.maxTeamComplaints); - courseManagement.getMaxComplaintTimeDays().contains(courseData.maxComplaintTimeDays); - courseManagement.getMaxRequestMoreFeedbackTimeDays().contains(courseData.maxRequestMoreFeedbackTimeDays); - }); - - if (allowGroupCustomization) { - it('Creates a new course with custom groups', () => { - navigationBar.openCourseManagement(); - courseManagement.openCourseCreation(); - courseCreation.setTitle(courseData.title); - courseCreation.setShortName(courseData.shortName); - courseCreation.setTestCourse(courseData.testCourse); - courseCreation.setCustomizeGroupNames(true); - courseCreation.setStudentGroup(courseData.studentGroupName); - courseCreation.setTutorGroup(courseData.tutorGroupName); - courseCreation.setEditorGroup(courseData.editorGroupName); - courseCreation.setInstructorGroup(courseData.instructorGroupName); - courseCreation.submit().then((request: Interception) => { - const courseBody = request.response!.body; - course2 = courseBody; - expect(courseBody.title).to.eq(courseData.title); - expect(courseBody.shortName).to.eq(courseData.shortName); - expect(courseBody.testCourse).to.eq(courseData.testCourse); - expect(courseBody.studentGroupName).to.eq(courseData.studentGroupName); - expect(courseBody.teachingAssistantGroupName).to.eq(courseData.tutorGroupName); - expect(courseBody.editorGroupName).to.eq(courseData.editorGroupName); - expect(courseBody.instructorGroupName).to.eq(courseData.instructorGroupName); - }); - courseManagement.getCourseHeaderTitle().scrollIntoView(); - courseManagement.getCourseHeaderTitle().contains(courseData.title).should('be.visible'); - courseManagement.getCourseTitle().contains(courseData.title); - courseManagement.getCourseShortName().contains(courseData.shortName); - courseManagement.getCourseTestCourse().find(convertBooleanToCheckIconClass(courseData.testCourse)).should('exist'); - courseManagement.getCourseStudentGroupName().contains(courseData.studentGroupName); - courseManagement.getCourseTutorGroupName().contains(courseData.tutorGroupName); - courseManagement.getCourseEditorGroupName().contains(courseData.editorGroupName); - courseManagement.getCourseInstructorGroupName().contains(courseData.instructorGroupName); - }); - } - - after('Delete courses', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - courseManagementAPIRequest.deleteCourse(course2, admin); - }); - }); - - describe('Course edit', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin, '/'); - const uid = generateUUID(); - courseData.title = 'Course ' + uid; - courseData.shortName = 'cypress' + uid; - courseManagementAPIRequest.createCourse({ courseName: courseData.title, courseShortName: courseData.shortName }).then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Edits a existing course', () => { - const uid = generateUUID(); - editedCourseData.title = 'Course ' + uid; - - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.openCourseEdit(); - - courseCreation.setTitle(editedCourseData.title); - courseCreation.setTestCourse(editedCourseData.testCourse); - - courseCreation.update().then((request: Interception) => { - course = request.response!.body; - expect(course.title).to.eq(editedCourseData.title); - expect(course.shortName).to.eq(courseData.shortName); - expect(course.testCourse).to.eq(editedCourseData.testCourse); - }); - courseManagement.getCourseHeaderTitle().scrollIntoView(); - courseManagement.getCourseHeaderTitle().contains(editedCourseData.title).should('be.visible'); - courseManagement.getCourseTitle().contains(editedCourseData.title); - courseManagement.getCourseShortName().contains(courseData.shortName); - courseManagement.getCourseTestCourse().find(convertBooleanToCheckIconClass(editedCourseData.testCourse)).should('exist'); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); - }); - - describe('Course deletion', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin, '/'); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Deletes an existing course', () => { - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.deleteCourse(course); - courseManagement.getCourse(course.id!).should('not.exist'); - }); - }); - - describe.only('Course icon deletion', () => { - describe('Course within icon', () => { - let course: Course; - - before('Creates course with icon', () => { - cy.login(admin, '/'); - cy.fixture('course/icon.png', 'base64') - .then(Cypress.Blob.base64StringToBlob) - .then((blob) => { - courseManagementAPIRequest.createCourse({ iconFileName: 'icon.png', iconFile: blob }).then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - }); - - it('Deletes an existing course icon', () => { - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.clickEditCourse(); - courseManagement.removeIconFromCourse(); - courseManagement.updateCourse(course).then(() => { - courseManagement.clickEditCourse(); - courseManagement.checkCourseHasNoIcon(); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); - }); - - describe('Course without icon', () => { - let course: Course; - - before('Creates course without icon', () => { - cy.login(admin, '/'); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Deletes not existing course icon', () => { - navigationBar.openCourseManagement(); - courseManagement.openCourse(course.id!); - courseManagement.clickEditCourse(); - courseManagement.checkCourseHasNoIcon(); - }); - - after('Delete courses', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); - }); - }); -}); diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts deleted file mode 100644 index 3c156e961df5..000000000000 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ /dev/null @@ -1,428 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { Channel } from 'app/entities/metis/conversation/channel.model'; -import { GroupChat } from 'app/entities/metis/conversation/group-chat.model'; - -import { communicationAPIRequest, courseManagementAPIRequest, courseMessages, examAPIRequests, exerciseAPIRequest } from '../../support/artemis'; -import { admin, instructor, studentOne, studentTwo, tutor, users } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID, titleLowercase } from '../../support/utils'; - -describe.skip('Course messages', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addStudentToCourse(course, studentTwo); - }); - }); - - it('accepts code of conduct', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); - cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); - cy.login(studentTwo, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); - cy.login(tutor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); - }); - - describe('Channel messages', () => { - describe('Create channel', () => { - it('check for pre-created channels', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.browseChannelsButton(); - courseMessages.checkChannelsExists('tech-support'); - courseMessages.checkChannelsExists('organization'); - courseMessages.checkChannelsExists('random'); - courseMessages.checkChannelsExists('announcement'); - }); - - it('instructors should be able to create public announcement channel', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'public-ancmnt-ch'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.setDescription('A public announcement channel'); - courseMessages.setPublic(); - courseMessages.setAnnouncementChannel(); - courseMessages.createChannel(true, true); - courseMessages.getName().contains(name); - }); - - it('instructors should be able to create private announcement channel', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'private-ancmnt-ch'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.setDescription('A private announcement channel'); - courseMessages.setPrivate(); - courseMessages.setAnnouncementChannel(); - courseMessages.createChannel(true, false); - courseMessages.getName().contains(name); - }); - - it('instructors should be able to create public unrestricted channel', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'public-unrstct-ch'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.setDescription('A public unrestricted channel'); - courseMessages.setPublic(); - courseMessages.setUnrestrictedChannel(); - courseMessages.createChannel(false, true); - courseMessages.getName().contains(name); - }); - - it('instructors should be able to create private unrestricted channel', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'private-unrstct-ch'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.setDescription('A public unrestricted channel'); - courseMessages.setPrivate(); - courseMessages.setUnrestrictedChannel(); - courseMessages.createChannel(false, false); - courseMessages.getName().contains(name); - }); - - it('instructors should not be able to create channel with uppercase name', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'Forbidden Name'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.getError().contains('Names can only contain lowercase letters'); - }); - - it('instructors should not be able to create channel with name longer than 30 chars', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - const name = 'way-way-way-too-long-channel-title'; - courseMessages.createChannelButton(); - courseMessages.setName(name); - courseMessages.getError().contains('Name can be max 30 characters long!'); - }); - - it('check that channel is created, when a lecture is created', () => { - cy.login(admin); - courseManagementAPIRequest.createLecture(course, 'Test Lecture'); - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.browseLectureChannelsButton(); - courseMessages.checkChannelsExists('lecture-test-lecture'); - }); - - it('check that channel is created, when an exercise is created', () => { - cy.login(admin); - exerciseAPIRequest.createTextExercise({ course }, 'Test Exercise'); - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.browseExerciseChannelsButton(); - courseMessages.checkChannelsExists('exercise-test-exercise'); - }); - - it('check that channel is created, when an exam is created', () => { - cy.login(admin); - const examTitle = 'exam' + generateUUID(); - examAPIRequests.createExam({ course, title: examTitle }); - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.browseExamChannelsButton(); - courseMessages.checkChannelsExists(titleLowercase(examTitle)); - }); - }); - - describe('Edit channel', () => { - let channel: Channel; - before('create channel', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageChannel(course, 'test-channel', 'Test Channel', true, true).then((response) => { - channel = response.body; - communicationAPIRequest.joinUserIntoChannel(course, channel.id!, instructor); - }); - }); - - it('instructors should be able to edit a channel', () => { - cy.login(instructor, `/courses/${course.id}/messages?conversationId=${channel.id}`); - const newName = 'new-test-name'; - const topic = 'test-topic'; - courseMessages.getName().click(); - courseMessages.editName(newName); - courseMessages.editTopic(topic); - courseMessages.editDescription('New Description'); - courseMessages.closeEditPanel(); - cy.reload(); - courseMessages.getName().contains(newName); - courseMessages.getTopic().contains(topic); - }); - }); - - describe('Join channel', () => { - let channel: Channel; - before('create channel', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageChannel(course, 'join-test-channel', 'Join Test Channel', true, true).then((response) => { - channel = response.body; - }); - }); - - it('student should be joined into pre-created channels automatically', () => { - cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.browseChannelsButton(); - courseMessages.getChannelIdByName('tech-support').then((response) => { - const techSupportChannelId = Number(response!); - courseMessages.checkBadgeJoined(techSupportChannelId).should('exist').contains('Joined'); - }); - courseMessages.getChannelIdByName('random').then((response) => { - const techSupportChannelId = Number(response!); - courseMessages.checkBadgeJoined(techSupportChannelId).should('exist').contains('Joined'); - }); - courseMessages.getChannelIdByName('announcement').then((response) => { - const techSupportChannelId = Number(response!); - courseMessages.checkBadgeJoined(techSupportChannelId).should('exist').contains('Joined'); - }); - courseMessages.getChannelIdByName('organization').then((response) => { - const techSupportChannelId = Number(response!); - courseMessages.checkBadgeJoined(techSupportChannelId).should('exist').contains('Joined'); - }); - }); - - it('student should be able to join a public channel', () => { - cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.browseChannelsButton(); - courseMessages.joinChannel(channel.id!); - courseMessages.checkBadgeJoined(channel.id!).should('exist').contains('Joined'); - }); - - it('student should be able to leave a public channel', () => { - cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.browseChannelsButton(); - courseMessages.leaveChannel(channel.id!); - courseMessages.checkBadgeJoined(channel.id!).should('not.exist'); - }); - }); - - describe('Write/edit/delete message in channel', () => { - let channel: Channel; - before('create channel', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageChannel(course, 'write-test-channel', 'Write Test Channel', false, true).then((response) => { - channel = response.body; - communicationAPIRequest.joinUserIntoChannel(course, channel.id!, studentOne); - }); - }); - - it('student should be able to write message in channel', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - const messageText = 'Student Test Message'; - courseMessages.writeMessage(messageText); - courseMessages.save().then((interception) => { - const message = interception.response!.body; - courseMessages.checkMessage(message.id, messageText); - }); - }); - - it('student should be able to edit message in channel', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - const messageText = 'Student Edit Test Message'; - communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { - const message = response.body; - const newMessage = 'Edited Text'; - courseMessages.editMessage(message.id, newMessage); - courseMessages.checkMessage(message.id, newMessage); - courseMessages.getSinglePost(message.id).find('.edited-text').should('exist'); - }); - }); - - it('student should be able to delete message in channel', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - const messageText = 'Student Edit Test Message'; - communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { - const message = response.body; - courseMessages.checkMessage(message.id, messageText); - courseMessages.deleteMessage(message.id); - courseMessages.getSinglePost(message.id).should('not.exist'); - }); - }); - }); - }); - - describe('Group chats', () => { - let instructorName: string; - let tutorName: string; - let studentOneName: string; - let studentTwoName: string; - - before('Get usernames', () => { - cy.login(admin); - users.getUserInfo(instructor.username, (userInfo) => { - instructorName = userInfo.name; - }); - users.getUserInfo(tutor.username, (userInfo) => { - tutorName = userInfo.name; - }); - users.getUserInfo(studentOne.username, (userInfo) => { - studentOneName = userInfo.name; - }); - users.getUserInfo(studentTwo.username, (userInfo) => { - studentTwoName = userInfo.name; - }); - }); - - describe('Create group chat', () => { - it('instructors should be able to create group chat', () => { - cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.createGroupChatButton(); - courseMessages.addUserToGroupChat(studentOne.username); - courseMessages.addUserToGroupChat(studentTwo.username); - courseMessages.createGroupChat().then((interception) => { - const group = interception.response!.body; - courseMessages.listMembersButton(course.id!, group.id); - courseMessages.checkMemberList(studentOneName); - courseMessages.checkMemberList(studentTwoName); - courseMessages.closeEditPanel(); - }); - }); - - it('tutor should be able to create group chat', () => { - cy.login(tutor, `/courses/${course.id}/messages`); - courseMessages.createGroupChatButton(); - courseMessages.addUserToGroupChat(studentOne.username); - courseMessages.addUserToGroupChat(instructor.username); - courseMessages.createGroupChat().then((interception) => { - const group = interception.response!.body; - courseMessages.listMembersButton(course.id!, group.id); - courseMessages.checkMemberList(studentOneName); - courseMessages.checkMemberList(instructorName); - courseMessages.closeEditPanel(); - }); - }); - - it('student should be able to create group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.createGroupChatButton(); - courseMessages.addUserToGroupChat(studentTwo.username); - courseMessages.addUserToGroupChat(tutor.username); - courseMessages.createGroupChat().then((interception) => { - const group = interception.response!.body; - courseMessages.listMembersButton(course.id!, group.id); - courseMessages.checkMemberList(studentTwoName); - courseMessages.checkMemberList(tutorName); - courseMessages.closeEditPanel(); - }); - }); - }); - - describe('Add to group chat', () => { - let groupChat: GroupChat; - before('create group chat', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageGroupChat(course, [studentOne.username, tutor.username]).then((response) => { - groupChat = response.body; - }); - }); - - it('tutor should be able to add user to group chat', () => { - cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.addUserToGroupChatButton(); - courseMessages.addUserToGroupChat(instructor.username); - courseMessages.updateGroupChat(); - - courseMessages.listMembersButton(course.id!, groupChat.id!); - courseMessages.checkMemberList(instructorName); - courseMessages.closeEditPanel(); - }); - - it('student should be able to add user to group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.addUserToGroupChatButton(); - courseMessages.addUserToGroupChat(studentTwo.username); - courseMessages.updateGroupChat(); - - courseMessages.listMembersButton(course.id!, groupChat.id!); - courseMessages.checkMemberList(studentTwoName); - courseMessages.closeEditPanel(); - }); - }); - - describe('Leave group chat', () => { - let groupChat: GroupChat; - const groupChatName = 'leave-test'; - - before('create group chat', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageGroupChat(course, [studentOne.username, tutor.username]).then((response) => { - groupChat = response.body; - communicationAPIRequest.updateCourseMessageGroupChatName(course, groupChat, groupChatName); - }); - }); - - it('tutor should be able to leave group chat', () => { - cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.checkGroupChatExists(groupChatName, true); - courseMessages.listMembersButton(course.id!, groupChat.id!); - courseMessages.openSettingsTab(); - courseMessages.leaveGroupChat(); - cy.visit(`/courses/${course.id}/messages`); - courseMessages.checkGroupChatExists(groupChatName, false); - }); - - it('student should be able to leave group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.checkGroupChatExists(groupChatName, true); - courseMessages.listMembersButton(course.id!, groupChat.id!); - courseMessages.openSettingsTab(); - courseMessages.leaveGroupChat(); - cy.visit(`/courses/${course.id}/messages`); - courseMessages.checkGroupChatExists(groupChatName, false); - }); - }); - - describe('Write/edit/delete message in group chat', () => { - let groupChat: GroupChat; - before('create group chat', () => { - cy.login(admin); - communicationAPIRequest.createCourseMessageGroupChat(course, [studentOne.username, tutor.username]).then((response) => { - groupChat = response.body; - }); - }); - - it('student should be able to write message in group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - const messageText = 'Student Test Message'; - courseMessages.writeMessage(messageText); - courseMessages.save(true).then((interception) => { - const message = interception.response!.body; - courseMessages.checkMessage(message.id, messageText); - }); - }); - - it('student should be able to edit message in group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - const messageText = 'Student Edit Test Message'; - communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { - const message = response.body; - const newMessage = 'Edited Text'; - courseMessages.editMessage(message.id, newMessage); - courseMessages.checkMessage(message.id, newMessage); - courseMessages.getSinglePost(message.id).find('.edited-text').should('exist'); - }); - }); - - it('student should be able to delete message in group chat', () => { - cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - const messageText = 'Student Edit Test Message'; - communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { - const message = response.body; - courseMessages.checkMessage(message.id, messageText); - courseMessages.deleteMessage(message.id); - courseMessages.getSinglePost(message.id).should('not.exist'); - }); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/ExamAssessment.cy.ts b/src/test/cypress/e2e/exam/ExamAssessment.cy.ts deleted file mode 100644 index 37c6a20822bc..000000000000 --- a/src/test/cypress/e2e/exam/ExamAssessment.cy.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs, { Dayjs } from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import javaPartiallySuccessful from '../../fixtures/exercise/programming/java/partially_successful/submission.json'; -import { - courseAssessment, - courseManagementAPIRequest, - examAPIRequests, - examAssessment, - examExerciseGroupCreation, - examManagement, - examNavigation, - examParticipation, - examStartEnd, - exerciseAssessment, - modelingExerciseAssessment, - studentAssessment, -} from '../../support/artemis'; -import { Exercise, ExerciseType, ProgrammingExerciseAssessmentType } from '../../support/constants'; -import { admin, instructor, studentOne, tutor, users } from '../../support/users'; -import { convertModelAfterMultiPart } from '../../support/utils'; - -let exam: Exam; - -describe('Exam assessment', () => { - let course: Course; - let examEnd: Dayjs; - let programmingAssessmentSuccessful = false; - let modelingAssessmentSuccessful = false; - let textAssessmentSuccessful = false; - let studentOneName: string; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - }); - users.getUserInfo(studentOne.username, (userInfo) => { - studentOneName = userInfo.name; - }); - }); - - // For some reason the typing of cypress gets slower the longer the test runs, so we test the programming exercise first - describe('Programming exercise assessment', () => { - before('Prepare exam', () => { - examEnd = dayjs().add(2, 'minutes'); - prepareExam(course, examEnd, ExerciseType.PROGRAMMING); - }); - - it('Assess a programming exercise submission (MANUAL)', () => { - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentOneName); - cy.login(tutor); - startAssessing(course.id!, exam.id!, 175000); - examAssessment.addNewFeedback(2, 'Good job'); - examAssessment.submit(); - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - examParticipation.getResultScore().should('contain.text', '66.2%').and('be.visible'); - programmingAssessmentSuccessful = true; - }); - - it('Complaints about programming exercises assessment', () => { - if (programmingAssessmentSuccessful) { - handleComplaint(course, exam, false, ExerciseType.PROGRAMMING); - } - }); - }); - - describe('Modeling exercise assessment', () => { - before('Prepare exam', () => { - examEnd = dayjs().add(45, 'seconds'); - prepareExam(course, examEnd, ExerciseType.MODELING); - }); - - it('Assess a modeling exercise submission', () => { - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentOneName); - cy.login(tutor); - startAssessing(course.id!, exam.id!, 60000); - modelingExerciseAssessment.addNewFeedback(5, 'Good'); - modelingExerciseAssessment.openAssessmentForComponent(1); - modelingExerciseAssessment.assessComponent(-1, 'Wrong'); - modelingExerciseAssessment.clickNextAssessment(); - modelingExerciseAssessment.assessComponent(0, 'Neutral'); - modelingExerciseAssessment.clickNextAssessment(); - examAssessment.submitModelingAssessment().then((assessmentResponse: Interception) => { - expect(assessmentResponse.response?.statusCode).to.equal(200); - }); - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - examParticipation.getResultScore().should('contain.text', '40%').and('be.visible'); - modelingAssessmentSuccessful = true; - }); - - it('Complaints about modeling exercises assessment', () => { - if (modelingAssessmentSuccessful) { - handleComplaint(course, exam, true, ExerciseType.MODELING); - } - }); - }); - - describe('Text exercise assessment', () => { - before('Prepare exam', () => { - examEnd = dayjs().add(40, 'seconds'); - prepareExam(course, examEnd, ExerciseType.TEXT); - }); - - it('Assess a text exercise submission', () => { - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentOneName); - cy.login(tutor); - startAssessing(course.id!, exam.id!, 60000); - examAssessment.addNewFeedback(7, 'Good job'); - examAssessment.submitTextAssessment().then((assessmentResponse: Interception) => { - expect(assessmentResponse.response!.statusCode).to.equal(200); - }); - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - examParticipation.getResultScore().should('contain.text', '70%').and('be.visible'); - textAssessmentSuccessful = true; - }); - - it('Complaints about text exercises assessment', () => { - if (textAssessmentSuccessful) { - handleComplaint(course, exam, false, ExerciseType.TEXT); - } - }); - }); - - describe.only('Quiz exercise assessment', () => { - let resultDate: Dayjs; - - before('Prepare exam', () => { - examEnd = dayjs().add(30, 'seconds'); - resultDate = examEnd.add(5, 'seconds'); - prepareExam(course, examEnd, ExerciseType.QUIZ); - }); - - it.skip('Assesses quiz automatically', () => { - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentOneName); - if (dayjs().isBefore(examEnd)) { - cy.wait(examEnd.diff(dayjs(), 'ms') + 1000); - } - examManagement.openAssessmentDashboard(course.id!, exam.id!, 60000); - cy.visit(`/course-management/${course.id}/exams/${exam.id}/assessment-dashboard`); - courseAssessment.clickEvaluateQuizzes().its('response.statusCode').should('eq', 200); - if (dayjs().isBefore(resultDate)) { - cy.wait(resultDate.diff(dayjs(), 'ms') + 1000); - } - examManagement.checkQuizSubmission(course.id!, exam.id!, studentOneName, '50%'); - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - examParticipation.getResultScore().should('contain.text', '50%').and('be.visible'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); - -function prepareExam(course: Course, end: dayjs.Dayjs, exerciseType: ExerciseType) { - cy.login(admin); - const resultDate = end.add(1, 'second'); - const examConfig: Exam = { - course, - startDate: dayjs(), - endDate: end, - numberOfCorrectionRoundsInExam: 1, - examStudentReviewStart: resultDate, - examStudentReviewEnd: resultDate.add(1, 'minute'), - publishResultsDate: resultDate, - gracePeriod: 10, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examAPIRequests.registerStudentForExam(exam, studentOne); - let additionalData = {}; - switch (exerciseType) { - case ExerciseType.PROGRAMMING: - additionalData = { submission: javaPartiallySuccessful, progExerciseAssessmentType: ProgrammingExerciseAssessmentType.SEMI_AUTOMATIC }; - break; - case ExerciseType.TEXT: - additionalData = { textFixture: 'loremIpsum-short.txt' }; - break; - case ExerciseType.QUIZ: - additionalData = { quizExerciseID: 0 }; - break; - } - - examExerciseGroupCreation.addGroupWithExercise(exam, exerciseType, additionalData).then((response) => { - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - makeExamSubmission(course, exam, response); - }); - }); -} - -function makeExamSubmission(course: Course, exam: Exam, exercise: Exercise) { - examParticipation.startParticipation(studentOne, course, exam); - examNavigation.openExerciseAtIndex(0); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - cy.wait(2000); - examNavigation.handInEarly(); - examStartEnd.finishExam(); -} - -function startAssessing(courseID: number, examID: number, timeout: number) { - examManagement.openAssessmentDashboard(courseID, examID, timeout); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - exerciseAssessment.clickStartNewAssessment(); - exerciseAssessment.getLockedMessage(); -} - -function handleComplaint(course: Course, exam: Exam, reject: boolean, exerciseType: ExerciseType) { - const complaintText = 'Lorem ipsum dolor sit amet'; - const complaintResponseText = ' consetetur sadipscing elitr'; - - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - studentAssessment.startComplaint(); - studentAssessment.enterComplaint(complaintText); - studentAssessment.submitComplaint(); - cy.get('.message').should('contain.text', 'Your complaint has been submitted'); - - cy.login(instructor, '/course-management/' + course.id + '/exams'); - examManagement.openAssessmentDashboard(course.id!, exam.id!); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - - exerciseAssessment.clickEvaluateComplaint(); - exerciseAssessment.getComplaintText().should('have.value', complaintText); - if (reject) { - examAssessment.rejectComplaint(complaintResponseText, true, exerciseType); - } else { - examAssessment.acceptComplaint(complaintResponseText, true, exerciseType); - } - if (exerciseType == ExerciseType.MODELING) { - cy.get('.message').should('contain.text', 'Response to complaint has been submitted'); - } else { - cy.get('.message').should('contain.text', 'The assessment was updated successfully.'); - } - - cy.login(studentOne, '/courses/' + course.id + '/exams/' + exam.id); - if (reject) { - studentAssessment.getComplaintBadge().should('contain.text', 'Complaint was rejected'); - } else { - studentAssessment.getComplaintBadge().should('contain.text', 'Complaint was accepted'); - } - studentAssessment.getComplaintResponse().should('have.value', complaintResponseText); -} diff --git a/src/test/cypress/e2e/exam/ExamCreationDeletion.cy.ts b/src/test/cypress/e2e/exam/ExamCreationDeletion.cy.ts deleted file mode 100644 index 9622cfd6fce4..000000000000 --- a/src/test/cypress/e2e/exam/ExamCreationDeletion.cy.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import { courseManagement, courseManagementAPIRequest, examAPIRequests, examCreation, examDetails, examManagement, navigationBar } from '../../support/artemis'; -import { admin } from '../../support/users'; -import { convertModelAfterMultiPart, dayjsToString, generateUUID, trimDate } from '../../support/utils'; - -// Common primitives -const examData = { - title: 'exam' + generateUUID(), - visibleDate: dayjs(), - startDate: dayjs().add(1, 'day'), - endDate: dayjs().add(2, 'day'), - numberOfExercisesInExam: 4, - examMaxPoints: 40, - startText: 'Exam start text', - endText: 'Exam end text', - confirmationStartText: 'Exam confirmation start text', - confirmationEndText: 'Exam confirmation end text', -}; - -const editedExamData = { - title: 'exam' + generateUUID(), - visibleDate: dayjs(), - startDate: dayjs().add(2, 'day'), - endDate: dayjs().add(4, 'day'), - numberOfExercisesInExam: 3, - examMaxPoints: 30, - startText: 'Edited exam start text', - endText: 'Edited exam end text', - confirmationStartText: 'Edited exam confirmation start text', - confirmationEndText: 'Edited exam confirmation end text', -}; - -const dateFormat = 'MMM D, YYYY HH:mm'; - -describe.skip('Exam creation/deletion', () => { - let course: Course; - let examId: number; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - beforeEach(() => { - cy.login(admin, '/'); - }); - - it('Creates an exam', () => { - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - - examManagement.createNewExam(); - examCreation.setTitle(examData.title); - examCreation.setVisibleDate(examData.visibleDate); - examCreation.setStartDate(examData.startDate); - examCreation.setEndDate(examData.endDate); - examCreation.setNumberOfExercises(examData.numberOfExercisesInExam); - examCreation.setExamMaxPoints(examData.examMaxPoints); - - examCreation.setStartText(examData.startText); - examCreation.setEndText(examData.endText); - examCreation.setConfirmationStartText(examData.confirmationStartText); - examCreation.setConfirmationEndText(examData.confirmationEndText); - examCreation.submit().then((examResponse: Interception) => { - const examBody = examResponse.response!.body; - examId = examBody.id; - expect(examResponse.response!.statusCode).to.eq(201); - expect(examBody.testExam).to.be.false; - expect(trimDate(examBody.visibleDate)).to.eq(trimDate(dayjsToString(examData.visibleDate))); - expect(trimDate(examBody.startDate)).to.eq(trimDate(dayjsToString(examData.startDate))); - expect(trimDate(examBody.endDate)).to.eq(trimDate(dayjsToString(examData.endDate))); - expect(examBody.numberOfExercisesInExam).to.eq(examData.numberOfExercisesInExam); - expect(examBody.examMaxPoints).to.eq(examData.examMaxPoints); - expect(examBody.startText).to.eq(examData.startText); - expect(examBody.endText).to.eq(examData.endText); - expect(examBody.confirmationStartText).to.eq(examData.confirmationStartText); - expect(examBody.confirmationEndText).to.eq(examData.confirmationEndText); - cy.url().should('contain', `/exams/${examId}`); - }); - examManagement.getExamTitle().contains(examData.title); - examManagement.getExamVisibleDate().contains(examData.visibleDate.format(dateFormat)); - examManagement.getExamStartDate().contains(examData.startDate.format(dateFormat)); - examManagement.getExamEndDate().contains(examData.endDate.format(dateFormat)); - examManagement.getExamNumberOfExercises().contains(examData.numberOfExercisesInExam); - examManagement.getExamMaxPoints().contains(examData.examMaxPoints); - examManagement.getExamStartText().contains(examData.startText); - examManagement.getExamEndText().contains(examData.endText); - examManagement.getExamConfirmationStartText().contains(examData.confirmationStartText); - examManagement.getExamConfirmationEndText().contains(examData.confirmationEndText); - examManagement.getExamWorkingTime().contains('1d 0h'); - }); - - describe('Exam deletion', () => { - before('Create exam', () => { - examData.title = 'exam' + generateUUID(); - const examConfig: Exam = { - course, - title: examData.title, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - examId = examResponse.body.id!; - }); - }); - - it('Deletes an existing exam', () => { - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExam(examId); - examDetails.deleteExam(examData.title); - examManagement.getExamSelector(examData.title).should('not.exist'); - }); - }); - - describe('Edits an exam', () => { - before('Create exam', () => { - examData.title = 'exam' + generateUUID(); - const examConfig: Exam = { - course, - title: examData.title, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - examId = examResponse.body.id!; - }); - }); - - it('Edits an existing exam', () => { - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExam(examId); - examManagement.getExamTitle().contains(examData.title); - examManagement.clickEdit(); - - examCreation.setTitle(editedExamData.title); - examCreation.setVisibleDate(editedExamData.visibleDate); - examCreation.setStartDate(editedExamData.startDate); - examCreation.setEndDate(editedExamData.endDate); - examCreation.setNumberOfExercises(editedExamData.numberOfExercisesInExam); - examCreation.setExamMaxPoints(editedExamData.examMaxPoints); - - examCreation.setStartText(editedExamData.startText); - examCreation.setEndText(editedExamData.endText); - examCreation.setConfirmationStartText(editedExamData.confirmationStartText); - examCreation.setConfirmationEndText(editedExamData.confirmationEndText); - examCreation.update().then((examResponse: Interception) => { - const examBody = examResponse.response!.body; - examId = examBody.id; - expect(examResponse.response!.statusCode).to.eq(200); - expect(examBody.testExam).to.be.false; - expect(trimDate(examBody.visibleDate)).to.eq(trimDate(dayjsToString(editedExamData.visibleDate))); - expect(trimDate(examBody.startDate)).to.eq(trimDate(dayjsToString(editedExamData.startDate))); - expect(trimDate(examBody.endDate)).to.eq(trimDate(dayjsToString(editedExamData.endDate))); - expect(examBody.numberOfExercisesInExam).to.eq(editedExamData.numberOfExercisesInExam); - expect(examBody.examMaxPoints).to.eq(editedExamData.examMaxPoints); - expect(examBody.startText).to.eq(editedExamData.startText); - expect(examBody.endText).to.eq(editedExamData.endText); - expect(examBody.confirmationStartText).to.eq(editedExamData.confirmationStartText); - expect(examBody.confirmationEndText).to.eq(editedExamData.confirmationEndText); - cy.url().should('contain', `/exams/${examId}`); - }); - examManagement.getExamTitle().contains(editedExamData.title); - examManagement.getExamVisibleDate().contains(editedExamData.visibleDate.format(dateFormat)); - examManagement.getExamStartDate().contains(editedExamData.startDate.format(dateFormat)); - examManagement.getExamEndDate().contains(editedExamData.endDate.format(dateFormat)); - examManagement.getExamNumberOfExercises().contains(editedExamData.numberOfExercisesInExam); - examManagement.getExamMaxPoints().contains(editedExamData.examMaxPoints); - examManagement.getExamStartText().contains(editedExamData.startText); - examManagement.getExamEndText().contains(editedExamData.endText); - examManagement.getExamConfirmationStartText().contains(editedExamData.confirmationStartText); - examManagement.getExamConfirmationEndText().contains(editedExamData.confirmationEndText); - examManagement.getExamWorkingTime().contains('2d 0h'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/ExamDateVerification.cy.ts b/src/test/cypress/e2e/exam/ExamDateVerification.cy.ts deleted file mode 100644 index f6cc2ee0bdea..000000000000 --- a/src/test/cypress/e2e/exam/ExamDateVerification.cy.ts +++ /dev/null @@ -1,151 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { ExerciseGroup } from 'app/entities/exercise-group.model'; - -import { - courseManagementAPIRequest, - courseOverview, - examAPIRequests, - examNavigation, - examParticipation, - examStartEnd, - exerciseAPIRequest, - textExerciseEditor, -} from '../../support/artemis'; -import { admin, studentOne } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -describe.skip('Exam date verification', () => { - let course: Course; - let examTitle: string; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - }); - }); - - beforeEach(() => { - examTitle = 'exam' + generateUUID(); - cy.login(admin, '/'); - }); - - describe('Exam timing', () => { - let exam: Exam; - it('Does not show exam before visible date', () => { - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().add(1, 'day'), - startDate: dayjs().add(2, 'days'), - endDate: dayjs().add(3, 'days'), - }; - examAPIRequests.createExam(examConfig).then((response) => { - exam = response.body; - }); - cy.login(studentOne, `/courses`); - cy.contains(examTitle).should('not.exist'); - cy.visit(`/courses/${course.id}`); - cy.url().should('contain', `${course.id}`); - cy.contains(examTitle).should('not.exist'); - }); - - it('Shows after visible date', () => { - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(5, 'days'), - startDate: dayjs().add(2, 'days'), - endDate: dayjs().add(3, 'days'), - }; - examAPIRequests.createExam(examConfig).then((response) => { - exam = response.body; - examAPIRequests.registerStudentForExam(exam, studentOne); - cy.login(studentOne, `/courses/${course.id}`); - cy.url().should('contain', `${course.id}`); - courseOverview.openExamsTab(); - courseOverview.openExam(exam.id!); - cy.url({ timeout: 40000 }).should('contain', `/exams/${exam.id}`); - }); - }); - - it('Student can start after start Date', () => { - let exerciseGroup: ExerciseGroup; - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'days'), - startDate: dayjs().subtract(2, 'days'), - endDate: dayjs().add(3, 'days'), - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examAPIRequests.registerStudentForExam(exam, studentOne); - examAPIRequests.addExerciseGroupForExam(exam).then((groupResponse) => { - exerciseGroup = groupResponse.body; - exerciseAPIRequest.createTextExercise({ exerciseGroup }).then((exerciseResponse) => { - const exercise = exerciseResponse.body; - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - cy.login(studentOne, `/courses/${course.id}/exams`); - courseOverview.openExam(exam.id!); - cy.url().should('contain', `/exams/${exam.id}`); - cy.contains(exam.title!).should('be.visible'); - examStartEnd.startExam(); - examNavigation.openExerciseAtIndex(0); - cy.fixture('loremIpsum-short.txt').then((submission) => { - textExerciseEditor.typeSubmission(exercise.id!, submission); - }); - examNavigation.clickSave(); - }); - }); - }); - }); - - it('Exam ends after end time', () => { - let exerciseGroup: ExerciseGroup; - const examEnd = dayjs().add(30, 'seconds'); - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'days'), - startDate: dayjs().subtract(2, 'days'), - endDate: examEnd, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examAPIRequests.registerStudentForExam(exam, studentOne); - examAPIRequests.addExerciseGroupForExam(exam).then((groupResponse) => { - exerciseGroup = groupResponse.body; - exerciseAPIRequest.createTextExercise({ exerciseGroup }).then((exerciseResponse) => { - const exercise = exerciseResponse.body; - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - cy.login(studentOne, `/courses/${course.id}/exams`); - courseOverview.openExam(exam.id!); - cy.contains(exam.title!).should('be.visible'); - examStartEnd.startExam(); - examNavigation.openExerciseAtIndex(0); - cy.fixture('loremIpsum-short.txt').then((submissionText) => { - textExerciseEditor.typeSubmission(exercise.id!, submissionText); - }); - examNavigation.clickSave(); - if (examEnd.isAfter(dayjs())) { - cy.wait(examEnd.diff(dayjs())); - } - examParticipation.checkExamFinishedTitle(exam.title!); - examStartEnd.finishExam(); - }); - }); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/ExamManagement.cy.ts b/src/test/cypress/e2e/exam/ExamManagement.cy.ts deleted file mode 100644 index f86fa782374e..000000000000 --- a/src/test/cypress/e2e/exam/ExamManagement.cy.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { ExerciseGroup } from 'app/entities/exercise-group.model'; - -import { - courseManagement, - courseManagementAPIRequest, - examAPIRequests, - examExerciseGroupCreation, - examExerciseGroups, - examManagement, - modelingExerciseCreation, - navigationBar, - programmingExerciseCreation, - quizExerciseCreation, - studentExamManagement, - textExerciseCreation, -} from '../../support/artemis'; -import { admin, instructor, studentOne } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -describe.skip('Exam management', () => { - let course: Course; - let exam: Exam; - let createdGroup: ExerciseGroup; - let groupCount = 0; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - const examConfig: Exam = { - course, - title: 'Exam ' + generateUUID(), - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - }); - }); - }); - - describe('Manage Group', () => { - let exerciseGroup: ExerciseGroup; - - before(() => { - cy.login(instructor); - examAPIRequests.addExerciseGroupForExam(exam).then((response) => { - exerciseGroup = response.body; - groupCount++; - }); - }); - - beforeEach(() => { - cy.login(instructor); - }); - - it('Create exercise group', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.shouldShowNumberOfExerciseGroups(groupCount); - examExerciseGroups.clickAddExerciseGroup(); - const groupName = 'Group 1'; - examExerciseGroupCreation.typeTitle(groupName); - examExerciseGroupCreation.isMandatoryBoxShouldBeChecked(); - examExerciseGroupCreation.clickSave().then((interception) => { - const group = interception.response!.body; - groupCount++; - examExerciseGroups.shouldHaveTitle(group.id, groupName); - examExerciseGroups.shouldShowNumberOfExerciseGroups(groupCount); - createdGroup = group; - }); - }); - - it('Adds a text exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddTextExercise(exerciseGroup.id!); - const textExerciseTitle = 'Text ' + generateUUID(); - textExerciseCreation.typeTitle(textExerciseTitle); - textExerciseCreation.typeMaxPoints(10); - textExerciseCreation.create().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, textExerciseTitle); - }); - - it('Adds a quiz exercise', () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddQuizExercise(exerciseGroup.id!); - const quizExerciseTitle = 'Quiz ' + generateUUID(); - quizExerciseCreation.setTitle(quizExerciseTitle); - quizExerciseCreation.addMultipleChoiceQuestion(quizExerciseTitle, 10); - quizExerciseCreation.saveQuiz().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, quizExerciseTitle); - }); - - it('Adds a modeling exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddModelingExercise(exerciseGroup.id!); - const modelingExerciseTitle = 'Modeling ' + generateUUID(); - modelingExerciseCreation.setTitle(modelingExerciseTitle); - modelingExerciseCreation.setPoints(10); - modelingExerciseCreation.save().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, modelingExerciseTitle); - }); - - it('Adds a programming exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddProgrammingExercise(exerciseGroup.id!); - const uid = generateUUID(); - const programmingExerciseTitle = 'Programming ' + uid; - const programmingExerciseShortName = 'programming' + uid; - programmingExerciseCreation.setTitle(programmingExerciseTitle); - programmingExerciseCreation.setShortName(programmingExerciseShortName); - programmingExerciseCreation.setPackageName('de.test'); - programmingExerciseCreation.setPoints(10); - programmingExerciseCreation.generate().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, programmingExerciseTitle); - }); - - it('Edits an exercise group', () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.shouldHaveTitle(exerciseGroup.id!, exerciseGroup.title!); - examExerciseGroups.clickEditGroup(exerciseGroup.id!); - const newGroupName = 'Group 3'; - examExerciseGroupCreation.typeTitle(newGroupName); - examExerciseGroupCreation.update(); - examExerciseGroups.shouldHaveTitle(exerciseGroup.id!, newGroupName); - exerciseGroup.title = newGroupName; - }); - - it('Delete an exercise group', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExerciseGroups(exam.id!); - // If the group in the "Create group test" was created successfully, we delete it so there is no group with no exercise - let group = exerciseGroup; - if (createdGroup) { - group = createdGroup; - } - examExerciseGroups.clickDeleteGroup(group.id!, group.title!); - examExerciseGroups.shouldNotExist(group.id!); - }); - }); - - describe('Manage Students', () => { - beforeEach(() => { - cy.login(instructor); - }); - - it('Registers the course students for the exam', () => { - // We already verified in the previous test that we can navigate here - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openStudentRegistration(exam.id!); - studentExamManagement.clickRegisterCourseStudents().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - }); - studentExamManagement.getRegisteredStudents().contains(studentOne.username).should('be.visible'); - }); - - it('Generates student exams', () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openStudentExams(exam.id!); - studentExamManagement.clickGenerateStudentExams(); - studentExamManagement.getGenerateStudentExamsButton().should('be.disabled'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/ExamParticipation.cy.ts b/src/test/cypress/e2e/exam/ExamParticipation.cy.ts deleted file mode 100644 index a0c047580ada..000000000000 --- a/src/test/cypress/e2e/exam/ExamParticipation.cy.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import javaAllSuccessfulSubmission from '../../fixtures/exercise/programming/java/all_successful/submission.json'; -import { - courseManagementAPIRequest, - examAPIRequests, - examExerciseGroupCreation, - examManagement, - examNavigation, - examParticipation, - examStartEnd, - textExerciseEditor, -} from '../../support/artemis'; -import { Exercise, ExerciseType } from '../../support/constants'; -import { admin, instructor, studentFour, studentThree, studentTwo, tutor, users } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -// Common primitives -const textFixture = 'loremIpsum.txt'; -const textFixtureShort = 'loremIpsum-short.txt'; - -describe.skip('Exam participation', () => { - let course: Course; - let exerciseArray: Array = []; - let studentTwoName: string; - let studentThreeName: string; - let studentFourName: string; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentTwo); - courseManagementAPIRequest.addStudentToCourse(course, studentThree); - courseManagementAPIRequest.addStudentToCourse(course, studentFour); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - }); - users.getUserInfo(studentTwo.username, (userInfo) => { - studentTwoName = userInfo.name; - }); - users.getUserInfo(studentThree.username, (userInfo) => { - studentThreeName = userInfo.name; - }); - users.getUserInfo(studentFour.username, (userInfo) => { - studentFourName = userInfo.name; - }); - }); - - describe('Early Hand-in', () => { - let exam: Exam; - const examTitle = 'exam' + generateUUID(); - - before('Create exam', () => { - cy.login(admin); - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'minutes'), - startDate: dayjs().subtract(2, 'minutes'), - endDate: dayjs().add(1, 'hour'), - examMaxPoints: 40, - numberOfExercisesInExam: 4, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - Promise.all([ - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.PROGRAMMING, { submission: javaAllSuccessfulSubmission }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.QUIZ, { quizExerciseID: 0 }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.MODELING), - ]).then((responses) => { - exerciseArray = exerciseArray.concat(responses); - }); - - examAPIRequests.registerStudentForExam(exam, studentTwo); - examAPIRequests.registerStudentForExam(exam, studentThree); - examAPIRequests.registerStudentForExam(exam, studentFour); - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - }); - }); - - it('Participates as a student in a registered exam', () => { - examParticipation.startParticipation(studentTwo, course, exam); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examNavigation.openExerciseAtIndex(j); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - examParticipation.handInEarly(); - examStartEnd.pressShowSummary(); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examParticipation.verifyExerciseTitleOnFinalPage(exercise.id, exercise.exerciseGroup!.title!); - if (exercise.type === ExerciseType.TEXT) { - examParticipation.verifyTextExerciseOnFinalPage(exercise.additionalData!.textFixture!); - } - } - examParticipation.checkExamTitle(examTitle); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentTwoName); - }); - - it('Using save and continue to navigate within exam', () => { - examParticipation.startParticipation(studentThree, course, exam); - examNavigation.openExerciseAtIndex(0); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - // Skip programming exercise this time to save execution time - // (we also need to use the navigation bar here, since programming exercises do not have a "Save and continue" button) - if (exercise.type == ExerciseType.PROGRAMMING) { - examNavigation.openExerciseAtIndex(j + 1); - } else { - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - examParticipation.clickSaveAndContinue(); - } - } - examParticipation.handInEarly(); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentThreeName); - }); - - it('Using exercise overview to navigate within exam', () => { - examParticipation.startParticipation(studentFour, course, exam); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - // Skip programming exercise this time to save execution time - // (we also need to use the navigation bar here, since programming exercises do not have a "Save and continue" button) - if (exercise.type == ExerciseType.PROGRAMMING) { - continue; - } else { - examNavigation.openExerciseOverview(); - examParticipation.selectExerciseOnOverview(j + 1); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - } - examParticipation.handInEarly(); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentFourName); - }); - }); - - describe('Early hand-in with continue and reload page', () => { - let exam: Exam; - const examTitle = 'exam' + generateUUID(); - - before('Create exam', () => { - exerciseArray = []; - - cy.login(admin); - - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'minutes'), - startDate: dayjs().subtract(2, 'minutes'), - endDate: dayjs().add(1, 'hour'), - examMaxPoints: 10, - numberOfExercisesInExam: 1, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }).then((response) => { - exerciseArray.push(response); - }); - - examAPIRequests.registerStudentForExam(exam, studentTwo); - examAPIRequests.registerStudentForExam(exam, studentThree); - examAPIRequests.registerStudentForExam(exam, studentFour); - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - }); - }); - - it('Participates in the exam, hand-in early, but instead continues', () => { - examParticipation.startParticipation(studentTwo, course, exam); - const textExerciseIndex = 0; - const textExercise = exerciseArray[textExerciseIndex]; - examNavigation.openExerciseAtIndex(textExerciseIndex); - examParticipation.makeTextExerciseSubmission(textExercise.id, textExercise.additionalData!.textFixture!); - examParticipation.clickSaveAndContinue(); - examNavigation.handInEarly(); - - examStartEnd.clickContinue(); - examNavigation.openExerciseAtIndex(textExerciseIndex); - textExerciseEditor.clearSubmission(textExercise.id); - examParticipation.makeTextExerciseSubmission(textExercise.id, textFixtureShort); - examParticipation.clickSaveAndContinue(); - - examParticipation.handInEarly(); - examStartEnd.pressShowSummary(); - examParticipation.verifyTextExerciseOnFinalPage(textFixtureShort); - examParticipation.checkExamTitle(examTitle); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentTwoName); - }); - - it('Reloads exam page during participation and ensures that everything is as expected', () => { - examParticipation.startParticipation(studentThree, course, exam); - const textExerciseIndex = 0; - const textExercise = exerciseArray[textExerciseIndex]; - examNavigation.openExerciseAtIndex(textExerciseIndex); - examParticipation.makeTextExerciseSubmission(textExercise.id, textExercise.additionalData!.textFixture!); - examParticipation.clickSaveAndContinue(); - - cy.reload(); - examParticipation.startParticipation(studentThree, course, exam); - examNavigation.openExerciseAtIndex(textExerciseIndex); - textExerciseEditor.checkCurrentContent(textExercise.id, textExercise.additionalData!.textFixture!); - examParticipation.clickSaveAndContinue(); - examParticipation.handInEarly(); - examStartEnd.pressShowSummary(); - examParticipation.verifyTextExerciseOnFinalPage(textExercise.additionalData!.textFixture!); - examParticipation.checkExamTitle(examTitle); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentThreeName); - }); - - it('Reloads exam result page and ensures that everything is as expected', () => { - examParticipation.startParticipation(studentFour, course, exam); - const textExerciseIndex = 0; - const textExercise = exerciseArray[textExerciseIndex]; - examNavigation.openExerciseAtIndex(textExerciseIndex); - examParticipation.makeTextExerciseSubmission(textExercise.id, textExercise.additionalData!.textFixture!); - examParticipation.clickSaveAndContinue(); - examParticipation.handInEarly(); - examStartEnd.pressShowSummary(); - examParticipation.verifyTextExerciseOnFinalPage(textExercise.additionalData!.textFixture!); - examParticipation.checkExamTitle(examTitle); - - cy.reload(); - - examParticipation.verifyTextExerciseOnFinalPage(textExercise.additionalData!.textFixture!); - examParticipation.checkExamTitle(examTitle); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentFourName); - }); - }); - - describe('Normal Hand-in', () => { - let exam: Exam; - const examTitle = 'exam' + generateUUID(); - - before('Create exam', () => { - exerciseArray = []; - - cy.login(admin); - - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'minutes'), - startDate: dayjs().subtract(2, 'minutes'), - endDate: dayjs().add(30, 'seconds'), - examMaxPoints: 10, - numberOfExercisesInExam: 1, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }).then((response) => { - exerciseArray.push(response); - }); - - examAPIRequests.registerStudentForExam(exam, studentFour); - examAPIRequests.generateMissingIndividualExams(exam); - examAPIRequests.prepareExerciseStartForExam(exam); - }); - }); - - it('Participates as a student in a registered exam', () => { - examParticipation.startParticipation(studentFour, course, exam); - const textExerciseIndex = 0; - const textExercise = exerciseArray[textExerciseIndex]; - examNavigation.openExerciseAtIndex(textExerciseIndex); - examParticipation.makeSubmission(textExercise.id, textExercise.type, textExercise.additionalData); - examParticipation.clickSaveAndContinue(); - examParticipation.checkExamFullnameInputExists(); - examParticipation.checkYourFullname(studentFourName); - examStartEnd.finishExam().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - }); - examStartEnd.pressShowSummary(); - examParticipation.verifyTextExerciseOnFinalPage(textExercise.additionalData!.textFixture!); - examParticipation.checkExamTitle(examTitle); - - cy.login(instructor); - examManagement.verifySubmitted(course.id!, exam.id!, studentFourName); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/ExamTestRun.cy.ts b/src/test/cypress/e2e/exam/ExamTestRun.cy.ts deleted file mode 100644 index f835fa27a288..000000000000 --- a/src/test/cypress/e2e/exam/ExamTestRun.cy.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import javaBuildErrorSubmission from '../../fixtures/exercise/programming/java/build_error/submission.json'; -import { courseManagementAPIRequest, examAPIRequests, examExerciseGroupCreation, examManagement, examNavigation, examParticipation, examTestRun } from '../../support/artemis'; -import { Exercise, ExerciseType } from '../../support/constants'; -import { admin, instructor } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -// Common primitives -const textFixture = 'loremIpsum-short.txt'; -const examTitle = 'exam' + generateUUID(); - -describe.skip('Exam test run', () => { - let course: Course; - let exam: Exam; - let testRun: any; - let exerciseArray: Array = []; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - const examConfig: Exam = { - course, - title: examTitle, - visibleDate: dayjs().subtract(3, 'days'), - startDate: dayjs().add(1, 'days'), - endDate: dayjs().add(3, 'days'), - examMaxPoints: 40, - numberOfExercisesInExam: 4, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - Promise.all([ - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.PROGRAMMING, { submission: javaBuildErrorSubmission, practiceMode: true }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.QUIZ, { quizExerciseID: 0 }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.MODELING), - ]).then((responses) => { - exerciseArray = exerciseArray.concat(responses); - }); - }); - }); - }); - - beforeEach('Create test run instance', () => { - cy.login(instructor); - // TODO: API call does not work yet, for now we use the UI to create the test run - // courseManagementAPIRequests.createExamTestRun(exam, exerciseArray).then((response: any) => { - // testRun = response.body; - // }); - cy.visit(`/course-management/${course.id}/exams/${exam.id}`); - examManagement.openTestRun(); - examTestRun.createTestRun(); - examTestRun.setWorkingTimeMinutes(2); - examTestRun.confirmTestRun().then((testRunResponse: Interception) => { - testRun = testRunResponse.response!.body; - }); - }); - - it('Create a test run', () => { - cy.login(instructor); - - let testRunID: number; - const minutes = 40; - const seconds = 30; - - cy.visit(`/course-management/${course.id}/exams/${exam.id}`); - examManagement.openTestRun(); - examTestRun.createTestRun(); - examTestRun.setWorkingTimeMinutes(minutes); - examTestRun.setWorkingTimeSeconds(seconds); - examTestRun.confirmTestRun().then((testRunResponse: Interception) => { - const testRun = testRunResponse.response!.body; - testRunID = testRun.id; - - expect(testRunResponse.response!.statusCode).to.eq(200); - expect(testRun.testRun).to.be.true; - expect(testRun.submitted).to.be.false; - expect(testRun.workingTime).to.eq(minutes * 60 + seconds); - - examTestRun.getWorkingTime(testRunID).contains('40min 30s'); - examTestRun.getStarted(testRunID).contains('No'); - examTestRun.getSubmitted(testRunID).contains('No'); - }); - }); - - it('Change test run working time', () => { - const hour = 1; - const minutes = 20; - const seconds = 45; - - cy.login(instructor); - examTestRun.openTestRunPage(course, exam); - examTestRun.changeWorkingTime(testRun.id); - examTestRun.setWorkingTimeHours(hour); - examTestRun.setWorkingTimeMinutes(minutes); - examTestRun.setWorkingTimeSeconds(seconds); - examTestRun.saveTestRun().then((testRunResponse: Interception) => { - const testRun = testRunResponse.response!.body; - - expect(testRun.id).to.eq(testRun.id); - expect(testRunResponse.response!.statusCode).to.eq(200); - expect(testRun.workingTime).to.eq(hour * 3600 + minutes * 60 + seconds); - - examTestRun.openTestRunPage(course, exam); - examTestRun.getWorkingTime(testRun.id).contains(`${hour}h ${minutes}min ${seconds}s`); - examTestRun.getStarted(testRun.id).contains('No'); - examTestRun.getSubmitted(testRun.id).contains('No'); - }); - }); - - it('Conducts a test run', () => { - examTestRun.startParticipation(instructor, course, exam, testRun.id); - examTestRun.getTestRunRibbon().contains('Test Run'); - - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examNavigation.openExerciseAtIndex(j); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - examParticipation.handInEarly(); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examParticipation.verifyExerciseTitleOnFinalPage(exercise.id, exercise.exerciseGroup!.title!); - if (exercise.type === ExerciseType.TEXT) { - examParticipation.verifyTextExerciseOnFinalPage(exercise.additionalData!.textFixture!); - } - } - examParticipation.checkExamTitle(examTitle); - examTestRun.openTestRunPage(course, exam); - examTestRun.getStarted(testRun.id).contains('Yes'); - examTestRun.getSubmitted(testRun.id).contains('Yes'); - }); - - it('Deletes a test run', () => { - cy.login(instructor); - examTestRun.openTestRunPage(course, exam); - examTestRun.getTestRunIdElement(testRun.id).should('exist'); - examTestRun.deleteTestRun(testRun.id); - examTestRun.getTestRun(testRun.id).should('not.exist'); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/test-exam/TestExamCreationDeletion.cy.ts b/src/test/cypress/e2e/exam/test-exam/TestExamCreationDeletion.cy.ts deleted file mode 100644 index 66d49298a858..000000000000 --- a/src/test/cypress/e2e/exam/test-exam/TestExamCreationDeletion.cy.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import { courseManagement, courseManagementAPIRequest, examAPIRequests, examCreation, examDetails, examManagement, navigationBar } from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, dayjsToString, generateUUID, trimDate } from '../../../support/utils'; - -// Common primitives -const examData = { - title: 'exam' + generateUUID(), - visibleDate: dayjs(), - startDate: dayjs().add(1, 'day'), - endDate: dayjs().add(2, 'day'), - workingTime: 5, - numberOfExercises: 4, - maxPoints: 40, - startText: 'Exam start text', - endText: 'Exam end text', - confirmationStartText: 'Exam confirmation start text', - confirmationEndText: 'Exam confirmation end text', -}; - -describe.skip('Test Exam creation/deletion', () => { - let course: Course; - let exam: Exam; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - beforeEach(() => { - cy.login(admin, '/'); - }); - - it('Creates a test exam', function () { - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - - examManagement.createNewExam(); - examCreation.setTitle(examData.title); - examCreation.setTestMode(); - examCreation.setVisibleDate(examData.visibleDate); - examCreation.setStartDate(examData.startDate); - examCreation.setEndDate(examData.endDate); - examCreation.setWorkingTime(examData.workingTime); - examCreation.setNumberOfExercises(examData.numberOfExercises); - examCreation.setExamMaxPoints(examData.maxPoints); - - examCreation.setStartText(examData.startText); - examCreation.setEndText(examData.endText); - examCreation.setConfirmationStartText(examData.confirmationStartText); - examCreation.setConfirmationEndText(examData.confirmationEndText); - - examCreation.submit().then((examResponse: Interception) => { - const examBody = examResponse.response!.body; - exam = examResponse.response!.body; - expect(examResponse.response!.statusCode).to.eq(201); - expect(examBody.title).to.eq(examData.title); - expect(examBody.testExam).to.be.true; - expect(trimDate(examBody.visibleDate)).to.eq(trimDate(dayjsToString(examData.visibleDate))); - expect(trimDate(examBody.startDate)).to.eq(trimDate(dayjsToString(examData.startDate))); - expect(trimDate(examBody.endDate)).to.eq(trimDate(dayjsToString(examData.endDate))); - expect(examBody.workingTime).to.eq(examData.workingTime * 60); - expect(examBody.numberOfExercisesInExam).to.eq(examData.numberOfExercises); - expect(examBody.examMaxPoints).to.eq(examData.maxPoints); - expect(examBody.startText).to.eq(examData.startText); - expect(examBody.endText).to.eq(examData.endText); - expect(examBody.confirmationStartText).to.eq(examData.confirmationStartText); - expect(examBody.confirmationEndText).to.eq(examData.confirmationEndText); - cy.url().should('contain', `/exams/${exam.id}`); - }); - examManagement.getExamTitle().contains(examData.title); - }); - - describe('Test exam deletion', () => { - beforeEach(() => { - examData.title = 'exam' + generateUUID(); - const examConfig: Exam = { - course, - title: examData.title, - testExam: true, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - }); - }); - - it('Deletes an existing test exam', () => { - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.getExamSelector(examData.title).should('exist'); - examManagement.openExam(exam.id!); - examDetails.deleteExam(examData.title); - examManagement.getExamSelector(examData.title).should('not.exist'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/test-exam/TestExamManagement.cy.ts b/src/test/cypress/e2e/exam/test-exam/TestExamManagement.cy.ts deleted file mode 100644 index 435595a7a2bc..000000000000 --- a/src/test/cypress/e2e/exam/test-exam/TestExamManagement.cy.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { ExerciseGroup } from 'app/entities/exercise-group.model'; - -import { - courseManagement, - courseManagementAPIRequest, - examAPIRequests, - examExerciseGroupCreation, - examExerciseGroups, - examManagement, - modelingExerciseCreation, - navigationBar, - programmingExerciseCreation, - quizExerciseCreation, - textExerciseCreation, -} from '../../../support/artemis'; -import { admin, instructor, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -// Common primitives -const uid = generateUUID(); -const examTitle = 'test-exam' + uid; - -describe.skip('Test Exam management', () => { - let course: Course; - let exam: Exam; - let createdGroup: ExerciseGroup; - let groupCount = 0; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - examAPIRequests.createExam({ course, title: examTitle, testExam: true }).then((examResponse) => { - exam = examResponse.body; - }); - }); - }); - - describe('Manage Group', () => { - let exerciseGroup: ExerciseGroup; - - before(() => { - cy.login(instructor); - examAPIRequests.addExerciseGroupForExam(exam).then((response) => { - exerciseGroup = response.body; - groupCount++; - }); - }); - - beforeEach(() => { - cy.login(instructor); - }); - - it('Create exercise group', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.shouldShowNumberOfExerciseGroups(groupCount); - examExerciseGroups.clickAddExerciseGroup(); - const groupName = 'Group 1'; - examExerciseGroupCreation.typeTitle(groupName); - examExerciseGroupCreation.isMandatoryBoxShouldBeChecked(); - examExerciseGroupCreation.clickSave().then((interception) => { - const group = interception.response!.body; - groupCount++; - examExerciseGroups.shouldHaveTitle(group.id, groupName); - examExerciseGroups.shouldShowNumberOfExerciseGroups(groupCount); - createdGroup = group; - }); - }); - - it('Adds a text exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddTextExercise(exerciseGroup.id!); - const textExerciseTitle = 'text' + uid; - textExerciseCreation.typeTitle(textExerciseTitle); - textExerciseCreation.typeMaxPoints(10); - textExerciseCreation.create().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, textExerciseTitle); - }); - - it('Adds a quiz exercise', () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddQuizExercise(exerciseGroup.id!); - const quizExerciseTitle = 'quiz' + uid; - quizExerciseCreation.setTitle(quizExerciseTitle); - quizExerciseCreation.addMultipleChoiceQuestion(quizExerciseTitle, 10); - quizExerciseCreation.saveQuiz().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, quizExerciseTitle); - }); - - it('Adds a modeling exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddModelingExercise(exerciseGroup.id!); - const modelingExerciseTitle = 'modeling' + uid; - modelingExerciseCreation.setTitle(modelingExerciseTitle); - modelingExerciseCreation.setPoints(10); - modelingExerciseCreation.save().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, modelingExerciseTitle); - }); - - it('Adds a programming exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.clickAddProgrammingExercise(exerciseGroup.id!); - const programmingExerciseTitle = 'programming' + uid; - programmingExerciseCreation.setTitle(programmingExerciseTitle); - programmingExerciseCreation.setShortName(programmingExerciseTitle); - programmingExerciseCreation.setPackageName('de.test'); - programmingExerciseCreation.setPoints(10); - programmingExerciseCreation.generate().its('response.statusCode').should('eq', 201); - examExerciseGroups.visitPageViaUrl(course.id!, exam.id!); - examExerciseGroups.shouldContainExerciseWithTitle(exerciseGroup.id!, programmingExerciseTitle); - }); - - it('Edits an exercise group', () => { - cy.visit(`/course-management/${course.id}/exams`); - examManagement.openExerciseGroups(exam.id!); - examExerciseGroups.shouldHaveTitle(exerciseGroup.id!, exerciseGroup.title!); - examExerciseGroups.clickEditGroup(exerciseGroup.id!); - const newGroupName = 'Group 3'; - examExerciseGroupCreation.typeTitle(newGroupName); - examExerciseGroupCreation.update(); - examExerciseGroups.shouldHaveTitle(exerciseGroup.id!, newGroupName); - exerciseGroup.title = newGroupName; - }); - - it('Delete an exercise group', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openExerciseGroups(exam.id!); - // If the group in the "Create group test" was created successfully, we delete it so there is no group with no exercise - let group = exerciseGroup; - if (createdGroup) { - group = createdGroup; - } - examExerciseGroups.clickDeleteGroup(group.id!, group.title!); - examExerciseGroups.shouldNotExist(group.id!); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/test-exam/TestExamParticipation.cy.ts b/src/test/cypress/e2e/exam/test-exam/TestExamParticipation.cy.ts deleted file mode 100644 index f22ab0fed6a2..000000000000 --- a/src/test/cypress/e2e/exam/test-exam/TestExamParticipation.cy.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import javaAllSuccessfulSubmission from '../../../fixtures/exercise/programming/java/all_successful/submission.json'; -import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; -import { courseManagementAPIRequest, examAPIRequests, examExerciseGroupCreation, examNavigation, examParticipation, examStartEnd } from '../../../support/artemis'; -import { Exercise, ExerciseType } from '../../../support/constants'; -import { admin, studentFour, studentThree, studentTwo, users } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -// Common primitives -const textFixture = 'loremIpsum-short.txt'; - -describe.skip('Test exam participation', () => { - let course: Course; - let exerciseArray: Array = []; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentTwo); - courseManagementAPIRequest.addStudentToCourse(course, studentThree); - courseManagementAPIRequest.addStudentToCourse(course, studentFour); - }); - }); - - describe('Early Hand-in', () => { - let exam: Exam; - const examTitle = 'test-exam' + generateUUID(); - - before('Create test exam', () => { - cy.login(admin); - const examConfig: Exam = { - course, - title: examTitle, - testExam: true, - startDate: dayjs().subtract(1, 'day'), - visibleDate: dayjs().subtract(2, 'days'), - examMaxPoints: 100, - numberOfExercisesInExam: 10, - numberOfCorrectionRoundsInExam: 0, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - Promise.all([ - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.PROGRAMMING, { submission: javaAllSuccessfulSubmission, expectedScore: 100 }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.PROGRAMMING, { submission: javaBuildErrorSubmission, expectedScore: 0 }), - - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.QUIZ, { quizExerciseID: 0 }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.QUIZ, { quizExerciseID: 0 }), - - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.MODELING), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.MODELING), - ]).then((responses) => { - exerciseArray = exerciseArray.concat(responses); - }); - }); - }); - - it('Participates as a student in a registered test exam', () => { - examParticipation.startParticipation(studentTwo, course, exam); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examNavigation.openExerciseAtIndex(j); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - examParticipation.handInEarly(); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examParticipation.verifyExerciseTitleOnFinalPage(exercise.id, exercise.exerciseGroup!.title!); - if (exercise.type === ExerciseType.TEXT) { - examParticipation.verifyTextExerciseOnFinalPage(exercise.additionalData!.textFixture!); - } - } - examParticipation.checkExamTitle(examTitle); - }); - - it('Using save and continue to navigate within exam', () => { - examParticipation.startParticipation(studentThree, course, exam); - examNavigation.openExerciseAtIndex(0); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - // Skip programming exercise this time to save execution time - // (we also need to use the navigation bar here, since programming exercises do not have a "Save and continue" button) - if (exercise.type == ExerciseType.PROGRAMMING) { - examNavigation.openExerciseAtIndex(j + 1); - } else { - examParticipation.checkExerciseTitle(exerciseArray[j].id, exerciseArray[j].exerciseGroup!.title!); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - examParticipation.clickSaveAndContinue(); - } - } - examParticipation.handInEarly(); - }); - - it('Using exercise overview to navigate within exam', () => { - examParticipation.startParticipation(studentFour, course, exam); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - // Skip programming exercise this time to save execution time - // (we also need to use the navigation bar here, since programming exercises do not have a "Save and continue" button) - if (exercise.type === ExerciseType.PROGRAMMING) { - continue; - } else { - examNavigation.openExerciseOverview(); - examParticipation.selectExerciseOnOverview(j + 1); - examParticipation.checkExerciseTitle(exerciseArray[j].id, exerciseArray[j].exerciseGroup!.title!); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - } - examParticipation.handInEarly(); - }); - }); - - describe('Normal Hand-in', () => { - let exam: Exam; - let studentFourName: string; - const examTitle = 'exam' + generateUUID(); - - before('Create exam', () => { - exerciseArray = []; - - cy.login(admin); - - users.getUserInfo(studentFour.username, (userInfo) => { - studentFourName = userInfo.name; - }); - - const examConfig: Exam = { - course, - title: examTitle, - testExam: true, - startDate: dayjs().subtract(1, 'day'), - visibleDate: dayjs().subtract(2, 'days'), - workingTime: 15, - examMaxPoints: 10, - numberOfCorrectionRoundsInExam: 1, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }).then((response) => { - exerciseArray.push(response); - }); - }); - }); - - it('Participates as a student in a registered exam', () => { - examParticipation.startParticipation(studentFour, course, exam); - const textExerciseIndex = 0; - const textExercise = exerciseArray[textExerciseIndex]; - examNavigation.openExerciseAtIndex(textExerciseIndex); - examParticipation.makeSubmission(textExercise.id, textExercise.type, textExercise.additionalData); - examParticipation.clickSaveAndContinue(); - examParticipation.checkExamFullnameInputExists(); - examParticipation.checkYourFullname(studentFourName); - examStartEnd.finishExam().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - }); - examParticipation.verifyTextExerciseOnFinalPage(textExercise.additionalData!.textFixture!); - examParticipation.checkExamTitle(examTitle); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/test-exam/TestExamStudentExams.cy.ts b/src/test/cypress/e2e/exam/test-exam/TestExamStudentExams.cy.ts deleted file mode 100644 index 63e7df7b6a31..000000000000 --- a/src/test/cypress/e2e/exam/test-exam/TestExamStudentExams.cy.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { CypressUserManagement, admin, studentOne, studentThree, studentTwo } from '../../../support/users'; -import { - courseManagement, - courseManagementAPIRequest, - examAPIRequests, - examExerciseGroupCreation, - examManagement, - examNavigation, - examParticipation, - navigationBar, - studentExamManagement, -} from '../../../support/artemis'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; -import { CypressCredentials } from '../../../support/users'; -import { Exercise, ExerciseType } from '../../../support/constants'; -import dayjs from 'dayjs/esm'; - -// Common primitives -const uid = generateUUID(); -const examTitle = 'test-exam' + uid; -const textFixture = 'loremIpsum-short.txt'; -const students: Array = [studentOne, studentTwo, studentThree]; -const studentNames: string[] = []; - -const userManagement = new CypressUserManagement(); - -let examExercise: Exercise; - -describe.skip('Test Exam - student exams', () => { - let course: Course; - let exam: Exam; - - before('Create course and exam', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - - for (const student of students) { - courseManagementAPIRequest.addStudentToCourse(course, student); - } - - const examConfig: Exam = { - course, - title: examTitle, - testExam: true, - startDate: dayjs().subtract(1, 'day'), - visibleDate: dayjs().subtract(2, 'days'), - workingTime: 5, - examMaxPoints: 10, - numberOfCorrectionRoundsInExam: 1, - }; - - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }).then((response) => { - examExercise = response; - participateInExam(students.at(0)!, course, exam, true, true); - participateInExam(students.at(1)!, course, exam, true, false); - participateInExam(students.at(2)!, course, exam, false, false); - }); - }); - }); - }); - - before('Get student names', () => { - cy.login(admin); - for (let index = 0; index < students.length; index++) { - userManagement.getUserInfo(students[index].username, (userInfo) => { - studentNames[index] = userInfo.name; - }); - } - }); - - beforeEach(() => { - cy.login(admin); - }); - - describe('Check exam participants and their submissions', () => { - it('Open the list of exam students', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openStudentExams(exam.id!); - for (const student of students) { - studentExamManagement.checkExamStudent(student.username); - } - studentExamManagement.getStudentExamRows().should('have.length', students.length); - - studentExamManagement.checkExamProperty(students.at(0)!.username, 'Started', 'Yes'); - studentExamManagement.checkExamProperty(students.at(1)!.username, 'Started', 'Yes'); - studentExamManagement.checkExamProperty(students.at(2)!.username, 'Started', 'No'); - - studentExamManagement.checkExamProperty(students.at(0)!.username, 'Submitted', 'Yes'); - studentExamManagement.checkExamProperty(students.at(1)!.username, 'Submitted', 'No'); - studentExamManagement.checkExamProperty(students.at(2)!.username, 'Submitted', 'No'); - - studentExamManagement.checkExamProperty(students.at(1)!.username, 'Used working time', '0s'); - studentExamManagement.checkExamProperty(students.at(2)!.username, 'Used working time', '0s'); - - studentExamManagement.checkExamProperty(students.at(0)!.username, 'Student', studentNames.at(0)!.trim()); - studentExamManagement.checkExamProperty(students.at(1)!.username, 'Student', studentNames.at(1)!.trim()); - studentExamManagement.checkExamProperty(students.at(2)!.username, 'Student', studentNames.at(2)!.trim()); - }); - - it('Search for a student in exams', () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExamsOfCourse(course.id!); - examManagement.openStudentExams(exam.id!); - - let searchText = students.at(0)!.username + ', ' + students.at(1)!.username; - studentExamManagement.typeSearchText(searchText); - studentExamManagement.checkExamStudent(students.at(0)!.username); - studentExamManagement.checkExamStudent(students.at(1)!.username); - - searchText = studentNames.at(0)! + ', ' + studentNames.at(1)!; - studentExamManagement.typeSearchText(searchText); - studentExamManagement.checkExamStudent(students.at(0)!.username); - studentExamManagement.checkExamStudent(students.at(1)!.username); - - searchText = 'Artemis Test User'; - studentExamManagement.typeSearchText(searchText); - studentExamManagement.checkExamStudent(students.at(0)!.username); - studentExamManagement.checkExamStudent(students.at(1)!.username); - studentExamManagement.checkExamStudent(students.at(2)!.username); - }); - }); - - function participateInExam(student: CypressCredentials, course: Course, exam: Exam, toStart: boolean, toSubmit: boolean) { - if (!toStart) { - examParticipation.openExam(student, course, exam); - } else { - examParticipation.startParticipation(student, course, exam); - examNavigation.openExerciseAtIndex(0); - examParticipation.makeSubmission(examExercise.id, examExercise.type, examExercise.additionalData); - } - - if (toSubmit) { - examParticipation.handInEarly(); - } - } - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exam/test-exam/TestExamTestRun.cy.ts b/src/test/cypress/e2e/exam/test-exam/TestExamTestRun.cy.ts deleted file mode 100644 index 015700fb7a0f..000000000000 --- a/src/test/cypress/e2e/exam/test-exam/TestExamTestRun.cy.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; -import { courseManagementAPIRequest, examAPIRequests, examExerciseGroupCreation, examManagement, examNavigation, examParticipation, examTestRun } from '../../../support/artemis'; -import { Exercise, ExerciseType } from '../../../support/constants'; -import { admin, instructor } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -// Common primitives -const textFixture = 'loremIpsum.txt'; -const examTitle = 'exam' + generateUUID(); - -describe.skip('Test exam test run', () => { - let course: Course; - let exam: Exam; - let testRun: any; - let exerciseArray: Array = []; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - const examConfig: Exam = { - course, - title: examTitle, - testExam: true, - examMaxPoints: 40, - numberOfExercisesInExam: 4, - }; - examAPIRequests.createExam(examConfig).then((examResponse) => { - exam = examResponse.body; - Promise.all([ - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.TEXT, { textFixture }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.PROGRAMMING, { submission: javaBuildErrorSubmission, practiceMode: true }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.QUIZ, { quizExerciseID: 0 }), - examExerciseGroupCreation.addGroupWithExercise(exam, ExerciseType.MODELING), - ]).then((responses) => { - exerciseArray = exerciseArray.concat(responses); - }); - }); - }); - }); - - beforeEach('Create test run instance', () => { - cy.login(instructor); - // TODO: API call does not work yet, for now we use the UI to create the test run - // courseManagementAPIRequest.createExamTestRun(exam, exerciseArray).then((response: any) => { - // testRun = response.body; - // }); - cy.visit(`/course-management/${course.id}/exams/${exam.id}`); - examManagement.openTestRun(); - examTestRun.createTestRun(); - examTestRun.setWorkingTimeMinutes(2); - examTestRun.confirmTestRun().then((testRunResponse: Interception) => { - testRun = testRunResponse.response!.body; - }); - }); - - it('Create a test run', () => { - cy.login(instructor); - - let testRunID: number; - const minutes = 40; - const seconds = 30; - - cy.visit(`/course-management/${course.id}/exams/${exam.id}`); - examManagement.openTestRun(); - examTestRun.createTestRun(); - examTestRun.setWorkingTimeMinutes(minutes); - examTestRun.setWorkingTimeSeconds(seconds); - examTestRun.confirmTestRun().then((testRunResponse: Interception) => { - const testRun = testRunResponse.response!.body; - testRunID = testRun.id; - - expect(testRunResponse.response!.statusCode).to.eq(200); - expect(testRun.testRun).to.be.true; - expect(testRun.submitted).to.be.false; - expect(testRun.workingTime).to.eq(minutes * 60 + seconds); - - examTestRun.getWorkingTime(testRunID).contains('40min 30s'); - examTestRun.getStarted(testRunID).contains('No'); - examTestRun.getSubmitted(testRunID).contains('No'); - }); - }); - - // Skip since this functionality is not yet supported - // TODO: Activate test as soon as editing test run working times for test exams is possible - // it.skip('Change test run working time', () => { - // const hour = 1; - // const minutes = 20; - // const seconds = 45; - - // cy.login(instructor); - // examTestRun.openTestRunPage(course, exam); - // examTestRun.changeWorkingTime(testRun.id); - // examTestRun.setWorkingTimeHours(hour); - // examTestRun.setWorkingTimeMinutes(minutes); - // examTestRun.setWorkingTimeSeconds(seconds); - // examTestRun.saveTestRun().then((testRunResponse: Interception) => { - // const testRun = testRunResponse.response!.body; - - // expect(testRun.id).to.eq(testRun.id); - // expect(testRunResponse.response!.statusCode).to.eq(200); - // expect(testRun.workingTime).to.eq(hour * 3600 + minutes * 60 + seconds); - - // examTestRun.openTestRunPage(course, exam); - // examTestRun.getWorkingTime(testRun.id).contains(`${hour}h ${minutes}min ${seconds}s`); - // examTestRun.getStarted(testRun.id).contains('No'); - // examTestRun.getSubmitted(testRun.id).contains('No'); - // }); - // }); - - it('Conducts a test run', () => { - examTestRun.startParticipation(instructor, course, exam, testRun.id); - examTestRun.getTestRunRibbon().contains('Test Run'); - - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examNavigation.openExerciseAtIndex(j); - examParticipation.makeSubmission(exercise.id, exercise.type, exercise.additionalData); - } - examParticipation.handInEarly(); - for (let j = 0; j < exerciseArray.length; j++) { - const exercise = exerciseArray[j]; - examParticipation.verifyExerciseTitleOnFinalPage(exercise.id, exercise.exerciseGroup!.title!); - if (exercise.type === ExerciseType.TEXT) { - examParticipation.verifyTextExerciseOnFinalPage(exercise.additionalData!.textFixture!); - } - } - examParticipation.checkExamTitle(examTitle); - examTestRun.openTestRunPage(course, exam); - examTestRun.getStarted(testRun.id).contains('Yes'); - examTestRun.getSubmitted(testRun.id).contains('Yes'); - }); - - it('Deletes a test run', () => { - cy.login(instructor); - examTestRun.openTestRunPage(course, exam); - examTestRun.getTestRunIdElement(testRun.id).should('exist'); - examTestRun.deleteTestRun(testRun.id); - examTestRun.getTestRun(testRun.id).should('not.exist'); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/ExerciseImport.cy.ts b/src/test/cypress/e2e/exercises/ExerciseImport.cy.ts deleted file mode 100644 index 306a07bfd0a8..000000000000 --- a/src/test/cypress/e2e/exercises/ExerciseImport.cy.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; - -import javaPartiallySuccessfulSubmission from '../../fixtures/exercise/programming/java/partially_successful/submission.json'; -import multipleChoiceQuizTemplate from '../../fixtures/exercise/quiz/multiple_choice/template.json'; -import { - courseManagementAPIRequest, - courseManagementExercises, - courseOverview, - exerciseAPIRequest, - modelingExerciseCreation, - modelingExerciseEditor, - programmingExerciseCreation, - programmingExerciseEditor, - quizExerciseCreation, - quizExerciseMultipleChoice, - textExerciseCreation, - textExerciseEditor, -} from '../../support/artemis'; -import { admin, instructor, studentOne } from '../../support/users'; -import { checkField, convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -describe.skip('Import exercises', { scrollBehavior: 'center' }, () => { - let course: Course; - let secondCourse: Course; - let textExercise: TextExercise; - let quizExercise: QuizExercise; - let modelingExercise: ModelingExercise; - let programmingExercise: ProgrammingExercise; - - before('Setup course with exercises', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - exerciseAPIRequest.createTextExercise({ course }).then((response) => { - textExercise = response.body; - }); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate]).then((response) => { - quizExercise = convertModelAfterMultiPart(response); - }); - exerciseAPIRequest.createModelingExercise({ course }).then((response) => { - modelingExercise = response.body; - }); - exerciseAPIRequest.createProgrammingExercise({ course }).then((response) => { - programmingExercise = response.body; - }); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - secondCourse = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(secondCourse, studentOne); - courseManagementAPIRequest.addInstructorToCourse(secondCourse, instructor); - }); - }); - }); - - after('Delete Courses', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - courseManagementAPIRequest.deleteCourse(secondCourse, admin); - }); - - it('Imports text exercise', () => { - cy.login(instructor, `/course-management/${secondCourse.id}/exercises`); - courseManagementExercises.importTextExercise(); - courseManagementExercises.clickImportExercise(textExercise.id!); - - checkField('#field_title', textExercise.title!); - checkField('#field_points', textExercise.maxPoints!); - - textExerciseCreation.setReleaseDate(dayjs()); - textExerciseCreation.setDueDate(dayjs().add(1, 'days')); - textExerciseCreation.setAssessmentDueDate(dayjs().add(2, 'days')); - - textExerciseCreation.import().then((request: Interception) => { - const exercise = request.response!.body; - cy.login(studentOne, `/courses/${secondCourse.id}`); - courseOverview.openExerciseOverview(exercise.title!); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - cy.fixture('loremIpsum-short.txt').then((submission) => { - textExerciseEditor.shouldShowNumberOfWords(0); - textExerciseEditor.shouldShowNumberOfCharacters(0); - textExerciseEditor.typeSubmission(exercise.id!, submission); - textExerciseEditor.shouldShowNumberOfWords(16); - textExerciseEditor.shouldShowNumberOfCharacters(83); - textExerciseEditor.submit().then((request: Interception) => { - expect(request.response!.body.text).to.eq(submission); - expect(request.response!.body.submitted).to.be.true; - expect(request.response!.statusCode).to.eq(200); - }); - }); - }); - }); - - it('Imports quiz exercise', () => { - cy.login(instructor, `/course-management/${secondCourse.id}/exercises`); - courseManagementExercises.importQuizExercise(); - courseManagementExercises.clickImportExercise(quizExercise.id!); - - checkField('#field_title', quizExercise.title!); - checkField('#quiz-duration-minutes', quizExercise.duration! / 60); - - cy.wait(500); - - quizExerciseCreation.setVisibleFrom(dayjs()); - - quizExerciseCreation.import().then((request: Interception) => { - const exercise = request.response!.body; - courseManagementExercises.startQuiz(exercise.id!); - cy.login(studentOne, `/courses/${secondCourse.id}`); - courseOverview.openExerciseOverview(exercise.title!); - courseOverview.startExercise(exercise.id!); - quizExerciseMultipleChoice.tickAnswerOption(exercise.id!, 0); - quizExerciseMultipleChoice.tickAnswerOption(exercise.id!, 2); - quizExerciseMultipleChoice.submit().then((request: Interception) => { - expect(request.response!.body.submitted).to.be.true; - expect(request.response!.statusCode).to.eq(200); - }); - }); - }); - - it('Imports modeling exercise', () => { - cy.login(instructor, `/course-management/${secondCourse.id}/exercises`); - courseManagementExercises.importModelingExercise(); - courseManagementExercises.clickImportExercise(modelingExercise.id!); - - checkField('#field_title', modelingExercise.title!); - checkField('#field_points', modelingExercise.maxPoints!); - - modelingExerciseCreation.setReleaseDate(dayjs()); - modelingExerciseCreation.setDueDate(dayjs().add(1, 'days')); - modelingExerciseCreation.setAssessmentDueDate(dayjs().add(2, 'days')); - - modelingExerciseCreation.import().then((request: Interception) => { - const exercise = request.response!.body; - cy.login(studentOne, `/courses/${secondCourse.id}`); - courseOverview.openExerciseOverview(exercise.title!); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - modelingExerciseEditor.addComponentToModel(exercise.id!, 1); - modelingExerciseEditor.addComponentToModel(exercise.id!, 2); - modelingExerciseEditor.addComponentToModel(exercise.id!, 3); - modelingExerciseEditor.submit().then((request: Interception) => { - expect(request.response!.body.submitted).to.be.true; - expect(request.response!.statusCode).to.eq(200); - }); - }); - }); - - it('Imports programming exercise', () => { - cy.login(instructor, `/course-management/${secondCourse.id}/exercises`); - courseManagementExercises.importProgrammingExercise(); - courseManagementExercises.clickImportExercise(programmingExercise.id!); - - checkField('#field_points', programmingExercise.maxPoints!); - - programmingExerciseCreation.setTitle('Import Test'); - programmingExerciseCreation.setShortName('importtest' + generateUUID()); - programmingExerciseCreation.setDueDate(dayjs().add(3, 'days')); - - programmingExerciseCreation.import().then((request: Interception) => { - const exercise = request.response!.body; - cy.login(studentOne, `/courses/${secondCourse.id}`); - courseOverview.openExerciseOverview(exercise.title!); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, javaPartiallySuccessfulSubmission, () => { - programmingExerciseEditor.getResultScore().contains(javaPartiallySuccessfulSubmission.expectedResult).and('be.visible'); - }); - }); - }); -}); diff --git a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseAssessment.cy.ts b/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseAssessment.cy.ts deleted file mode 100644 index fa19aa4b05f6..000000000000 --- a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseAssessment.cy.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; - -import { - courseAssessment, - courseManagement, - courseManagementAPIRequest, - courseOverview, - exerciseAPIRequest, - exerciseAssessment, - exerciseResult, - fileUploadExerciseAssessment, - fileUploadExerciseEditor, - fileUploadExerciseFeedback, -} from '../../../support/artemis'; -import { admin, instructor, studentOne, tutor } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -// Common primitives -const tutorFeedback = 'Try to use some newlines next time!'; -const tutorFeedbackPoints = 4; -const complaint = "That feedback wasn't very useful!"; - -describe.skip('File upload exercise assessment', () => { - let course: Course; - let exercise: FileUploadExercise; - - before('Creates a file upload exercise and makes a student submission', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - exerciseAPIRequest.createFileUploadExercise({ course }).then((textResponse) => { - exercise = textResponse.body; - cy.login(studentOne, `/courses/${course.id}/exercises`); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - fileUploadExerciseEditor.attachFile('pdf-test-file.pdf'); - fileUploadExerciseEditor.submit(); - }); - }); - }); - - it('Assesses the file upload exercise submission', () => { - cy.login(tutor, '/course-management'); - courseManagement.openAssessmentDashboardOfCourse(course.id!); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - exerciseAssessment.clickStartNewAssessment(); - fileUploadExerciseAssessment.getInstructionsRootElement().contains(exercise.title!).should('be.visible'); - fileUploadExerciseAssessment.getInstructionsRootElement().contains(exercise.problemStatement!).should('be.visible'); - fileUploadExerciseAssessment.getInstructionsRootElement().contains(exercise.exampleSolution!).should('be.visible'); - fileUploadExerciseAssessment.getInstructionsRootElement().contains(exercise.gradingInstructions!).should('be.visible'); - fileUploadExerciseAssessment.addNewFeedback(tutorFeedbackPoints, tutorFeedback); - fileUploadExerciseAssessment.submitFeedback(); - }); - - describe('Feedback', () => { - it('Student sees feedback after assessment due date and complains', () => { - cy.login(studentOne, `/courses/${course.id}/exercises/${exercise.id}`); - const percentage = tutorFeedbackPoints * 10; - exerciseResult.shouldShowExerciseTitle(exercise.title!); - exerciseResult.shouldShowProblemStatement(exercise.problemStatement!); - exerciseResult.shouldShowScore(percentage); - exerciseResult.clickOpenExercise(exercise.id!); - fileUploadExerciseFeedback.shouldShowAdditionalFeedback(tutorFeedbackPoints, tutorFeedback); - fileUploadExerciseFeedback.shouldShowScore(percentage); - fileUploadExerciseFeedback.complain(complaint); - }); - - it('Instructor can see complaint and reject it', () => { - cy.login(instructor, `/course-management/${course.id}/complaints`); - fileUploadExerciseAssessment.acceptComplaint('Makes sense', false).its('response.statusCode').should('eq', 200); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseManagement.cy.ts b/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseManagement.cy.ts deleted file mode 100644 index 199c15c05eb9..000000000000 --- a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseManagement.cy.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; - -import { courseManagement, courseManagementAPIRequest, courseManagementExercises, exerciseAPIRequest, fileUploadExerciseCreation, navigationBar } from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -describe.skip('File upload exercise management', () => { - let course: Course; - - before(() => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Creates a file upload exercise in the UI', { scrollBehavior: 'center' }, () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.createFileUploadExercise(); - - // Fill out file upload exercise form - const exerciseTitle = 'file upload exercise' + generateUUID(); - fileUploadExerciseCreation.typeTitle(exerciseTitle); - fileUploadExerciseCreation.setReleaseDate(dayjs()); - fileUploadExerciseCreation.setDueDate(dayjs().add(1, 'days')); - fileUploadExerciseCreation.setAssessmentDueDate(dayjs().add(2, 'days')); - fileUploadExerciseCreation.typeMaxPoints(10); - const problemStatement = 'This is a problem statement'; - const exampleSolution = 'E = mc^2'; - fileUploadExerciseCreation.typeProblemStatement(problemStatement); - fileUploadExerciseCreation.typeExampleSolution(exampleSolution); - let exercise: FileUploadExercise; - fileUploadExerciseCreation.create().then((request: Interception) => { - exercise = request.response!.body; - }); - - // Make sure file upload exercise is shown in exercises list - cy.visit(`course-management/${course.id}/exercises`).then(() => { - courseManagementExercises.getExercise(exercise.id!).should('be.visible'); - }); - }); - - describe('File upload exercise deletion', () => { - let exercise: FileUploadExercise; - - before(() => { - cy.login(admin, '/'); - exerciseAPIRequest.createFileUploadExercise({ course }).then((response) => { - exercise = response.body; - }); - }); - - it('Deletes an existing file upload exercise', () => { - cy.login(admin, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.deleteFileUploadExercise(exercise); - courseManagementExercises.getExercise(exercise.id!).should('not.exist'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseParticipation.cy.ts b/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseParticipation.cy.ts deleted file mode 100644 index a8c35cc2c7e8..000000000000 --- a/src/test/cypress/e2e/exercises/file-upload/FileUploadExerciseParticipation.cy.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; - -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest, fileUploadExerciseEditor } from '../../../support/artemis'; -import { admin, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('File upload exercise participation', () => { - let course: Course; - let exercise: FileUploadExercise; - - before(() => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - exerciseAPIRequest.createFileUploadExercise({ course }).then((exerciseResponse) => { - exercise = exerciseResponse.body; - }); - }); - }); - - it('Creates a file upload exercise in the UI', () => { - cy.login(studentOne, `/courses/${course.id}/exercises`); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - - // Verify the initial state of the text editor - fileUploadExerciseEditor.shouldShowExerciseTitleInHeader(exercise.title!); - fileUploadExerciseEditor.shouldShowProblemStatement(); - - // Make a submission - fileUploadExerciseEditor.attachFile('pdf-test-file.pdf'); - fileUploadExerciseEditor.submit().then((request: Interception) => { - expect(request.response!.body.submitted).to.be.true; - expect(request.response!.statusCode).to.eq(200); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseAssessment.cy.ts b/src/test/cypress/e2e/exercises/modeling/ModelingExerciseAssessment.cy.ts deleted file mode 100644 index 235cac1ddd9a..000000000000 --- a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseAssessment.cy.ts +++ /dev/null @@ -1,93 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; - -import { - courseAssessment, - courseManagement, - courseManagementAPIRequest, - exerciseAPIRequest, - exerciseAssessment, - exerciseResult, - modelingExerciseAssessment, - modelingExerciseFeedback, -} from '../../../support/artemis'; -import { admin, instructor, studentOne, tutor } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Modeling Exercise Assessment', () => { - let course: Course; - let modelingExercise: ModelingExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - exerciseAPIRequest.createModelingExercise({ course }).then((modelingResponse) => { - modelingExercise = modelingResponse.body; - cy.login(studentOne); - cy.wait(500); - exerciseAPIRequest.startExerciseParticipation(modelingExercise.id!).then((participation) => { - exerciseAPIRequest.makeModelingExerciseSubmission(modelingExercise.id!, participation.body); - cy.login(instructor); - exerciseAPIRequest.updateModelingExerciseDueDate(modelingExercise, dayjs().add(5, 'seconds')); - }); - }); - }); - }); - - it('Tutor can assess a submission', () => { - cy.login(tutor, '/course-management'); - courseManagement.openAssessmentDashboardOfCourse(course.id!); - cy.wait(500); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - exerciseAssessment.clickStartNewAssessment(); - exerciseAssessment.getLockedMessage().should('be.visible'); - modelingExerciseAssessment.addNewFeedback(1, 'Thanks, good job.'); - modelingExerciseAssessment.openAssessmentForComponent(1); - modelingExerciseAssessment.assessComponent(-1, 'False'); - modelingExerciseAssessment.clickNextAssessment(); - modelingExerciseAssessment.assessComponent(2, 'Good'); - modelingExerciseAssessment.clickNextAssessment(); - modelingExerciseAssessment.assessComponent(0, 'Unnecessary'); - modelingExerciseAssessment.submit(); - }); - - describe('Handling complaints', () => { - before(() => { - cy.login(admin); - exerciseAPIRequest - .updateModelingExerciseAssessmentDueDate(modelingExercise, dayjs()) - .its('body') - .then((exercise) => { - modelingExercise = exercise; - }); - }); - - it('Student can view the assessment and complain', () => { - cy.login(studentOne, `/courses/${course.id}/exercises/${modelingExercise.id}`); - exerciseResult.shouldShowExerciseTitle(modelingExercise.title!); - exerciseResult.shouldShowScore(20); - exerciseResult.clickOpenExercise(modelingExercise.id!); - modelingExerciseFeedback.shouldShowScore(20); - modelingExerciseFeedback.shouldShowAdditionalFeedback(1, 'Thanks, good job.'); - modelingExerciseFeedback.shouldShowComponentFeedback(1, 2, 'Good'); - modelingExerciseFeedback.complain('I am not happy with your assessment.'); - }); - - it('Instructor can see complaint and reject it', () => { - cy.login(instructor, `/course-management/${course.id}/complaints`); - courseAssessment.showTheComplaint(); - modelingExerciseAssessment.rejectComplaint('You are wrong.', false); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseManagement.cy.ts b/src/test/cypress/e2e/exercises/modeling/ModelingExerciseManagement.cy.ts deleted file mode 100644 index e453c5bbc4fb..000000000000 --- a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseManagement.cy.ts +++ /dev/null @@ -1,155 +0,0 @@ -import dayjs from 'dayjs/esm'; -import { MODELING_EDITOR_CANVAS } from 'src/test/cypress/support/constants'; - -import { Course } from 'app/entities/course.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; - -import { - courseManagement, - courseManagementAPIRequest, - courseManagementExercises, - courseOverview, - exerciseAPIRequest, - modelingExerciseAssessment, - modelingExerciseCreation, - modelingExerciseEditor, - navigationBar, -} from '../../../support/artemis'; -import { admin, instructor, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -describe.skip('Modeling Exercise Management', () => { - let course: Course; - let modelingExercise: ModelingExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response: Cypress.Response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - }); - }); - - describe('Create Modeling Exercise', () => { - it('Create a new modeling exercise', { scrollBehavior: 'center' }, () => { - cy.login(instructor); - cy.visit(`/course-management/${course.id}/exercises`); - courseManagementExercises.createModelingExercise(); - modelingExerciseCreation.setTitle('Modeling ' + generateUUID()); - modelingExerciseCreation.addCategories(['e2e-testing', 'test2']); - modelingExerciseCreation.setPoints(10); - modelingExerciseCreation - .save() - .its('response.body') - .then((body: any) => { - modelingExercise = body; - cy.contains(modelingExercise.title!).should('exist'); - cy.log('Create Example Solution'); - cy.visit(`/course-management/${course.id}/modeling-exercises/${modelingExercise.id}/edit`); - modelingExerciseEditor.addComponentToExampleSolutionModel(1); - modelingExerciseCreation.save(); - cy.get(MODELING_EDITOR_CANVAS).children().eq(0).should('exist'); - - cy.log('Create Example Submission'); - cy.visit(`/course-management/${course.id}/modeling-exercises/${modelingExercise.id}/example-submissions`); - modelingExerciseEditor.clickCreateExampleSubmission(); - modelingExerciseEditor.addComponentToExampleSolutionModel(1); - modelingExerciseEditor.addComponentToExampleSolutionModel(2); - modelingExerciseEditor.addComponentToExampleSolutionModel(3); - modelingExerciseEditor.clickCreateNewExampleSubmission(); - modelingExerciseEditor.showExampleAssessment(); - modelingExerciseAssessment.openAssessmentForComponent(1); - modelingExerciseAssessment.assessComponent(-1, 'False'); - modelingExerciseAssessment.clickNextAssessment(); - modelingExerciseAssessment.assessComponent(2, 'Good'); - modelingExerciseAssessment.clickNextAssessment(); - modelingExerciseAssessment.assessComponent(0, 'Unnecessary'); - modelingExerciseAssessment.submitExample(); - cy.visit(`/course-management/${course.id}/modeling-exercises/${modelingExercise.id}`); - modelingExerciseEditor.getModelingCanvas().should('exist'); - }); - }); - - after('Delete modeling exercise', () => { - if (modelingExercise) { - cy.login(admin); - exerciseAPIRequest.deleteModelingExercise(modelingExercise.id!); - } - }); - }); - - describe('Edit Modeling Exercise', () => { - before('Create Modeling Exercise', () => { - cy.login(admin); - exerciseAPIRequest.createModelingExercise({ course }).then((resp) => { - modelingExercise = resp.body; - }); - }); - - it('Edit Existing Modeling Exercise', { scrollBehavior: 'center' }, () => { - cy.visit(`/course-management/${course.id}/modeling-exercises/${modelingExercise.id}/edit`); - const newTitle = 'New Modeling Exercise Title'; - const points = 100; - modelingExerciseCreation.setTitle(newTitle); - modelingExerciseCreation.pickDifficulty({ hard: true }); - modelingExerciseCreation.setReleaseDate(dayjs().add(1, 'day')); - modelingExerciseCreation.setDueDate(dayjs().add(2, 'day')); - modelingExerciseCreation.setAssessmentDueDate(dayjs().add(3, 'day')); - modelingExerciseCreation.includeInOverallScore(); - modelingExerciseCreation.setPoints(points); - modelingExerciseCreation.save(); - cy.visit(`/course-management/${course.id}/exercises`); - courseManagementExercises.getModelingExerciseTitle(modelingExercise.id!).contains(newTitle); - courseManagementExercises.getModelingExerciseMaxPoints(modelingExercise.id!).contains(points.toString()); - }); - - after('Delete exercise', () => { - exerciseAPIRequest.deleteModelingExercise(modelingExercise.id!); - }); - }); - - describe('Delete Modeling Exercise', () => { - let modelingExercise: ModelingExercise; - - before('Create Modeling exercise', () => { - cy.login(admin, '/'); - exerciseAPIRequest.createModelingExercise({ course }).then((resp) => { - modelingExercise = resp.body; - }); - }); - - it('Deletes an existing Modeling exercise', () => { - cy.login(instructor, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.deleteModelingExercise(modelingExercise); - courseManagementExercises.getExercise(modelingExercise.id!).should('not.exist'); - }); - }); - - describe('Modeling Exercise Release', () => { - it('Student can not see unreleased Modeling Exercise', () => { - cy.login(instructor); - exerciseAPIRequest.createModelingExercise({ course }, 'Modeling ' + generateUUID(), dayjs().add(1, 'hour')).then((resp) => { - modelingExercise = resp.body; - }); - cy.login(studentOne, '/courses'); - cy.contains(course.title!).click({ force: true }); - courseOverview.getExercises().should('have.length', 0); - }); - - it('Student can see released Modeling Exercise', () => { - cy.login(instructor); - exerciseAPIRequest.createModelingExercise({ course }, 'Modeling ' + generateUUID(), dayjs().subtract(1, 'hour')).then((resp) => { - modelingExercise = resp.body; - }); - cy.login(studentOne, '/courses'); - cy.visit('/courses/' + course.id + '/exercises/' + modelingExercise.id); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseParticipation.cy.ts b/src/test/cypress/e2e/exercises/modeling/ModelingExerciseParticipation.cy.ts deleted file mode 100644 index dcbf04bc2918..000000000000 --- a/src/test/cypress/e2e/exercises/modeling/ModelingExerciseParticipation.cy.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; - -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest, modelingExerciseEditor } from '../../../support/artemis'; -import { admin, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Modeling Exercise Participation', () => { - let course: Course; - let modelingExercise: ModelingExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response: Cypress.Response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - exerciseAPIRequest.createModelingExercise({ course }).then((resp: Cypress.Response) => { - modelingExercise = resp.body; - }); - }); - }); - - it('Student can start and submit their model', () => { - cy.login(studentOne, `/courses/${course.id}`); - courseOverview.startExercise(modelingExercise.id!); - courseOverview.openRunningExercise(modelingExercise.id!); - modelingExerciseEditor.addComponentToModel(modelingExercise.id!, 1, false, 310, 320); - modelingExerciseEditor.addComponentToModel(modelingExercise.id!, 2, false, 730, 500); - modelingExerciseEditor.addComponentToModel(modelingExercise.id!, 3, false, 1000, 100); - modelingExerciseEditor.submit(); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseAssessment.cy.ts b/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseAssessment.cy.ts deleted file mode 100644 index c8ba45de348e..000000000000 --- a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseAssessment.cy.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; - -import { - courseAssessment, - courseManagement, - courseManagementAPIRequest, - exerciseAPIRequest, - exerciseAssessment, - exerciseResult, - programmingExerciseAssessment, - programmingExerciseEditor, - programmingExerciseFeedback, -} from '../../../support/artemis'; -import { ProgrammingExerciseAssessmentType } from '../../../support/constants'; -import { admin, instructor, studentOne, tutor } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -// Common primitives -const tutorFeedback = 'You are missing some classes! The classes, which you implemented look good though.'; -const tutorFeedbackPoints = 5; -const tutorCodeFeedback = 'The input parameter should be mentioned in javadoc!'; -const tutorCodeFeedbackPoints = -2; -const complaint = "That feedback wasn't very useful!"; - -describe.skip('Programming exercise assessment', () => { - let course: Course; - let exercise: ProgrammingExercise; - let dueDate: dayjs.Dayjs; - let assessmentDueDate: dayjs.Dayjs; - - before('Creates a programming exercise and makes a student submission', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - dueDate = dayjs().add(25, 'seconds'); - assessmentDueDate = dueDate.add(30, 'seconds'); - exerciseAPIRequest - .createProgrammingExercise({ - course, - recordTestwiseCoverage: false, - releaseDate: dayjs(), - dueDate: dueDate, - assessmentDate: assessmentDueDate, - assessmentType: ProgrammingExerciseAssessmentType.SEMI_AUTOMATIC, - }) - .then((programmingResponse) => { - exercise = programmingResponse.body; - cy.login(studentOne); - exerciseAPIRequest - .startExerciseParticipation(exercise.id!) - .its('body.id') - .then((participationId) => { - exerciseAPIRequest.makeProgrammingExerciseSubmission(participationId); - // Wait until the due date is in the past - const now = dayjs(); - if (now.isBefore(dueDate)) { - cy.wait(dueDate.diff(now, 'ms')); - } - }); - }); - }); - }); - - /* - * Skipped because the hovering behavior required to manually assess a submission is not supported by Cypress. - * Please refer to the playwright E2E test. - */ - it.skip('Assesses the programming exercise submission and verifies it', () => { - // Asses submission - cy.login(tutor, '/course-management'); - courseManagement.openAssessmentDashboardOfCourse(course.id!); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - exerciseAssessment.clickStartNewAssessment(); - programmingExerciseEditor.openFileWithName(exercise.id!, 'BubbleSort.java'); - programmingExerciseAssessment.provideFeedbackOnCodeLine(9, tutorCodeFeedbackPoints, tutorCodeFeedback); - programmingExerciseAssessment.addNewFeedback(tutorFeedbackPoints, tutorFeedback); - programmingExerciseAssessment.submit().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - // Wait until the assessment due date is over - const now = dayjs(); - if (now.isBefore(assessmentDueDate)) { - cy.wait(assessmentDueDate.diff(now, 'ms')); - } - }); - - // Verify assessment as student - cy.login(studentOne, `/courses/${course.id}/exercises/${exercise.id}`); - const totalPoints = tutorFeedbackPoints + tutorCodeFeedbackPoints; - const percentage = totalPoints * 10; - exerciseResult.shouldShowExerciseTitle(exercise.title!); - programmingExerciseFeedback.complain(complaint); - exerciseResult.clickOpenCodeEditor(exercise.id!); - programmingExerciseFeedback.shouldShowRepositoryLockedWarning(); - programmingExerciseFeedback.shouldShowAdditionalFeedback(tutorFeedbackPoints, tutorFeedback); - programmingExerciseFeedback.shouldShowScore(percentage); - programmingExerciseFeedback.shouldShowCodeFeedback(exercise.id!, 'BubbleSort.java', tutorCodeFeedback, '-2', programmingExerciseEditor); - - // Accept complaint - cy.login(instructor, `/course-management/${course.id}/complaints`); - programmingExerciseAssessment.acceptComplaint('Makes sense', false).then((request: Interception) => { - expect(request.response!.statusCode).to.equal(200); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseManagement.cy.ts b/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseManagement.cy.ts deleted file mode 100644 index e775d2174b3c..000000000000 --- a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseManagement.cy.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; - -import { courseManagement, courseManagementAPIRequest, courseManagementExercises, exerciseAPIRequest, navigationBar, programmingExerciseCreation } from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -describe.skip('Programming Exercise Management', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - describe('Programming exercise creation', () => { - it('Creates a new programming exercise', { scrollBehavior: 'center' }, () => { - cy.login(admin, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.createProgrammingExercise(); - cy.url().should('include', '/programming-exercises/new'); - cy.log('Filling out programming exercise info...'); - const exerciseTitle = 'Programming exercise ' + generateUUID(); - programmingExerciseCreation.setTitle(exerciseTitle); - programmingExerciseCreation.setShortName('programming' + generateUUID()); - programmingExerciseCreation.setPackageName('de.test'); - programmingExerciseCreation.setPoints(100); - programmingExerciseCreation.checkAllowOnlineEditor(); - programmingExerciseCreation.generate().then((request: Interception) => { - const exercise = request.response!.body; - courseManagementExercises.getExerciseTitle().should('contain.text', exerciseTitle); - cy.url().should('include', `/programming-exercises/${exercise.id}`); - }); - }); - }); - - describe('Programming exercise deletion', () => { - let exercise: ProgrammingExercise; - - before(() => { - cy.login(admin, '/'); - exerciseAPIRequest.createProgrammingExercise({ course }).then((response) => { - exercise = response.body; - }); - }); - - it('Deletes an existing programming exercise', () => { - cy.login(admin, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.deleteProgrammingExercise(exercise); - courseManagementExercises.getExercise(exercise.id!).should('not.exist'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseParticipation.cy.ts b/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseParticipation.cy.ts deleted file mode 100644 index 7f915290e41d..000000000000 --- a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseParticipation.cy.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; - -import cAllSuccessful from '../../../fixtures/exercise/programming/c/all_successful/submission.json'; -import javaAllSuccessfulSubmission from '../../../fixtures/exercise/programming/java/all_successful/submission.json'; -import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; -import javaPartiallySuccessfulSubmission from '../../../fixtures/exercise/programming/java/partially_successful/submission.json'; -import pythonAllSuccessful from '../../../fixtures/exercise/programming/python/all_successful/submission.json'; -import { courseManagementAPIRequest, exerciseAPIRequest, programmingExerciseEditor } from '../../../support/artemis'; -import { ProgrammingLanguage } from '../../../support/constants'; -import { admin, studentOne, studentThree, studentTwo } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Programming exercise participation', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin, '/'); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addStudentToCourse(course, studentTwo); - courseManagementAPIRequest.addStudentToCourse(course, studentThree); - }); - }); - - describe('Java programming exercise', () => { - let exercise: ProgrammingExercise; - - before('Setup java programming exercise', () => { - cy.login(admin); - exerciseAPIRequest.createProgrammingExercise({ course, programmingLanguage: ProgrammingLanguage.JAVA }).then((exerciseResponse) => { - exercise = exerciseResponse.body; - }); - }); - - it('Makes a failing submission', () => { - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentOne); - const submission = javaBuildErrorSubmission; - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, submission, () => { - programmingExerciseEditor.getResultScore().contains(submission.expectedResult).and('be.visible'); - }); - }); - - it('Makes a partially successful submission', () => { - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentTwo); - const submission = javaPartiallySuccessfulSubmission; - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, submission, () => { - programmingExerciseEditor.getResultScore().contains(submission.expectedResult).and('be.visible'); - }); - }); - - it('Makes a successful submission', () => { - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentThree); - const submission = javaAllSuccessfulSubmission; - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, submission, () => { - programmingExerciseEditor.getResultScore().contains(submission.expectedResult).and('be.visible'); - }); - }); - }); - - // Skip C tests within Jenkins used by the Postgres setup, since C is currently not supported there - // See https://github.com/ls1intum/Artemis/issues/6994 - if (Cypress.env('DB_TYPE') !== 'Postgres') { - describe('C programming exercise', () => { - let exercise: ProgrammingExercise; - - before('Setup c programming exercise', () => { - cy.login(admin); - exerciseAPIRequest.createProgrammingExercise({ course, programmingLanguage: ProgrammingLanguage.C }).then((exerciseResponse) => { - exercise = exerciseResponse.body; - }); - }); - - it('Makes a submission', () => { - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentOne); - const submission = cAllSuccessful; - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, submission, () => { - programmingExerciseEditor.getResultScore().contains(submission.expectedResult).and('be.visible'); - }); - }); - }); - } - - describe('Python programming exercise', () => { - let exercise: ProgrammingExercise; - - before('Setup python programming exercise', () => { - cy.login(admin); - exerciseAPIRequest.createProgrammingExercise({ course, programmingLanguage: ProgrammingLanguage.PYTHON }).then((exerciseResponse) => { - exercise = exerciseResponse.body; - }); - }); - - it('Makes a submission', () => { - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentOne); - const submission = pythonAllSuccessful; - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, submission, () => { - programmingExerciseEditor.getResultScore().contains(submission.expectedResult).and('be.visible'); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseStaticCodeAnalysis.cy.ts b/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseStaticCodeAnalysis.cy.ts deleted file mode 100644 index 9f06db6704d8..000000000000 --- a/src/test/cypress/e2e/exercises/programming/ProgrammingExerciseStaticCodeAnalysis.cy.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; - -import javaScaSubmission from '../../../fixtures/exercise/programming/java/static_code_analysis/submission.json'; -import { courseManagementAPIRequest, exerciseAPIRequest, programmingExerciseEditor, programmingExerciseScaFeedback, programmingExercisesScaConfig } from '../../../support/artemis'; -import { admin, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Static code analysis tests', () => { - let course: Course; - let exercise: ProgrammingExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse({ customizeGroups: true }).then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - exerciseAPIRequest.createProgrammingExercise({ course, scaMaxPenalty: 50 }).then((exerciseResponse) => { - exercise = exerciseResponse.body; - }); - }); - }); - - it('Configures SCA grading and makes a successful submission with SCA errors', () => { - // Configure SCA grading - cy.login(admin); - programmingExercisesScaConfig.visit(course.id!, exercise.id!); - programmingExercisesScaConfig.makeEveryScaCategoryInfluenceGrading(); - programmingExercisesScaConfig.saveChanges(); - - // Make submission with SCA errors - programmingExerciseEditor.startParticipation(course.id!, exercise.id!, studentOne); - programmingExerciseEditor.makeSubmissionAndVerifyResults(exercise.id!, javaScaSubmission, () => { - programmingExerciseEditor.getResultScore().contains(javaScaSubmission.expectedResult).and('be.visible').click(); - programmingExerciseScaFeedback.shouldShowPointChart(); - // We have to verify those static texts here. If we don't verify those messages the only difference between the SCA and normal programming exercise - // tests is the score, which hardly verifies the SCA functionality - programmingExerciseScaFeedback.shouldShowCodeIssue("Variable 'literal1' must be private and have accessor methods.", '5'); - programmingExerciseScaFeedback.shouldShowCodeIssue("Avoid unused private fields such as 'LITERAL_TWO'.", '0.5'); - programmingExerciseScaFeedback.shouldShowCodeIssue("de.test.BubbleSort.literal1 isn't final but should be", '2.5'); - programmingExerciseScaFeedback.shouldShowCodeIssue('Unread public/protected field: de.test.BubbleSort.literal1', '0.2'); - programmingExerciseScaFeedback.closeModal(); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseAssessment.cy.ts b/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseAssessment.cy.ts deleted file mode 100644 index 2a76b25de58c..000000000000 --- a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseAssessment.cy.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import multipleChoiceQuizTemplate from '../../../fixtures/exercise/quiz/multiple_choice/template.json'; -import shortAnswerQuizTemplate from '../../../fixtures/exercise/quiz/short_answer/template.json'; -import { courseManagementAPIRequest, exerciseAPIRequest, exerciseResult } from '../../../support/artemis'; -import { admin, studentOne, tutor } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Quiz Exercise Assessment', () => { - let course: Course; - let quizExercise: QuizExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - }); - }); - - describe( - 'MC Quiz assessment', - { - retries: 2, - }, - () => { - it('Assesses a mc quiz submission automatically', () => { - cy.login(admin); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate], undefined, undefined, 10).then((quizResponse) => { - quizExercise = convertModelAfterMultiPart(quizResponse); - exerciseAPIRequest.setQuizVisible(quizExercise.id!); - exerciseAPIRequest.startQuizNow(quizExercise.id!); - cy.login(studentOne); - exerciseAPIRequest.startExerciseParticipation(quizExercise.id!); - exerciseAPIRequest.createMultipleChoiceSubmission(quizExercise, [0, 2]); - cy.visit('/courses/' + course.id + '/exercises/' + quizExercise.id); - exerciseResult.shouldShowScore(50); - }); - }); - }, - ); - - describe( - 'SA Quiz assessment', - { - retries: 2, - }, - () => { - it('Assesses a sa quiz submission automatically', () => { - cy.login(admin); - exerciseAPIRequest.createQuizExercise({ course }, [shortAnswerQuizTemplate], undefined, undefined, 10).then((quizResponse) => { - quizExercise = convertModelAfterMultiPart(quizResponse); - exerciseAPIRequest.setQuizVisible(quizExercise.id!); - exerciseAPIRequest.startQuizNow(quizExercise.id!); - cy.login(studentOne); - exerciseAPIRequest.startExerciseParticipation(quizExercise.id!); - exerciseAPIRequest.createShortAnswerSubmission(quizExercise, ['give', 'let', 'run', 'desert']); - cy.visit('/courses/' + course.id + '/exercises/' + quizExercise.id); - exerciseResult.shouldShowScore(66.7); - }); - }); - }, - ); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseDropLocation.cy.ts b/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseDropLocation.cy.ts deleted file mode 100644 index f56ba79fc8f3..000000000000 --- a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseDropLocation.cy.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Course } from 'app/entities/course.model'; - -import { courseManagementAPIRequest, courseManagementExercises, quizExerciseCreation, quizExerciseDragAndDropQuiz } from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -let course: Course; - -describe.skip('Quiz Exercise Drop Location Spec', () => { - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - describe('DnD Quiz drop locations', () => { - before('Create DND quiz', () => { - cy.login(admin, '/course-management/' + course.id + '/exercises'); - courseManagementExercises.createQuizExercise(); - quizExerciseCreation.setTitle('Quiz Exercise ' + generateUUID()); - quizExerciseDragAndDropQuiz.createDnDQuiz('DnD Quiz Test'); - }); - - it('Checks drop locations', () => { - let containerBounds: DOMRect; - - quizExerciseDragAndDropQuiz.dragUsingCoordinates(310, 320); - quizExerciseDragAndDropQuiz.dragUsingCoordinates(730, 500); - quizExerciseDragAndDropQuiz.dragUsingCoordinates(730, 320); - - quizExerciseDragAndDropQuiz.activateInteractiveMode(); - - quizExerciseDragAndDropQuiz.markElementAsInteractive(0, 4); - quizExerciseDragAndDropQuiz.markElementAsInteractive(1, 3); - quizExerciseDragAndDropQuiz.markElementAsInteractive(2, 3); - quizExerciseDragAndDropQuiz.markElementAsInteractive(2, 4); - - quizExerciseDragAndDropQuiz.generateQuizExercise(); - quizExerciseDragAndDropQuiz.waitForQuizExerciseToBeGenerated(); - - cy.visit(`/course-management/${course.id}/exercises`); - quizExerciseDragAndDropQuiz.previewQuiz(); - quizExerciseDragAndDropQuiz.waitForQuizPreviewToLoad(); - - cy.get('.click-layer').then(($el) => { - containerBounds = $el[0].getBoundingClientRect(); - }); - - cy.get('.drop-location').then(($els) => { - const { minX, maxX } = quizExerciseDragAndDropQuiz.getXAxis($els); - expect(containerBounds.right - maxX).to.be.greaterThan(0); - expect(minX - containerBounds.left).to.be.greaterThan(0); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseManagement.cy.ts b/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseManagement.cy.ts deleted file mode 100644 index b26cec263fac..000000000000 --- a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseManagement.cy.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import { Course } from 'app/entities/course.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import multipleChoiceTemplate from '../../../fixtures/exercise/quiz/multiple_choice/template.json'; -import { courseManagement, courseManagementAPIRequest, courseManagementExercises, exerciseAPIRequest, navigationBar, quizExerciseCreation } from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -describe.skip('Quiz Exercise Management', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - describe('Quiz Exercise Creation', () => { - beforeEach(() => { - cy.login(admin, '/course-management/'); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.createQuizExercise(); - quizExerciseCreation.setTitle('Quiz Exercise ' + generateUUID()); - }); - - it('Creates a Quiz with Multiple Choice', () => { - const title = 'Multiple Choice Quiz'; - quizExerciseCreation.addMultipleChoiceQuestion(title); - quizExerciseCreation.saveQuiz().then((quizResponse: Interception) => { - cy.visit('/course-management/' + course.id + '/quiz-exercises/' + quizResponse.response!.body.id + '/preview'); - cy.contains(title).should('be.visible'); - }); - }); - - it('Creates a Quiz with Short Answer', () => { - const title = 'Short Answer Quiz'; - quizExerciseCreation.addShortAnswerQuestion(title); - quizExerciseCreation.saveQuiz().then((quizResponse: Interception) => { - cy.visit('/course-management/' + course.id + '/quiz-exercises/' + quizResponse.response!.body.id + '/preview'); - cy.contains(title).should('be.visible'); - }); - }); - - // TODO: Fix the drag and drop - // it.skip('Creates a Quiz with Drag and Drop', () => { - // quizExerciseCreation.addDragAndDropQuestion(quizQuestionTitle); - // quizExerciseCreation.saveQuiz().then((quizResponse: Interception) => { - // cy.visit('/course-management/' + course.id + '/quiz-exercises/' + quizResponse.response!.body.id + '/preview'); - // cy.contains(quizQuestionTitle).should('be.visible'); - // }); - // }); - }); - - describe('Quiz Exercise deletion', () => { - let quizExercise: QuizExercise; - - before('Create quiz Exercise', () => { - cy.login(admin); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceTemplate]).then((quizResponse) => { - quizExercise = convertModelAfterMultiPart(quizResponse); - }); - }); - - it('Deletes a quiz exercise', () => { - cy.login(admin, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.deleteQuizExercise(quizExercise); - courseManagementExercises.getExercise(quizExercise.id!).should('not.exist'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseParticipation.cy.ts b/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseParticipation.cy.ts deleted file mode 100644 index ce5023b500cc..000000000000 --- a/src/test/cypress/e2e/exercises/quiz-exercise/QuizExerciseParticipation.cy.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import multipleChoiceQuizTemplate from '../../../fixtures/exercise/quiz/multiple_choice/template.json'; -import shortAnswerQuizTemplate from '../../../fixtures/exercise/quiz/short_answer/template.json'; -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest, quizExerciseMultipleChoice, quizExerciseShortAnswerQuiz } from '../../../support/artemis'; -import { admin, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Quiz Exercise Participation', () => { - let course: Course; - let quizExercise: QuizExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - }); - }); - - describe('Quiz exercise participation', () => { - beforeEach('Create quiz exercise', () => { - cy.login(admin); - exerciseAPIRequest.createQuizExercise({ course }, [multipleChoiceQuizTemplate]).then((quizResponse) => { - quizExercise = convertModelAfterMultiPart(quizResponse); - }); - }); - - it('Student cannot see hidden quiz', () => { - cy.login(studentOne, '/courses/' + course.id); - courseOverview.getExercises().should('have.length', 0); - }); - - it('Student can see a visible quiz', () => { - cy.login(admin); - exerciseAPIRequest.setQuizVisible(quizExercise.id!); - cy.login(studentOne, '/courses/' + course.id); - courseOverview.openRunningExercise(quizExercise.id!); - }); - - it('Student can participate in MC quiz', () => { - cy.login(admin); - exerciseAPIRequest.setQuizVisible(quizExercise.id!); - exerciseAPIRequest.startQuizNow(quizExercise.id!); - cy.login(studentOne, '/courses/' + course.id); - courseOverview.startExercise(quizExercise.id!); - quizExerciseMultipleChoice.tickAnswerOption(quizExercise.id!, 0); - quizExerciseMultipleChoice.tickAnswerOption(quizExercise.id!, 2); - quizExerciseMultipleChoice.submit(); - }); - }); - - describe('SA quiz participation', () => { - before('Create SA quiz', () => { - cy.login(admin); - exerciseAPIRequest.createQuizExercise({ course }, [shortAnswerQuizTemplate]).then((quizResponse) => { - quizExercise = convertModelAfterMultiPart(quizResponse); - exerciseAPIRequest.setQuizVisible(quizExercise.id!); - exerciseAPIRequest.startQuizNow(quizExercise.id!); - }); - }); - - it('Student can participate in SA quiz', () => { - const quizQuestionId = quizExercise.quizQuestions![0].id!; - cy.login(studentOne, '/courses/' + course.id); - courseOverview.startExercise(quizExercise.id!); - quizExerciseShortAnswerQuiz.typeAnswer(0, 1, quizQuestionId, 'give'); - quizExerciseShortAnswerQuiz.typeAnswer(1, 1, quizQuestionId, 'let'); - quizExerciseShortAnswerQuiz.typeAnswer(2, 1, quizQuestionId, 'run'); - quizExerciseShortAnswerQuiz.typeAnswer(2, 3, quizQuestionId, 'desert'); - quizExerciseShortAnswerQuiz.typeAnswer(3, 1, quizQuestionId, 'cry'); - quizExerciseShortAnswerQuiz.typeAnswer(4, 1, quizQuestionId, 'goodbye'); - quizExerciseShortAnswerQuiz.submit(); - }); - }); - - // TODO: Fix the drag and drop - // describe.skip('DnD Quiz participation', () => { - // before('Create DND quiz', () => { - // cy.login(admin, '/course-management/' + course.id + '/exercises'); - // courseManagementExercises.createQuizExercise(); - // quizExerciseCreation.setTitle('Cypress Quiz'); - // quizExerciseCreation.addDragAndDropQuestion('DnD Quiz'); - // quizExerciseCreation.saveQuiz().then((quizResponse) => { - // quizExercise = quizResponse.response?.body; - // courseManagementAPIRequest.setQuizVisible(quizExercise.id!); - // courseManagementAPIRequest.startQuizNow(quizExercise.id!); - // }); - // }); - - // it('Student can participate in DnD Quiz', () => { - // cy.login(studentOne, '/courses/' + course.id); - // courseOverview.startExercise(quizExercise.id!); - // quizExerciseDragAndDropQuiz.dragItemIntoDragArea(0); - // quizExerciseDragAndDropQuiz.submit(); - // }); - // }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/text/TextExerciseAssessment.cy.ts b/src/test/cypress/e2e/exercises/text/TextExerciseAssessment.cy.ts deleted file mode 100644 index 10c74b853808..000000000000 --- a/src/test/cypress/e2e/exercises/text/TextExerciseAssessment.cy.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; - -import { - courseAssessment, - courseManagement, - courseManagementAPIRequest, - exerciseAPIRequest, - exerciseAssessment, - exerciseResult, - textExerciseAssessment, - textExerciseFeedback, -} from '../../../support/artemis'; -import { admin, instructor, studentOne, tutor } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -// Common primitives -const tutorFeedback = 'Try to use some newlines next time!'; -const tutorFeedbackPoints = 4; -const tutorTextFeedback = 'Nice ending of the sentence!'; -const tutorTextFeedbackPoints = 2; -const complaint = "That feedback wasn't very useful!"; - -describe.skip('Text exercise assessment', () => { - let course: Course; - let exercise: TextExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - courseManagementAPIRequest.addTutorToCourse(course, tutor); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - exerciseAPIRequest.createTextExercise({ course }).then((textResponse) => { - exercise = textResponse.body; - cy.login(studentOne); - exerciseAPIRequest.startExerciseParticipation(exercise.id!); - cy.fixture('loremIpsum-short.txt').then((submission) => { - exerciseAPIRequest.makeTextExerciseSubmission(exercise.id!, submission); - }); - }); - }); - }); - - it('Assesses the text exercise submission', () => { - cy.login(tutor, '/course-management'); - courseManagement.openAssessmentDashboardOfCourse(course.id!); - courseAssessment.clickExerciseDashboardButton(); - exerciseAssessment.clickHaveReadInstructionsButton(); - exerciseAssessment.clickStartNewAssessment(); - textExerciseAssessment.getInstructionsRootElement().contains(exercise.title!).should('be.visible'); - textExerciseAssessment.getInstructionsRootElement().contains(exercise.problemStatement!).should('be.visible'); - textExerciseAssessment.getInstructionsRootElement().contains(exercise.exampleSolution!).should('be.visible'); - textExerciseAssessment.getInstructionsRootElement().contains(exercise.gradingInstructions!).should('be.visible'); - // Assert the correct word and character count without relying on translations - textExerciseAssessment.getWordCountElement().should('contain.text', 16).and('be.visible'); - textExerciseAssessment.getCharacterCountElement().should('contain.text', 83).and('be.visible'); - textExerciseAssessment.provideFeedbackOnTextSection(1, tutorTextFeedbackPoints, tutorTextFeedback); - textExerciseAssessment.addNewFeedback(tutorFeedbackPoints, tutorFeedback); - textExerciseAssessment.submit().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - }); - }); - - describe('Feedback', () => { - it('Student sees feedback after assessment due date and complains', () => { - cy.login(studentOne, `/courses/${course.id}/exercises/${exercise.id}`); - const totalPoints = tutorFeedbackPoints + tutorTextFeedbackPoints; - const percentage = totalPoints * 10; - exerciseResult.shouldShowExerciseTitle(exercise.title!); - exerciseResult.shouldShowProblemStatement(exercise.problemStatement!); - exerciseResult.shouldShowScore(percentage); - exerciseResult.clickOpenExercise(exercise.id!); - textExerciseFeedback.shouldShowTextFeedback(1, tutorTextFeedback); - textExerciseFeedback.shouldShowAdditionalFeedback(tutorFeedbackPoints, tutorFeedback); - textExerciseFeedback.shouldShowScore(percentage); - textExerciseFeedback.complain(complaint); - }); - - it('Instructor can see complaint and reject it', () => { - cy.login(instructor, `/course-management/${course.id}/complaints`); - textExerciseAssessment.acceptComplaint('Makes sense', false).its('response.statusCode').should('eq', 200); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/text/TextExerciseManagement.cy.ts b/src/test/cypress/e2e/exercises/text/TextExerciseManagement.cy.ts deleted file mode 100644 index 773136e8b727..000000000000 --- a/src/test/cypress/e2e/exercises/text/TextExerciseManagement.cy.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; - -import { - courseManagement, - courseManagementAPIRequest, - courseManagementExercises, - exerciseAPIRequest, - navigationBar, - textExerciseCreation, - textExerciseExampleSubmissionCreation, - textExerciseExampleSubmissions, -} from '../../../support/artemis'; -import { admin } from '../../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../../support/utils'; - -describe.skip('Text exercise management', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - }); - }); - - it('Creates a text exercise in the UI', { scrollBehavior: 'center' }, () => { - cy.visit('/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.createTextExercise(); - - // Fill out text exercise form - const exerciseTitle = 'text exercise' + generateUUID(); - textExerciseCreation.typeTitle(exerciseTitle); - textExerciseCreation.setReleaseDate(dayjs()); - textExerciseCreation.setDueDate(dayjs().add(1, 'days')); - textExerciseCreation.setAssessmentDueDate(dayjs().add(2, 'days')); - textExerciseCreation.typeMaxPoints(10); - const problemStatement = 'This is a problem statement'; - const exampleSolution = 'E = mc^2'; - textExerciseCreation.typeProblemStatement(problemStatement); - textExerciseCreation.typeExampleSolution(exampleSolution); - let exercise: TextExercise; - textExerciseCreation.create().then((request: Interception) => { - exercise = request.response!.body; - }); - - // Create an example submission - courseManagementExercises.clickExampleSubmissionsButton(); - textExerciseExampleSubmissions.clickCreateExampleSubmission(); - textExerciseExampleSubmissionCreation.showsExerciseTitle(exerciseTitle); - textExerciseExampleSubmissionCreation.showsProblemStatement(problemStatement); - textExerciseExampleSubmissionCreation.showsExampleSolution(exampleSolution); - const submission = 'This is an\nexample\nsubmission'; - textExerciseExampleSubmissionCreation.typeExampleSubmission(submission); - textExerciseExampleSubmissionCreation.clickCreateNewExampleSubmission().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - expect(request.response!.body.submission.text).to.equal(submission); - }); - - // Make sure text exercise is shown in exercises list - cy.visit(`course-management/${course.id}/exercises`).then(() => { - courseManagementExercises.getExercise(exercise.id!).should('be.visible'); - }); - }); - - describe('Text exercise deletion', () => { - let exercise: TextExercise; - - before('Create text exercise', () => { - cy.login(admin, '/'); - exerciseAPIRequest.createTextExercise({ course }).then((response: Cypress.Response) => { - exercise = response.body; - }); - }); - - it('Deletes an existing text exercise', () => { - cy.login(admin, '/'); - navigationBar.openCourseManagement(); - courseManagement.openExercisesOfCourse(course.id!); - courseManagementExercises.deleteTextExercise(exercise); - courseManagementExercises.getExercise(exercise.id!).should('not.exist'); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/exercises/text/TextExerciseParticipation.cy.ts b/src/test/cypress/e2e/exercises/text/TextExerciseParticipation.cy.ts deleted file mode 100644 index 389fda0c887c..000000000000 --- a/src/test/cypress/e2e/exercises/text/TextExerciseParticipation.cy.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; - -import { courseManagementAPIRequest, courseOverview, exerciseAPIRequest, textExerciseEditor } from '../../../support/artemis'; -import { admin, studentOne } from '../../../support/users'; -import { convertModelAfterMultiPart } from '../../../support/utils'; - -describe.skip('Text exercise participation', () => { - let course: Course; - let exercise: TextExercise; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addStudentToCourse(course, studentOne); - exerciseAPIRequest.createTextExercise({ course }).then((exerciseResponse: Cypress.Response) => { - exercise = exerciseResponse.body; - }); - }); - }); - - it('Makes a text exercise submission as student', () => { - cy.login(studentOne, `/courses/${course.id}/exercises`); - courseOverview.startExercise(exercise.id!); - courseOverview.openRunningExercise(exercise.id!); - - // Verify the initial state of the text editor - textExerciseEditor.shouldShowExerciseTitleInHeader(exercise.title!); - textExerciseEditor.shouldShowProblemStatement(); - - // Make a submission - cy.fixture('loremIpsum.txt').then((submission) => { - textExerciseEditor.shouldShowNumberOfWords(0); - textExerciseEditor.shouldShowNumberOfCharacters(0); - textExerciseEditor.typeSubmission(exercise.id!, submission); - textExerciseEditor.shouldShowNumberOfWords(74); - textExerciseEditor.shouldShowNumberOfCharacters(451); - textExerciseEditor.submit().then((request: Interception) => { - expect(request.response!.body.text).to.eq(submission); - expect(request.response!.body.submitted).to.be.true; - expect(request.response!.statusCode).to.eq(200); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/e2e/lecture/LectureManagement.cy.ts b/src/test/cypress/e2e/lecture/LectureManagement.cy.ts deleted file mode 100644 index df4f85cbf740..000000000000 --- a/src/test/cypress/e2e/lecture/LectureManagement.cy.ts +++ /dev/null @@ -1,91 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Lecture } from 'app/entities/lecture.model'; - -import { courseManagementAPIRequest, exerciseAPIRequest, lectureCreation, lectureManagement } from '../../support/artemis'; -import { admin, instructor } from '../../support/users'; -import { convertModelAfterMultiPart, generateUUID } from '../../support/utils'; - -describe.skip('Lecture management', () => { - let course: Course; - - before('Create course', () => { - cy.login(admin); - courseManagementAPIRequest.createCourse().then((response) => { - course = convertModelAfterMultiPart(response); - courseManagementAPIRequest.addInstructorToCourse(course, instructor); - }); - }); - - it('Creates a lecture', () => { - const lectureTitle = 'Lecture ' + generateUUID(); - cy.login(instructor, '/course-management/' + course.id); - lectureManagement.getLectures().click(); - lectureManagement.clickCreateLecture(); - lectureCreation.setTitle(lectureTitle); - cy.fixture('loremIpsum-short.txt').then((text) => { - lectureCreation.typeDescription(text); - }); - lectureCreation.setVisibleDate(dayjs()); - lectureCreation.setStartDate(dayjs()); - lectureCreation.setEndDate(dayjs().add(1, 'hour')); - lectureCreation.save().then((lectureResponse) => { - expect(lectureResponse.response!.statusCode).to.eq(201); - }); - }); - - it('Deletes a lecture', () => { - let lecture: Lecture; - cy.login(instructor, '/'); - courseManagementAPIRequest.createLecture(course).then((lectureResponse) => { - lecture = lectureResponse.body; - cy.visit('/course-management/' + course.id + '/lectures'); - lectureManagement.deleteLecture(lecture).then((resp) => { - expect(resp.response!.statusCode).to.eq(200); - lectureManagement.getLecture(lecture.id!).should('not.exist'); - }); - }); - }); - - describe('Handle existing lecture', () => { - let lecture: Lecture; - - beforeEach('Create a lecture', () => { - cy.login(instructor, '/course-management/' + course.id + '/lectures'); - courseManagementAPIRequest.createLecture(course).then((lectureResponse) => { - lecture = lectureResponse.body; - }); - }); - - it('Deletes an existing lecture', () => { - lectureManagement.deleteLecture(lecture).then((resp) => { - expect(resp.response!.statusCode).to.eq(200); - lectureManagement.getLecture(lecture.id!).should('not.exist'); - }); - }); - - it('Adds a text unit to the lecture', () => { - cy.login(instructor, '/course-management/' + course.id + '/lectures'); - lectureManagement.openUnitsPage(lecture.id!); - cy.fixture('loremIpsum-short.txt').then((text) => { - lectureManagement.addTextUnit('Text unit', text); - }); - cy.contains('Text unit').should('be.visible'); - }); - - it('Adds a exercise unit to the lecture', () => { - cy.login(instructor, '/course-management/' + course.id + '/lectures'); - exerciseAPIRequest.createModelingExercise({ course }).then((model) => { - const exercise = model.body; - lectureManagement.openUnitsPage(lecture.id!); - lectureManagement.addExerciseUnit(exercise.id!); - cy.contains(exercise.title!); - }); - }); - }); - - after('Delete course', () => { - courseManagementAPIRequest.deleteCourse(course, admin); - }); -}); diff --git a/src/test/cypress/fixtures/course/icon.png b/src/test/cypress/fixtures/course/icon.png deleted file mode 100644 index 11078220190c..000000000000 Binary files a/src/test/cypress/fixtures/course/icon.png and /dev/null differ diff --git a/src/test/cypress/fixtures/exam/template.json b/src/test/cypress/fixtures/exam/template.json deleted file mode 100644 index 5f4ea802e6a7..000000000000 --- a/src/test/cypress/fixtures/exam/template.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "randomizeExerciseOrder": false, - "numberOfCorrectionRoundsInExam": 1, - "examMaxPoints": 10, - "visible": false, - "started": false, - "gracePeriod": 30, - "course": "", - "title": "", - "visibleDate": "", - "startDate": "", - "endDate": "", - "numberOfExercisesInExam": 1, - "startText": "Exam start text", - "endText": "Exam end text", - "confirmationStartText": "Exam confirmation start text", - "confirmationEndText": "Exam confirmation end text" -} diff --git a/src/test/cypress/fixtures/exercise/file-upload/submission.json b/src/test/cypress/fixtures/exercise/file-upload/submission.json deleted file mode 100644 index 6d588766c95a..000000000000 --- a/src/test/cypress/fixtures/exercise/file-upload/submission.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files": [ - { - "name": "BubbleSort.java", - "path": "programming_exercise_submissions/build_error/BubbleSort.txt" - } - ] -} diff --git a/src/test/cypress/fixtures/exercise/file-upload/template.json b/src/test/cypress/fixtures/exercise/file-upload/template.json deleted file mode 100644 index add580f86f46..000000000000 --- a/src/test/cypress/fixtures/exercise/file-upload/template.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "mode": "INDIVIDUAL", - "includedInOverallScore": "INCLUDED_COMPLETELY", - "studentAssignedTeamIdComputed": false, - "secondCorrectionEnabled": false, - "type": "file-upload", - "bonusPoints": 0, - "isAtLeastTutor": false, - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "teamMode": false, - "assessmentDueDateError": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "presentationScoreEnabled": false, - "assessmentType": "MANUAL", - "title": "File Upload Exercise 1", - "filePattern": "pdf", - "maxPoints": 10, - "problemStatement": "Problem Statement", - "exampleSolution": "Example Solution", - "gradingInstructions": "Assessment Instructions" -} diff --git a/src/test/cypress/fixtures/exercise/modeling/submission.json b/src/test/cypress/fixtures/exercise/modeling/submission.json deleted file mode 100644 index 15f3e5c2b71d..000000000000 --- a/src/test/cypress/fixtures/exercise/modeling/submission.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "submissionExerciseType": "modeling", - "id": null, - "submitted": false, - "participation": null, - "empty": true, - "explanationText": "", - "model": "{\"version\":\"2.0.0\",\"type\":\"ClassDiagram\",\"size\":{\"width\":1000,\"height\":500},\"interactive\":{\"elements\":[],\"relationships\":[]},\"elements\":[{\"id\":\"ccf5edad-98d5-401d-9885-e332607dfa6a\",\"name\":\"Package\",\"type\":\"Package\",\"owner\":null,\"bounds\":{\"x\":240,\"y\":230,\"width\":200,\"height\":100}},{\"id\":\"59787b15-e81c-4710-99bf-e11104c22dbf\",\"name\":\"Class\",\"type\":\"Class\",\"owner\":null,\"bounds\":{\"x\":0,\"y\":0,\"width\":200,\"height\":102},\"attributes\":[\"c3469dd2-a822-45b2-823d-13bc5c8370da\"],\"methods\":[\"b6457544-7cbe-47f5-b704-523ce6e967f9\"]},{\"id\":\"c3469dd2-a822-45b2-823d-13bc5c8370da\",\"name\":\"+ attribute: Type\",\"type\":\"ClassAttribute\",\"owner\":\"59787b15-e81c-4710-99bf-e11104c22dbf\",\"bounds\":{\"x\":0.5,\"y\":40.5,\"width\":199,\"height\":30}},{\"id\":\"b6457544-7cbe-47f5-b704-523ce6e967f9\",\"name\":\"+ method()\",\"type\":\"ClassMethod\",\"owner\":\"59787b15-e81c-4710-99bf-e11104c22dbf\",\"bounds\":{\"x\":0.5,\"y\":71.5,\"width\":199,\"height\":30}},{\"id\":\"8786cede-a45c-4593-ac6a-b7ac235f1fae\",\"name\":\"Abstract\",\"type\":\"AbstractClass\",\"owner\":null,\"bounds\":{\"x\":0,\"y\":120,\"width\":200,\"height\":112},\"attributes\":[\"0aaf6115-8d34-4509-a179-26d7b7d22596\"],\"methods\":[\"9f285794-4e97-42d1-adcc-e5467977b76c\"]},{\"id\":\"0aaf6115-8d34-4509-a179-26d7b7d22596\",\"name\":\"+ attribute: Type\",\"type\":\"ClassAttribute\",\"owner\":\"8786cede-a45c-4593-ac6a-b7ac235f1fae\",\"bounds\":{\"x\":0.5,\"y\":170.5,\"width\":199,\"height\":30}},{\"id\":\"9f285794-4e97-42d1-adcc-e5467977b76c\",\"name\":\"+ method()\",\"type\":\"ClassMethod\",\"owner\":\"8786cede-a45c-4593-ac6a-b7ac235f1fae\",\"bounds\":{\"x\":0.5,\"y\":201.5,\"width\":199,\"height\":30}}],\"relationships\":[],\"assessments\":[]}" -} diff --git a/src/test/cypress/fixtures/exercise/modeling/template.json b/src/test/cypress/fixtures/exercise/modeling/template.json deleted file mode 100644 index 6173bb7cfe61..000000000000 --- a/src/test/cypress/fixtures/exercise/modeling/template.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "mode": "INDIVIDUAL", - "includedInOverallScore": "INCLUDED_COMPLETELY", - "numberOfAssessmentsOfCorrectionRounds": [ - { - "inTime": 0, - "late": 0 - } - ], - "studentAssignedTeamIdComputed": false, - "secondCorrectionEnabled": false, - "type": "modeling", - "bonusPoints": 10, - "isAtLeastTutor": false, - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "teamMode": false, - "assessmentDueDateError": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "presentationScoreEnabled": false, - "diagramType": "ClassDiagram", - "assessmentType": "MANUAL", - "title": "Modeling Exercise", - "categories": ["{\"category\":\"cypress-e2e\",\"color\":\"#6ae8ac\"}"], - "difficulty": "EASY", - "maxPoints": 10, - "problemStatement": "Problem Statement", - "gradingInstructions": "Grading Instruction ", - "exampleSolutionExplanation": "Example Solution Explanation", - "exampleSolutionModel": "{\"version\":\"2.0.0\",\"type\":\"ClassDiagram\",\"size\":{\"width\":640,\"height\":600},\"interactive\":{\"elements\":[],\"relationships\":[]},\"elements\":[{\"id\":\"6e1f57c6-cbc7-4b97-9df9-c5741dc905fa\",\"name\":\"Package\",\"type\":\"Package\",\"owner\":null,\"bounds\":{\"x\":230,\"y\":0,\"width\":200,\"height\":100}},{\"id\":\"ff7e3be0-9765-4301-baf5-cf9cf2f17c3c\",\"name\":\"Class\",\"type\":\"Class\",\"owner\":null,\"bounds\":{\"x\":0,\"y\":220,\"width\":200,\"height\":100},\"attributes\":[\"de2d464b-f969-4cf3-ac0d-2f300b3a6497\"],\"methods\":[\"084a59b9-3009-4ebd-885c-159b436581d9\"]},{\"id\":\"de2d464b-f969-4cf3-ac0d-2f300b3a6497\",\"name\":\"+ attribute: Type\",\"type\":\"ClassAttribute\",\"owner\":\"ff7e3be0-9765-4301-baf5-cf9cf2f17c3c\",\"bounds\":{\"x\":0,\"y\":260,\"width\":200,\"height\":30}},{\"id\":\"084a59b9-3009-4ebd-885c-159b436581d9\",\"name\":\"+ method()\",\"type\":\"ClassMethod\",\"owner\":\"ff7e3be0-9765-4301-baf5-cf9cf2f17c3c\",\"bounds\":{\"x\":0,\"y\":290,\"width\":200,\"height\":30}},{\"id\":\"1ad94aff-ee37-494f-8b99-9d861dc58e4a\",\"name\":\"Abstract\",\"type\":\"AbstractClass\",\"owner\":null,\"bounds\":{\"x\":380,\"y\":220,\"width\":200,\"height\":110},\"attributes\":[\"4d58287c-d4c0-42d1-9d02-ad239b701de6\"],\"methods\":[\"2de2a7c3-19c9-4534-8a71-f6607e93556c\"]},{\"id\":\"4d58287c-d4c0-42d1-9d02-ad239b701de6\",\"name\":\"+ attribute: Type\",\"type\":\"ClassAttribute\",\"owner\":\"1ad94aff-ee37-494f-8b99-9d861dc58e4a\",\"bounds\":{\"x\":380,\"y\":270,\"width\":200,\"height\":30}},{\"id\":\"2de2a7c3-19c9-4534-8a71-f6607e93556c\",\"name\":\"+ method()\",\"type\":\"ClassMethod\",\"owner\":\"1ad94aff-ee37-494f-8b99-9d861dc58e4a\",\"bounds\":{\"x\":380,\"y\":300,\"width\":200,\"height\":30}}],\"relationships\":[],\"assessments\":[]}" -} diff --git a/src/test/cypress/fixtures/exercise/programming/c/all_successful/exercise.txt b/src/test/cypress/fixtures/exercise/programming/c/all_successful/exercise.txt deleted file mode 100644 index 9fec30452413..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/c/all_successful/exercise.txt +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include - -int main(void) { - int x = 6; - int y = 10; - printf("%d\n", x * x + 5 * y - 4); - return EXIT_SUCCESS; -} diff --git a/src/test/cypress/fixtures/exercise/programming/c/all_successful/submission.json b/src/test/cypress/fixtures/exercise/programming/c/all_successful/submission.json deleted file mode 100644 index b12cadde14e5..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/c/all_successful/submission.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "deleteFiles": ["exercise.c"], - "createFilesInRootFolder": true, - "files": [ - { - "name": "exercise.c", - "path": "exercise/programming/c/all_successful/exercise.txt" - } - ], - "expectedResult": "100%" -} diff --git a/src/test/cypress/fixtures/exercise/programming/c/template.json b/src/test/cypress/fixtures/exercise/programming/c/template.json deleted file mode 100644 index ee82b71a9be9..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/c/template.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "allowOfflineIde": true, - "allowOnlineEditor": true, - "assessmentDueDateError": false, - "assessmentType": "AUTOMATIC", - "bonusPoints": 0, - "checkoutSolutionRepository": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "includedInOverallScore": "INCLUDED_COMPLETELY", - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "isAtLeastTutor": false, - "maxPoints": 10, - "mode": "INDIVIDUAL", - "noVersionControlAndContinuousIntegrationAvailable": false, - "packageName": "", - "presentationScoreEnabled": false, - "problemStatement": "# Aufgabe (6 Punkte)\n\n## Beschreibung\n\nErstelle ein C-Programm, welches das Ergebnis des Terms $$ x^2 + 5 \\cdot y - 4 $$ berechnet und auf der\nStandardausgabe ausgibt. Verwende für die Variablen `x` und `y` den Datentyp `int` und wähle für beide\nVariablen einen beliebigen Wert!\n\n### Beispielausgaben\n\nBeispiel 1: `x = 3` und `y = 5`\n\n```text\n30\n```\n\nBeispiel 2: `x = -3` und `y = 5`\n\n```text\n30\n```\n\nBeispiel 3: `x = 5` und `y = -10`\n\n```text\n-29\n```\n\n## Hinweise\n\n- Bei den Ein-/Ausgabetests werden beim Testen die Werte der Variablen `x` und `y` in Ihrem Programm verändert!\n\n## Tests\n\n1. [task][Kompilieren](Compile)\n2. [task][Strukturtests](CodeStructure)\n3. [task][Ein-/Ausgabetests](InputOutput)\n", - "programmingLanguage": "C", - "projectType": "FACT", - "secondCorrectionEnabled": false, - "shortName": "", - "showTestNamesToStudents": false, - "solutionParticipation": { - "type": "solution" - }, - "staticCodeAnalysisEnabled": false, - "studentAssignedTeamIdComputed": false, - "teamMode": false, - "templateParticipation": { - "type": "template" - }, - "title": "", - "type": "programming" -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/BubbleSort.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/BubbleSort.txt deleted file mode 100644 index 8484f2f2bc90..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/BubbleSort.txt +++ /dev/null @@ -1,23 +0,0 @@ -package de.test; - -import java.util.*; - -public class BubbleSort implements SortStrategy { - - /** - * Sorts dates with BubbleSort. - * - * @param input the List of Dates to be sorted - */ - public void performSort(List input) { - for (int i = input.size() - 1; i >= 0; i--) { - for (int j = 0; j < i; j++) { - if (input.get(j).compareTo(input.get(j + 1)) > 0) { - Date temp = input.get(j); - input.set(j, input.get(j + 1)); - input.set(j + 1, temp); - } - } - } - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Client.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/Client.txt deleted file mode 100644 index b4142dec7883..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Client.txt +++ /dev/null @@ -1,109 +0,0 @@ -package de.test; - -import java.text.*; -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; - -public final class Client { - - private static final int ITERATIONS = 10; - - private static final int RANDOM_FLOOR = 5; - - private static final int RANDOM_CEILING = 15; - - private Client() { - } - - /** - * Main method. - * Add code to demonstrate your implementation here. - * - * @param args command line arguments - */ - public static void main(String[] args) throws ParseException { - - Context sortingContext = new Context(); - Policy policy = new Policy(sortingContext); - - for (int i = 0; i < ITERATIONS; i++) { - List dates = createRandomDatesList(); - - sortingContext.setDates(dates); - policy.configure(); - - System.out.print("Unsorted Array of course dates = "); - printDateList(dates); - - sortingContext.sort(); - - System.out.print("Sorted Array of course dates = "); - printDateList(dates); - } - } - - /** - * Generates a List of random Date objects with random List size between - * {@link #RANDOM_FLOOR} and {@link #RANDOM_CEILING}. - * - * @return a List of random Date objects - * @throws ParserException if date string cannot be parsed - */ - private static List createRandomDatesList() throws ParseException { - int listLength = randomIntegerWithin(RANDOM_FLOOR, RANDOM_CEILING); - List list = new ArrayList<>(); - - SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); - Date lowestDate = dateFormat.parse("08.11.2016"); - Date highestDate = dateFormat.parse("03.11.2020"); - - for (int i = 0; i < listLength; i++) { - Date randomDate = randomDateWithin(lowestDate, highestDate); - list.add(randomDate); - } - return list; - } - - /** - * Creates a random Date within the given range. - * - * @param low the lower bound - * @param high the upper bound - * @return random Date within the given range - */ - private static Date randomDateWithin(Date low, Date high) { - long randomLong = randomLongWithin(low.getTime(), high.getTime()); - return new Date(randomLong); - } - - /** - * Creates a random long within the given range. - * - * @param low the lower bound - * @param high the upper bound - * @return random long within the given range - */ - private static long randomLongWithin(long low, long high) { - return ThreadLocalRandom.current().nextLong(low, high + 1); - } - - /** - * Creates a random int within the given range. - * - * @param low the lower bound - * @param high the upper bound - * @return random int within the given range - */ - private static int randomIntegerWithin(int low, int high) { - return ThreadLocalRandom.current().nextInt(low, high + 1); - } - - /** - * Prints out the given Array of Date objects. - * - * @param list of the dates to print - */ - private static void printDateList(List list) { - System.out.println(list.toString()); - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Context.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/Context.txt deleted file mode 100644 index c44eca71bc4c..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Context.txt +++ /dev/null @@ -1,34 +0,0 @@ -package de.test; - -import java.util.*; - -public class Context { - private SortStrategy sortAlgorithm; - - private List dates; - - public List getDates() { - return dates; - } - - public void setDates(List dates) { - this.dates = dates; - } - - public void setSortAlgorithm(SortStrategy sa) { - sortAlgorithm = sa; - } - - public SortStrategy getSortAlgorithm() { - return sortAlgorithm; - } - - /** - * Runs the configured sort algorithm. - */ - public void sort() { - if (sortAlgorithm != null) { - sortAlgorithm.performSort(this.dates); - } - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/MergeSort.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/MergeSort.txt deleted file mode 100644 index 449b265e261c..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/MergeSort.txt +++ /dev/null @@ -1,53 +0,0 @@ -package de.test; - -import java.util.*; - -public class MergeSort implements SortStrategy { - - /** - * Wrapper method for the real MergeSort algorithm. - * - * @param input the List of Dates to be sorted - */ - public void performSort(List input) { - mergesort(input, 0, input.size() - 1); - } - - private void mergesort(List input, int low, int high) { - if (high - low < 1) { - return; - } - int mid = (low + high) / 2; - mergesort(input, low, mid); - mergesort(input, mid + 1, high); - merge(input, low, mid, high); - } - - private void merge(List input, int low, int middle, int high) { - - Date[] temp = new Date[high - low + 1]; - int leftIndex = low; - int rightIndex = middle + 1; - int wholeIndex = 0; - while (leftIndex <= middle && rightIndex <= high) { - if (input.get(leftIndex).compareTo(input.get(rightIndex)) <= 0) { - temp[wholeIndex] = input.get(leftIndex++); - } else { - temp[wholeIndex] = input.get(rightIndex++); - } - wholeIndex++; - } - if (leftIndex <= middle && rightIndex > high) { - while (leftIndex <= middle) { - temp[wholeIndex++] = input.get(leftIndex++); - } - } else { - while (rightIndex <= high) { - temp[wholeIndex++] = input.get(rightIndex++); - } - } - for (wholeIndex = 0; wholeIndex < temp.length; wholeIndex++) { - input.set(wholeIndex + low, temp[wholeIndex]); - } - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Policy.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/Policy.txt deleted file mode 100644 index 1ed740f33d38..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/Policy.txt +++ /dev/null @@ -1,25 +0,0 @@ -package de.test; - -public class Policy { - - private static final int DATES_SIZE_THRESHOLD = 10; - - private Context context; - - public Policy(Context context) { - this.context = context; - } - - /** - * Chooses a strategy depending on the number of date objects. - */ - public void configure() { - if (this.context.getDates().size() > DATES_SIZE_THRESHOLD) { - System.out.println("More than " + DATES_SIZE_THRESHOLD + " dates, choosing merge sort!"); - this.context.setSortAlgorithm(new MergeSort()); - } else { - System.out.println("Less or equal than " + DATES_SIZE_THRESHOLD + " dates. choosing quick sort!"); - this.context.setSortAlgorithm(new BubbleSort()); - } - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/SortStrategy.txt b/src/test/cypress/fixtures/exercise/programming/java/all_successful/SortStrategy.txt deleted file mode 100644 index c721267ddb7a..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/SortStrategy.txt +++ /dev/null @@ -1,14 +0,0 @@ -package de.test; - -import java.util.Date; -import java.util.List; - -public interface SortStrategy { - - /** - * Sorts a list of Dates. - * - * @param input list of Dates - */ - void performSort(List input); -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/all_successful/submission.json b/src/test/cypress/fixtures/exercise/programming/java/all_successful/submission.json deleted file mode 100644 index 8a4d1751131b..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/all_successful/submission.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "deleteFiles": ["BubbleSort.java", "Client.java", "MergeSort.java"], - "createFilesInRootFolder": false, - "files": [ - { - "name": "BubbleSort.java", - "path": "exercise/programming/java/all_successful/BubbleSort.txt" - }, - { - "name": "Context.java", - "path": "exercise/programming/java/all_successful/Context.txt" - }, - { - "name": "Client.java", - "path": "exercise/programming/java/all_successful/Client.txt" - }, - { - "name": "MergeSort.java", - "path": "exercise/programming/java/all_successful/MergeSort.txt" - }, - { - "name": "Policy.java", - "path": "exercise/programming/java/all_successful/Policy.txt" - }, - { - "name": "SortStrategy.java", - "path": "exercise/programming/java/all_successful/SortStrategy.txt" - } - ], - "expectedResult": "100%", - "packageName": "de.test" -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/assessment/submission.json b/src/test/cypress/fixtures/exercise/programming/java/assessment/submission.json deleted file mode 100644 index 00f63679dd36..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/assessment/submission.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "fileName": "src/de/test/MergeSort.java", - "fileContent": "package de.test;\n\nimport java.util.*;\n\npublic class MergeSort implements SortStrategy {\n \n /**\n * Wrapper method for the real MergeSort algorithm.\n *\n * @param input the List of Dates to be sorted\n */\n public void performSort(List input) {\n mergesort(input, 0, input.size() - 1);\n }\n\n // Recursive merge sort method\n private void mergesort(List input, int low, int high) {\n if (high - low < 1) {\n return;\n }\n int mid = (low + high) / 2;\n mergesort(input, low, mid);\n mergesort(input, mid + 1, high);\n merge(input, low, mid, high);\n }\n\n // Merge method\n private void merge(List input, int low, int middle, int high) {\n\n Date[] temp = new Date[high - low + 1];\n int leftIndex = low;\n int rightIndex = middle + 1;\n int wholeIndex = 0;\n while (leftIndex <= middle && rightIndex <= high) {\n if (input.get(leftIndex).compareTo(input.get(rightIndex)) <= 0) {\n temp[wholeIndex] = input.get(leftIndex++);\n }\n else {\n temp[wholeIndex] = input.get(rightIndex++);\n }\n wholeIndex++;\n }\n if (leftIndex <= middle && rightIndex > high) {\n while (leftIndex <= middle) {\n temp[wholeIndex++] = input.get(leftIndex++);\n }\n }\n else {\n while (rightIndex <= high) {\n temp[wholeIndex++] = input.get(rightIndex++);\n }\n }\n for (wholeIndex = 0; wholeIndex < temp.length; wholeIndex++) {\n input.set(wholeIndex + low, temp[wholeIndex]);\n }\n }\n}\n" - }, - { - "fileName": "src/de/test/Client.java", - "fileContent": "package de.test;\n\nimport java.text.*;\nimport java.util.*;\nimport java.util.concurrent.ThreadLocalRandom;\n\npublic final class Client {\n\n private static final int ITERATIONS = 10;\n \n private static final int RANDOM_FLOOR = 5;\n \n private static final int RANDOM_CEILING = 15;\n\n private Client() {\n }\n\n /**\n * Main method.\n * Add code to demonstrate your implementation here.\n *\n * @param args command line arguments\n */\n public static void main(String[] args) throws ParseException {\n\n // Init Context and Policy\n\n Context sortingContext = new Context();\n Policy policy = new Policy(sortingContext);\n\n // Run multiple times to simulate different sorting strategies\n for (int i = 0; i < ITERATIONS; i++) {\n List dates = createRandomDatesList();\n\n sortingContext.setDates(dates);\n policy.configure();\n\n System.out.print(\"Unsorted Array of course dates = \");\n printDateList(dates);\n\n sortingContext.sort();\n\n System.out.print(\"Sorted Array of course dates = \");\n printDateList(dates);\n }\n }\n\n /**\n * Generates a List of random Date objects with random List size between\n * {@link #RANDOM_FLOOR} and {@link #RANDOM_CEILING}.\n *\n * @return a List of random Date objects\n * @throws ParserException if date string cannot be parsed\n */\n private static List createRandomDatesList() throws ParseException {\n int listLength = randomIntegerWithin(RANDOM_FLOOR, RANDOM_CEILING);\n List list = new ArrayList<>();\n\n SimpleDateFormat dateFormat = new SimpleDateFormat(\"dd.MM.yyyy\");\n Date lowestDate = dateFormat.parse(\"08.11.2016\");\n Date highestDate = dateFormat.parse(\"03.11.2020\");\n\n for (int i = 0; i < listLength; i++) {\n Date randomDate = randomDateWithin(lowestDate, highestDate);\n list.add(randomDate);\n }\n return list;\n }\n\n /**\n * Creates a random Date within the given range.\n *\n * @param low the lower bound\n * @param high the upper bound\n * @return random Date within the given range\n */\n private static Date randomDateWithin(Date low, Date high) {\n long randomLong = randomLongWithin(low.getTime(), high.getTime());\n return new Date(randomLong);\n }\n\n /**\n * Creates a random long within the given range.\n *\n * @param low the lower bound\n * @param high the upper bound\n * @return random long within the given range\n */\n private static long randomLongWithin(long low, long high) {\n return ThreadLocalRandom.current().nextLong(low, high + 1);\n }\n\n /**\n * Creates a random int within the given range.\n *\n * @param low the lower bound\n * @param high the upper bound\n * @return random int within the given range\n */\n private static int randomIntegerWithin(int low, int high) {\n return ThreadLocalRandom.current().nextInt(low, high + 1);\n }\n\n /**\n * Prints out the given Array of Date objects.\n *\n * @param list of the dates to print\n */\n private static void printDateList(List list) {\n System.out.println(list.toString());\n }\n}\n" - }, - { - "fileName": "src/de/test/BubbleSort.java", - "fileContent": "package de.test;\n\nimport java.util.*;\n\npublic class BubbleSort implements SortStrategy {\n /**\n * Sorts dates with BubbleSort.\n *\n */\n public void performSort(List input) {\n for (int i = input.size() - 1; i >= 0; i--) {\n for (int j = 0; j < i; j++) {\n if (input.get(j).compareTo(input.get(j + 1)) > 0) {\n Date temp = input.get(j);\n input.set(j, input.get(j + 1));\n input.set(j + 1, temp);\n }\n }\n }\n\n }\n}\n" - } -] diff --git a/src/test/cypress/fixtures/exercise/programming/java/build_error/BubbleSort.txt b/src/test/cypress/fixtures/exercise/programming/java/build_error/BubbleSort.txt deleted file mode 100644 index 030102a8e7c1..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/build_error/BubbleSort.txt +++ /dev/null @@ -1 +0,0 @@ -this should fail \ No newline at end of file diff --git a/src/test/cypress/fixtures/exercise/programming/java/build_error/submission.json b/src/test/cypress/fixtures/exercise/programming/java/build_error/submission.json deleted file mode 100644 index 4a873094d884..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/build_error/submission.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "deleteFiles": ["BubbleSort.java", "Client.java", "MergeSort.java"], - "createFilesInRootFolder": false, - "files": [ - { - "name": "BubbleSort.java", - "path": "exercise/programming/java/build_error/BubbleSort.txt" - } - ], - "expectedResult": "0%", - "packageName": "de.test" -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/partially_successful/submission.json b/src/test/cypress/fixtures/exercise/programming/java/partially_successful/submission.json deleted file mode 100644 index 97560fee1f72..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/partially_successful/submission.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "deleteFiles": ["BubbleSort.java", "Client.java", "MergeSort.java"], - "createFilesInRootFolder": false, - "files": [ - { - "name": "BubbleSort.java", - "path": "exercise/programming/java/all_successful/BubbleSort.txt" - }, - { - "name": "MergeSort.java", - "path": "exercise/programming/java/all_successful/MergeSort.txt" - }, - { - "name": "SortStrategy.java", - "path": "exercise/programming/java/all_successful/SortStrategy.txt" - } - ], - "expectedResult": "46.2%", - "packageName": "de.test" -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/BubbleSort.txt b/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/BubbleSort.txt deleted file mode 100644 index e5c69849b14c..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/BubbleSort.txt +++ /dev/null @@ -1,28 +0,0 @@ -package de.test; - -import java.util.*; - -public class BubbleSort implements SortStrategy { - - public static String literal1 = "Header"; - private static final String LITERAL_TWO = "Literal2"; - - /** - * Sorts dates with BubbleSort. - * - * @param input the List of Dates to be sorted - */ - public void performSort(List input) { - - for (int i = input.size() - 1; i >= 0; i--) { - for (int j = 0; j < i; j++) { - if (input.get(j).compareTo(input.get(j + 1)) > 0) { - Date temp = input.get(j); - input.set(j, input.get(j + 1)); - input.set(j + 1, temp); - } - } - } - - } -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/submission.json b/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/submission.json deleted file mode 100644 index 330ddd0f554d..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/static_code_analysis/submission.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "deleteFiles": ["BubbleSort.java", "Client.java", "MergeSort.java"], - "createFilesInRootFolder": false, - "files": [ - { - "name": "BubbleSort.java", - "path": "exercise/programming/java/static_code_analysis/BubbleSort.txt" - }, - { - "name": "Context.java", - "path": "exercise/programming/java/all_successful/Context.txt" - }, - { - "name": "Client.java", - "path": "exercise/programming/java/all_successful/Client.txt" - }, - { - "name": "MergeSort.java", - "path": "exercise/programming/java/all_successful/MergeSort.txt" - }, - { - "name": "Policy.java", - "path": "exercise/programming/java/all_successful/Policy.txt" - }, - { - "name": "SortStrategy.java", - "path": "exercise/programming/java/all_successful/SortStrategy.txt" - } - ], - "expectedResult": "50%", - "packageName": "de.test" -} diff --git a/src/test/cypress/fixtures/exercise/programming/java/template.json b/src/test/cypress/fixtures/exercise/programming/java/template.json deleted file mode 100644 index b5a45c605e12..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/java/template.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "allowOfflineIde": true, - "allowOnlineEditor": true, - "assessmentDueDateError": false, - "assessmentType": "AUTOMATIC", - "bonusPoints": 0, - "checkoutSolutionRepository": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "includedInOverallScore": "INCLUDED_COMPLETELY", - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "isAtLeastTutor": false, - "maxPoints": 10, - "mode": "INDIVIDUAL", - "noVersionControlAndContinuousIntegrationAvailable": false, - "packageName": "", - "presentationScoreEnabled": false, - "problemStatement": "# Sorting with the Strategy Pattern\n\nIn this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables.\n\n### Part 1: Sorting\n\nFirst, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`.\n\n**You have the following tasks:**\n\n1. [task][Implement Bubble Sort](testBubbleSort)\nImplement the method `performSort(List)` in the class `BubbleSort`. Make sure to follow the Bubble Sort algorithm exactly.\n\n2. [task][Implement Merge Sort](testMergeSort)\nImplement the method `performSort(List)` in the class `MergeSort`. Make sure to follow the Merge Sort algorithm exactly.\n\n### Part 2: Strategy Pattern\n\nWe want the application to apply different algorithms for sorting a `List` of `Date` objects.\nUse the strategy pattern to select the right sorting algorithm at runtime.\n\n**You have the following tasks:**\n\n1. [task][SortStrategy Interface](testClass[SortStrategy],testMethods[SortStrategy])\nCreate a `SortStrategy` interface and adjust the sorting algorithms so that they implement this interface.\n\n2. [task][Context Class](testAttributes[Context],testMethods[Context])\nCreate and implement a `Context` class following the below class diagram\n\n3. [task][Context Policy](testConstructors[Policy],testAttributes[Policy],testMethods[Policy])\nCreate and implement a `Policy` class following the below class diagram with a simple configuration mechanism:\n\n 1. [task][Select MergeSort](testClass[MergeSort],testUseMergeSortForBigList)\n Select `MergeSort` when the List has more than 10 dates.\n\n 2. [task][Select BubbleSort](testClass[BubbleSort],testUseBubbleSortForSmallList)\n Select `BubbleSort` when the List has less or equal 10 dates.\n\n4. Complete the `Client` class which demonstrates switching between two strategies at runtime.\n\n@startuml\n\nclass Client {\n}\n\nclass Policy {\n +configure()\n}\n\nclass Context {\n -dates: List\n +sort()\n}\n\ninterface SortStrategy {\n +performSort(List)\n}\n\nclass BubbleSort {\n +performSort(List)\n}\n\nclass MergeSort {\n +performSort(List)\n}\n\nMergeSort -up-|> SortStrategy #testsColor(testClass[MergeSort])\nBubbleSort -up-|> SortStrategy #testsColor(testClass[BubbleSort])\nPolicy -right-> Context #testsColor(testAttributes[Policy]): context\nContext -right-> SortStrategy #testsColor(testAttributes[Context]): sortAlgorithm\nClient .down.> Policy\nClient .down.> Context\n\nhide empty fields\nhide empty methods\n\n@enduml\n\n\n### Part 3: Optional Challenges\n\n(These are not tested)\n\n1. Create a new class `QuickSort` that implements `SortStrategy` and implement the Quick Sort algorithm.\n\n2. Make the method `performSort(List)` generic, so that other objects can also be sorted by the same method.\n**Hint:** Have a look at Java Generics and the interface `Comparable`.\n\n3. Think about a useful decision in `Policy` when to use the new `QuickSort` algorithm.\n", - "programmingLanguage": "JAVA", - "projectType": "PLAIN_MAVEN", - "secondCorrectionEnabled": false, - "shortName": "", - "showTestNamesToStudents": false, - "solutionParticipation": { - "type": "solution" - }, - "staticCodeAnalysisEnabled": false, - "studentAssignedTeamIdComputed": false, - "teamMode": false, - "templateParticipation": { - "type": "template" - }, - "title": "", - "type": "programming" -} diff --git a/src/test/cypress/fixtures/exercise/programming/python/all_successful/context.txt b/src/test/cypress/fixtures/exercise/programming/python/all_successful/context.txt deleted file mode 100644 index b57df9cfc106..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/all_successful/context.txt +++ /dev/null @@ -1,6 +0,0 @@ -class Context: - sorting_algorithm = None - numbers = None - - def sort(self): - self.sorting_algorithm.perform_sort(self.numbers) diff --git a/src/test/cypress/fixtures/exercise/programming/python/all_successful/policy.txt b/src/test/cypress/fixtures/exercise/programming/python/all_successful/policy.txt deleted file mode 100644 index 72517c91096d..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/all_successful/policy.txt +++ /dev/null @@ -1,16 +0,0 @@ -from .sorting_algorithms import * - - -class Policy: - context = None - - def __init__(self, context): - self.context = context - - def configure(self): - if len(self.context.numbers) > 10: - print('More than 10 numbers, choosing merge sort!') - self.context.sorting_algorithm = MergeSort() - else: - print('Less or equal than 10 numbers, choosing bubble sort!') - self.context.sorting_algorithm = BubbleSort() diff --git a/src/test/cypress/fixtures/exercise/programming/python/all_successful/sort_strategy.txt b/src/test/cypress/fixtures/exercise/programming/python/all_successful/sort_strategy.txt deleted file mode 100644 index a1f8b65ee6a1..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/all_successful/sort_strategy.txt +++ /dev/null @@ -1,8 +0,0 @@ -from abc import ABC, abstractmethod - - -class SortStrategy(ABC): - - @abstractmethod - def perform_sort(self, array): - pass diff --git a/src/test/cypress/fixtures/exercise/programming/python/all_successful/sorting_algorithms.txt b/src/test/cypress/fixtures/exercise/programming/python/all_successful/sorting_algorithms.txt deleted file mode 100644 index a51fe1b136c6..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/all_successful/sorting_algorithms.txt +++ /dev/null @@ -1,58 +0,0 @@ -from .sort_strategy import SortStrategy - - -class BubbleSort(SortStrategy): - - def perform_sort(self, arr): - if arr is None: - return - - for i in range(len(arr))[::-1]: - for j in range(i): - if arr[j] > arr[j + 1]: - arr[j], arr[j + 1] = arr[j + 1], arr[j] - - -class MergeSort(SortStrategy): - - def perform_sort(self, arr): - self.__merge_sort(arr, 0, len(arr) - 1) - - def __merge_sort(self, arr, low, high): - if high - low < 1: - return - - mid = int((low + high) / 2) - self.__merge_sort(arr, low, mid) - self.__merge_sort(arr, mid + 1, high) - self.__merge(arr, low, mid, high) - - def __merge(self, arr, low, mid, high): - temp = [None] * (high - low + 1) - - left_index = low - right_index = mid + 1 - whole_index = 0 - - while left_index <= mid and right_index <= high: - if arr[left_index] <= arr[right_index]: - temp[whole_index] = arr[left_index] - left_index += 1 - else: - temp[whole_index] = arr[right_index] - right_index += 1 - whole_index += 1 - - if left_index <= mid and right_index > high: - while left_index <= mid: - temp[whole_index] = arr[left_index] - whole_index += 1 - left_index += 1 - else: - while right_index <= high: - temp[whole_index] = arr[right_index] - whole_index += 1 - right_index += 1 - - for whole_index in range(len(temp)): - arr[whole_index + low] = temp[whole_index] diff --git a/src/test/cypress/fixtures/exercise/programming/python/all_successful/submission.json b/src/test/cypress/fixtures/exercise/programming/python/all_successful/submission.json deleted file mode 100644 index 258756288bb9..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/all_successful/submission.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "deleteFiles": ["context.py", "policy.py", "sort_strategy.py", "sorting_algorithms.py"], - "createFilesInRootFolder": true, - "files": [ - { - "name": "context.py", - "path": "exercise/programming/python/all_successful/context.txt" - }, - { - "name": "policy.py", - "path": "exercise/programming/python/all_successful/policy.txt" - }, - { - "name": "sort_strategy.py", - "path": "exercise/programming/python/all_successful/sort_strategy.txt" - }, - { - "name": "sorting_algorithms.py", - "path": "exercise/programming/python/all_successful/sorting_algorithms.txt" - } - ], - "expectedResult": "100%" -} diff --git a/src/test/cypress/fixtures/exercise/programming/python/template.json b/src/test/cypress/fixtures/exercise/programming/python/template.json deleted file mode 100644 index cb0962ce3c00..000000000000 --- a/src/test/cypress/fixtures/exercise/programming/python/template.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "allowComplaintsForAutomaticAssessments": false, - "allowFeedbackRequests": false, - "allowOfflineIde": false, - "allowOnlineEditor": true, - "assessmentDueDateError": false, - "assessmentType": "AUTOMATIC", - "bonusPoints": 0, - "checkoutSolutionRepository": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "includedInOverallScore": "INCLUDED_COMPLETELY", - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "isAtLeastTutor": false, - "maxPoints": 10, - "mode": "INDIVIDUAL", - "noVersionControlAndContinuousIntegrationAvailable": false, - "packageName": "", - "presentationScoreEnabled": false, - "problemStatement": "# Sorting with the Strategy Pattern\n\nIn this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables.\n\n### Part 1: Sorting\n\nFirst, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`.\n\n**You have the following tasks:**\n\n1. [task][Implement Bubble Sort](test_bubble_sort)\nImplement the method `perform_sort(List)` in the class `BubbleSort`. Make sure to follow the Bubble Sort algorithm exactly.\n\n2. [task][Implement Merge Sort](test_merge_sort)\nImplement the method `perform_sort(List)` in the class `MergeSort`. Make sure to follow the Merge Sort algorithm exactly.\n\n### Part 2: Strategy Pattern\n\nWe want the application to apply different algorithms for sorting a `List` of `Int` objects.\nUse the strategy pattern to select the right sorting algorithm at runtime.\n\n**You have the following tasks:**\n\n1. [task][SortStrategy Interface](test_sort_strategy_class,test_sort_strategy_methods)\nCreate a `SortStrategy` abstract class with an abstract method and adjust the sorting algorithms so that they inherit from this class.\n\n2. [task][Context Class](test_context_attributes,test_context_methods)\nCreate and implement a `Context` class following the below class diagram\n\n3. [task][Context Policy](test_policy_constructor,test_policy_attributes,test_policy_methods)\nCreate and implement a `Policy` class following the below class diagram with a simple configuration mechanism:\n\n 1. [task][Select MergeSort](test_merge_sort_struct,test_merge_sort_for_big_list)\n Select `MergeSort` when the List has more than 10 dates.\n\n 2. [task][Select BubbleSort](test_bubble_sort_struct,test_bubble_sort_for_small_list)\n Select `BubbleSort` when the List has less or equal 10 dates.\n\n4. Complete the `Client` class which demonstrates switching between two strategies at runtime.\n\n@startuml\n\nclass Client {\n}\n\nclass Policy {\n +configure()\n}\n\nclass Context {\n numbers: List\n +sort()\n}\n\nabstract class SortStrategy {\n +perform_sort(List)\n}\n\nclass BubbleSort {\n +performSort(List)\n}\n\nclass MergeSort {\n +perform_sort(List)\n}\n\nMergeSort -up-|> SortStrategy #testsColor(test_merge_sort_class)\nBubbleSort -up-|> SortStrategy #testsColor(test_bubble_sort_class)\nPolicy -right-> Context #testsColor(test_policy_attributes): context\nContext -right-> SortStrategy #testsColor(test_context_attributes): sortAlgorithm\nClient .down.> Policy\nClient .down.> Context\n\nhide empty fields\nhide empty methods\n\n@enduml\n\n\n### Part 3: Optional Challenges\n\n(These are not tested)\n\n1. Create a new class `QuickSort` that inherits from `SortStrategy` and implement the Quick Sort algorithm.\n\n2. Think about a useful decision in `Policy` when to use the new `QuickSort` algorithm.\n", - "programmingLanguage": "PYTHON", - "secondCorrectionEnabled": false, - "shortName": "", - "showTestNamesToStudents": false, - "solutionParticipation": { - "type": "solution" - }, - "startDateError": false, - "staticCodeAnalysisEnabled": false, - "studentAssignedTeamIdComputed": false, - "teamMode": false, - "templateParticipation": { - "type": "template" - }, - "testwiseCoverageEnabled": false, - "title": "", - "type": "programming" -} diff --git a/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/background.jpg b/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/background.jpg deleted file mode 100644 index b7c357de728b..000000000000 Binary files a/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/background.jpg and /dev/null differ diff --git a/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/question.txt b/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/question.txt deleted file mode 100644 index 955e816cc5df..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/drag_and_drop/question.txt +++ /dev/null @@ -1,2 +0,0 @@ -Who is never going to give you up? - [hint] His first name is Rick diff --git a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/question.txt b/src/test/cypress/fixtures/exercise/quiz/multiple_choice/question.txt deleted file mode 100644 index ee4700fa3a9b..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/question.txt +++ /dev/null @@ -1,14 +0,0 @@ -A longer more detailed question -[hint] A general hint -[correct] Correct answer 1 -[hint] A hint -[exp] Explanation for why this is correct -[correct] Correct answer 2 -[hint] A hint -[exp] Explanation for why this is correct -[wrong] Wrong Answer 1 -[hint] A hint -[exp] Explanation for why this is wrong -[wrong] Wrong Answer 2 -[hint] A hint -[exp] Explanation for why this is wrong diff --git a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/submission.json b/src/test/cypress/fixtures/exercise/quiz/multiple_choice/submission.json deleted file mode 100644 index 5515e12441a9..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/submission.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "submissionExerciseType": "quiz", - "submitted": false, - "submittedAnswers": [ - { - "type": "multiple-choice", - "quizQuestion": null, - "selectedOptions": [] - } - ] -} diff --git a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/template.json b/src/test/cypress/fixtures/exercise/quiz/multiple_choice/template.json deleted file mode 100644 index 288e2447996b..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/multiple_choice/template.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "type": "multiple-choice", - "randomizeOrder": false, - "invalid": false, - "exportQuiz": false, - "title": "", - "text": "A longer more detailed question", - "hint": "A general hint", - "scoringType": "PROPORTIONAL_WITHOUT_PENALTY", - "points": 10, - "answerOptions": [ - { - "isCorrect": true, - "invalid": false, - "text": "Correct answer 1", - "hint": "A hint", - "explanation": "Explanation for why this is correct" - }, - { - "isCorrect": true, - "invalid": false, - "text": "Correct answer 2", - "hint": "A hint", - "explanation": "Explanation for why this is correct" - }, - { - "isCorrect": false, - "invalid": false, - "text": "Wrong Answer 1", - "hint": "A hint", - "explanation": "Explanation for why this is wrong" - }, - { - "isCorrect": false, - "invalid": false, - "text": "Wrong Answer 2", - "hint": "A hint", - "explanation": "Explanation for why this is wrong" - } - ] -} diff --git a/src/test/cypress/fixtures/exercise/quiz/short_answer/question.txt b/src/test/cypress/fixtures/exercise/quiz/short_answer/question.txt deleted file mode 100644 index 2691efde042f..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/short_answer/question.txt +++ /dev/null @@ -1,14 +0,0 @@ -Never gonna [-spot 1] you up -Never gonna [-spot 2] you down -Never gonna [-spot 3] around and [-spot 4] you -Never gonna make you [-spot 5] -Never gonna say [-spot 6] -Never gonna tell a lie and hurt you - - -[-option 1] give -[-option 2] let -[-option 3] run -[-option 4] desert -[-option 5] cry -[-option 6] goodbye diff --git a/src/test/cypress/fixtures/exercise/quiz/short_answer/submission.json b/src/test/cypress/fixtures/exercise/quiz/short_answer/submission.json deleted file mode 100644 index dc62a419a4b8..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/short_answer/submission.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "submissionExerciseType": "quiz", - "submitted": false, - "submittedAnswers": [ - { - "type": "short-answer", - "quizQuestion": null, - "submittedTexts": [] - } - ] -} diff --git a/src/test/cypress/fixtures/exercise/quiz/short_answer/template.json b/src/test/cypress/fixtures/exercise/quiz/short_answer/template.json deleted file mode 100644 index adb85d9280df..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/short_answer/template.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "type": "short-answer", - "randomizeOrder": true, - "invalid": false, - "exportQuiz": false, - "matchLetterCase": false, - "similarityValue": 85, - "title": "Short Answer Quiz", - "text": "Never gonna [-spot 1] you up\nNever gonna [-spot 2] you down\nNever gonna [-spot 3] around and [-spot 4] you\nNever gonna make you [-spot 5]\nNever gonna say [-spot 6]\nNever gonna tell a lie and hurt you", - "scoringType": "PROPORTIONAL_WITHOUT_PENALTY", - "points": 1, - "spots": [ - { - "tempID": 8562027009747859, - "invalid": false, - "width": 15, - "spotNr": 1 - }, - { - "tempID": 1693282244205775, - "invalid": false, - "width": 15, - "spotNr": 2 - }, - { - "tempID": 2470277842944981, - "invalid": false, - "width": 15, - "spotNr": 3 - }, - { - "tempID": 1444701769469013, - "invalid": false, - "width": 15, - "spotNr": 4 - }, - { - "tempID": 3791222413897993, - "invalid": false, - "width": 15, - "spotNr": 5 - }, - { - "tempID": 1633040437192643, - "invalid": false, - "width": 15, - "spotNr": 6 - } - ], - "solutions": [ - { - "tempID": 7036040666954049, - "invalid": false, - "text": "give" - }, - { - "tempID": 94143448556475, - "invalid": false, - "text": "let" - }, - { - "tempID": 5696516478778027, - "invalid": false, - "text": "run" - }, - { - "tempID": 3116087876447447, - "invalid": false, - "text": "desert" - }, - { - "tempID": 8350692113124205, - "invalid": false, - "text": "cry" - }, - { - "tempID": 1934608439373183, - "invalid": false, - "text": "goodbye" - } - ], - "correctMappings": [ - { - "spot": { - "tempID": 8562027009747859, - "invalid": false, - "width": 15, - "spotNr": 1 - }, - "solution": { - "tempID": 7036040666954049, - "invalid": false, - "text": "give" - }, - "invalid": false - }, - { - "spot": { - "tempID": 1693282244205775, - "invalid": false, - "width": 15, - "spotNr": 2 - }, - "solution": { - "tempID": 94143448556475, - "invalid": false, - "text": "let" - }, - "invalid": false - }, - { - "spot": { - "tempID": 2470277842944981, - "invalid": false, - "width": 15, - "spotNr": 3 - }, - "solution": { - "tempID": 5696516478778027, - "invalid": false, - "text": "run" - }, - "invalid": false - }, - { - "spot": { - "tempID": 1444701769469013, - "invalid": false, - "width": 15, - "spotNr": 4 - }, - "solution": { - "tempID": 3116087876447447, - "invalid": false, - "text": "desert" - }, - "invalid": false - }, - { - "spot": { - "tempID": 3791222413897993, - "invalid": false, - "width": 15, - "spotNr": 5 - }, - "solution": { - "tempID": 8350692113124205, - "invalid": false, - "text": "cry" - }, - "invalid": false - }, - { - "spot": { - "tempID": 1633040437192643, - "invalid": false, - "width": 15, - "spotNr": 6 - }, - "solution": { - "tempID": 1934608439373183, - "invalid": false, - "text": "goodbye" - }, - "invalid": false - } - ] -} diff --git a/src/test/cypress/fixtures/exercise/quiz/template.json b/src/test/cypress/fixtures/exercise/quiz/template.json deleted file mode 100644 index b487d5d1c09a..000000000000 --- a/src/test/cypress/fixtures/exercise/quiz/template.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "mode": "INDIVIDUAL", - "includedInOverallScore": "INCLUDED_COMPLETELY", - "numberOfAssessmentsOfCorrectionRounds": [{ "inTime": 0, "late": 0, "total": 0 }], - "studentAssignedTeamIdComputed": false, - "secondCorrectionEnabled": false, - "type": "quiz", - "bonusPoints": 0, - "isAtLeastTutor": false, - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "teamMode": false, - "assessmentDueDateError": false, - "dueDateError": false, - "presentationScoreEnabled": false, - "randomizeQuestionOrder": true, - "releaseDate": null, - "isVisibleBeforeStart": false, - "isOpenForPractice": false, - "isPlannedToStart": false, - "isActiveQuiz": false, - "isPracticeModeAvailable": true, - "title": "", - "duration": 600, - "quizQuestions": [], - "quizMode": "SYNCHRONIZED" -} diff --git a/src/test/cypress/fixtures/exercise/text/submission.json b/src/test/cypress/fixtures/exercise/text/submission.json deleted file mode 100644 index 4e7acc5e5242..000000000000 --- a/src/test/cypress/fixtures/exercise/text/submission.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "text": "On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish." -} diff --git a/src/test/cypress/fixtures/exercise/text/template.json b/src/test/cypress/fixtures/exercise/text/template.json deleted file mode 100644 index 9a185cec3058..000000000000 --- a/src/test/cypress/fixtures/exercise/text/template.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "mode": "INDIVIDUAL", - "includedInOverallScore": "INCLUDED_COMPLETELY", - "numberOfAssessmentsOfCorrectionRounds": [ - { - "inTime": 0, - "late": 0 - } - ], - "studentAssignedTeamIdComputed": false, - "secondCorrectionEnabled": false, - "type": "text", - "bonusPoints": 0, - "isAtLeastTutor": false, - "isAtLeastEditor": false, - "isAtLeastInstructor": false, - "teamMode": false, - "assessmentDueDateError": false, - "dueDateError": false, - "exampleSolutionPublicationDateError": false, - "exampleSolutionPublicationDateWarning": false, - "presentationScoreEnabled": false, - "assessmentType": "MANUAL", - "title": "Text Exercise 1", - "maxPoints": 10, - "problemStatement": "Problem Statement", - "exampleSolution": "Example Solution", - "gradingInstructions": "Assessment Instructions" -} diff --git a/src/test/cypress/fixtures/lecture/template.json b/src/test/cypress/fixtures/lecture/template.json deleted file mode 100644 index 4c540b3d11f2..000000000000 --- a/src/test/cypress/fixtures/lecture/template.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "course": null, - "title": "Test lecture", - "channelName": "lecture-test-lecture", - "description": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", - "startDate": null, - "endDate": null -} diff --git a/src/test/cypress/fixtures/loremIpsum-short.txt b/src/test/cypress/fixtures/loremIpsum-short.txt deleted file mode 100644 index b9c7a9bd3d14..000000000000 --- a/src/test/cypress/fixtures/loremIpsum-short.txt +++ /dev/null @@ -1 +0,0 @@ -At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren. \ No newline at end of file diff --git a/src/test/cypress/fixtures/loremIpsum.txt b/src/test/cypress/fixtures/loremIpsum.txt deleted file mode 100644 index b45a9d5eccd8..000000000000 --- a/src/test/cypress/fixtures/loremIpsum.txt +++ /dev/null @@ -1 +0,0 @@ -Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. \ No newline at end of file diff --git a/src/test/cypress/fixtures/pdf-test-file.pdf b/src/test/cypress/fixtures/pdf-test-file.pdf deleted file mode 100644 index e60c959ec0ba..000000000000 Binary files a/src/test/cypress/fixtures/pdf-test-file.pdf and /dev/null differ diff --git a/src/test/cypress/init/ImportUsers.cy.ts b/src/test/cypress/init/ImportUsers.cy.ts deleted file mode 100644 index 0e141f269d4c..000000000000 --- a/src/test/cypress/init/ImportUsers.cy.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { userManagementAPIRequest } from '../support/artemis'; -import { USER_ID, USER_ROLE, admin, instructor, studentFour, studentOne, studentThree, studentTwo, tutor, users } from '../support/users'; - -describe('Setup users', () => { - if (Cypress.env('createUsers')) { - before('Creates all required users', () => { - cy.login(admin); - for (const userKey in USER_ID) { - const user = users.getUserWithId(USER_ID[userKey]); - userManagementAPIRequest.getUser(user.username).then((response) => { - if (!response.isOkStatusCode) { - userManagementAPIRequest.createUser(user.username, user.password, USER_ROLE[userKey]); - } - }); - } - }); - } - - it('Logs in once with all required users', () => { - // If Artemis hasn't imported the required users from Jira we have to force this by logging in with these users once - cy.login(admin); - cy.login(instructor); - cy.login(tutor); - cy.login(studentOne); - cy.login(studentTwo); - cy.login(studentThree); - cy.login(studentFour); - }); -}); diff --git a/src/test/cypress/package-lock.json b/src/test/cypress/package-lock.json deleted file mode 100644 index 1b660ab0208e..000000000000 --- a/src/test/cypress/package-lock.json +++ /dev/null @@ -1,3222 +0,0 @@ -{ - "name": "artemis_cypress", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "artemis_cypress", - "license": "MIT", - "devDependencies": { - "@4tw/cypress-drag-drop": "2.2.5", - "@types/node": "20.9.1", - "cypress": "12.17.4", - "cypress-cloud": "2.0.0-beta.1", - "cypress-file-upload": "5.0.8", - "cypress-pipe": "2.0.0", - "cypress-wait-until": "2.0.1", - "typescript": "5.2.2", - "uuid": "9.0.1", - "wait-on": "7.2.0" - } - }, - "node_modules/@4tw/cypress-drag-drop": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.2.5.tgz", - "integrity": "sha512-3ghTmzhOmUqeN6U3QmUnKRUxI7OMLbJA4hHUY/eS/FhWJgxbiGgcaELbolWnBAOpajPXcsNQGYEj9brd59WH6A==", - "dev": true, - "peerDependencies": { - "cypress": "2 - 13" - } - }, - "node_modules/@babel/runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", - "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cypress/commit-info": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cypress/commit-info/-/commit-info-2.2.0.tgz", - "integrity": "sha512-A7CYS0Iqp/u52JTnSWlDFjWMKx7rIfd+mk0Fdksrcs4Wdf5HXPsoZO475VJ+xL7LPhJrjKhgyl/TPKO3worZyQ==", - "dev": true, - "dependencies": { - "bluebird": "3.5.5", - "check-more-types": "2.24.0", - "debug": "4.1.1", - "execa": "1.0.0", - "lazy-ass": "1.6.0", - "ramda": "0.26.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@cypress/commit-info/node_modules/bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", - "dev": true - }, - "node_modules/@cypress/commit-info/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/@cypress/commit-info/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@cypress/commit-info/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@cypress/commit-info/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@cypress/commit-info/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@cypress/commit-info/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@cypress/commit-info/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@cypress/commit-info/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@cypress/commit-info/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "6.10.4", - "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@cypress/request/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "dependencies": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "dev": true - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "dev": true - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz", - "integrity": "sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "node_modules/@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios-retry": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.7.0.tgz", - "integrity": "sha512-ZTnCkJbRtfScvwiRnoVskFAfvU0UG3xNcsjwTR0mawSbIJoothxn67gKsMaNAFHRXJ1RmuLhmZBzvyXi3+9WyQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/axios/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cy2": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/cy2/-/cy2-3.4.3.tgz", - "integrity": "sha512-I1yfJWJTRy2ROti1TlLM5Qk86WSeKMrtZbY/G6VD2tjm3VKTu6pDkpcV56C2HhN+txK5p6MMsmzKXJM2W9JlxA==", - "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "debug": "^4.3.2", - "escodegen": "^2.0.0", - "estraverse": "^5.3.0", - "js-yaml": "^4.1.0", - "npm-which": "^3.0.1", - "slash": "3.0.0" - }, - "bin": { - "cy2": "bin/cy2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/cypress": { - "version": "12.17.4", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.4.tgz", - "integrity": "sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@cypress/request": "2.88.12", - "@cypress/xvfb": "^1.2.4", - "@types/node": "^16.18.39", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.6.0", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.0", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" - } - }, - "node_modules/cypress-cloud": { - "version": "2.0.0-beta.1", - "resolved": "https://registry.npmjs.org/cypress-cloud/-/cypress-cloud-2.0.0-beta.1.tgz", - "integrity": "sha512-nMKf7077NaOK4AFHUwYGAnL3HtgTWsyQ+dSB4YxSH0GvQbtJo7Ljk0dlkzkUraiPb+0/Rr+XF0ozorSPz7ChJw==", - "dev": true, - "dependencies": { - "@cypress/commit-info": "^2.2.0", - "axios": "^1.2.0", - "axios-retry": "^3.4.0", - "bluebird": "^3.7.2", - "chalk": "^4.1.2", - "commander": "^10.0.0", - "common-path-prefix": "^3.0.0", - "cy2": "^3.4.2", - "date-fns": "^2.30.0", - "debug": "^4.3.4", - "execa": "^5.1.1", - "fast-safe-stringify": "^2.1.1", - "getos": "^3.2.1", - "globby": "^11.1.0", - "is-absolute": "^1.0.0", - "lil-http-terminator": "^1.2.3", - "lodash": "^4.17.21", - "nanoid": "^3.3.4", - "plur": "^4.0.0", - "pretty-ms": "^7.0.1", - "source-map-support": "^0.5.21", - "table": "^6.8.1", - "tmp-promise": "^3.0.3", - "ts-pattern": "^4.3.0", - "ws": "^8.13.0" - }, - "bin": { - "cypress-cloud": "bin/cli.js" - }, - "engines": { - "node": ">=14.7.0" - } - }, - "node_modules/cypress-cloud/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/cypress-cloud/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/cypress-cloud/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cypress-cloud/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.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/cypress-pipe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cypress-pipe/-/cypress-pipe-2.0.0.tgz", - "integrity": "sha512-KW9s+bz4tFLucH3rBGfjW+Q12n7S4QpUSSyxiGrgPOfoHlbYWzAGB3H26MO0VTojqf9NVvfd5Kt0MH5XMgbfyg==", - "dev": true - }, - "node_modules/cypress-wait-until": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cypress-wait-until/-/cypress-wait-until-2.0.1.tgz", - "integrity": "sha512-+IyVnYNiaX1+C+V/LazrJWAi/CqiwfNoRSrFviECQEyolW1gDRy765PZosL2alSSGK8V10Y7BGfOQyZUDgmnjQ==", - "dev": true - }, - "node_modules/cypress/node_modules/@types/node": { - "version": "16.18.54", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz", - "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/dayjs": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", - "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true - }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "dependencies": { - "async": "^3.2.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/irregular-plurals": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", - "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dev": true, - "dependencies": { - "is-unc-path": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dev": true, - "dependencies": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", - "dev": true, - "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", - "dev": true, - "engines": { - "node": "> 0.8" - } - }, - "node_modules/lil-http-terminator": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/lil-http-terminator/-/lil-http-terminator-1.2.3.tgz", - "integrity": "sha512-vQcHSwAFq/kTR2cG6peOVS7SjgksGgSPeH0G2lkw+buue33thE/FCHdn10wJXXshc5RswFy0Iaz48qA2Busw5Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/npm-path": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", - "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", - "dev": true, - "dependencies": { - "which": "^1.2.10" - }, - "bin": { - "npm-path": "bin/npm-path" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm-path/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm-which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", - "integrity": "sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A==", - "dev": true, - "dependencies": { - "commander": "^2.9.0", - "npm-path": "^2.0.2", - "which": "^1.2.10" - }, - "bin": { - "npm-which": "bin/npm-which.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/npm-which/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/npm-which/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plur": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", - "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", - "dev": true, - "dependencies": { - "irregular-plurals": "^3.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "dev": true, - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", - "dev": true - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dev": true, - "dependencies": { - "tmp": "^0.2.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/ts-pattern": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz", - "integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==", - "dev": true - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/wait-on": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", - "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", - "dev": true, - "dependencies": { - "axios": "^1.6.1", - "joi": "^17.11.0", - "lodash": "^4.17.21", - "minimist": "^1.2.8", - "rxjs": "^7.8.1" - }, - "bin": { - "wait-on": "bin/wait-on" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/src/test/cypress/package.json b/src/test/cypress/package.json deleted file mode 100644 index 88a277ab0820..000000000000 --- a/src/test/cypress/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "artemis_cypress", - "description": "Cypress tests for Artemis", - "private": true, - "license": "MIT", - "cacheDirectories": [ - "node_modules" - ], - "devDependencies": { - "@4tw/cypress-drag-drop": "2.2.5", - "@types/node": "20.9.1", - "cypress": "12.17.4", - "cypress-cloud": "2.0.0-beta.1", - "cypress-file-upload": "5.0.8", - "cypress-wait-until": "2.0.1", - "cypress-pipe": "2.0.0", - "typescript": "5.2.2", - "uuid": "9.0.1", - "wait-on": "7.2.0" - }, - "overrides": { - "semver": "7.5.3", - "word-wrap": "1.2.3", - "debug": "4.3.4", - "tough-cookie": "4.1.3", - "@cypress/request": "3.0.1" - }, - "scripts": { - "cypress:open": "cypress open", - "cypress:run": "cypress run --browser=chrome", - "cypress:setup": "cypress install && cypress run --quiet --spec init/ImportUsers.cy.ts", - "cypress:record:mysql": "npx cypress-cloud run --parallel --record --ci-build-id \"${SORRY_CYPRESS_BRANCH_NAME} #${SORRY_CYPRESS_BUILD_ID} ${SORRY_CYPRESS_RERUN_COUNT} (MySQL)\"", - "cypress:record:postgres": "npx cypress-cloud run --parallel --record --ci-build-id \"${SORRY_CYPRESS_BRANCH_NAME} #${SORRY_CYPRESS_BUILD_ID} ${SORRY_CYPRESS_RERUN_COUNT} (Postgres)\"", - "cypress:record:local": "npx cypress-cloud run --parallel --record --ci-build-id \"${SORRY_CYPRESS_BRANCH_NAME} #${SORRY_CYPRESS_BUILD_ID} ${SORRY_CYPRESS_RERUN_COUNT} (Local)\"", - "update": "ncu -i --format group" - } -} diff --git a/src/test/cypress/support/artemis.ts b/src/test/cypress/support/artemis.ts deleted file mode 100644 index c23be9249317..000000000000 --- a/src/test/cypress/support/artemis.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { ArtemisPageobjects } from './pageobjects/ArtemisPageobjects'; -import { ArtemisRequests } from './requests/ArtemisRequests'; - -/** - * File which contains all shared code related to testing Artemis. - */ - -// Requests -const requests = new ArtemisRequests(); -export const communicationAPIRequest = requests.communication; -export const courseManagementAPIRequest = requests.courseManagement; -export const examAPIRequests = requests.exam; -export const exerciseAPIRequest = requests.exercise; -export const userManagementAPIRequest = requests.userManagement; - -// PageObjects -const pageObjects = new ArtemisPageobjects(); - -export const loginPage = pageObjects.login; - -export const courseCreation = pageObjects.course.creation; -export const courseList = pageObjects.course.list; -export const courseOverview = pageObjects.course.overview; -export const courseManagement = pageObjects.course.management; -export const courseManagementExercises = pageObjects.course.managementExercises; -export const courseMessages = pageObjects.course.messages; -export const courseAssessment = pageObjects.assessment.course; - -export const lectureCreation = pageObjects.lecture.creation; -export const lectureManagement = pageObjects.lecture.management; - -export const navigationBar = pageObjects.navigationBar; - -export const examCreation = pageObjects.exam.creation; -export const examManagement = pageObjects.exam.management; -export const examDetails = pageObjects.exam.details; -export const examAssessment = pageObjects.assessment.exam; -export const examNavigation = pageObjects.exam.navigationBar; -export const examStartEnd = pageObjects.exam.startEnd; -export const examExerciseGroupCreation = pageObjects.exam.exerciseGroupCreation; -export const examExerciseGroups = pageObjects.exam.exerciseGroups; -export const examParticipation = pageObjects.exam.participation; -export const examTestRun = pageObjects.exam.testRun; -export const studentExamManagement = pageObjects.exam.studentExamManagement; - -export const studentAssessment = pageObjects.assessment.student; - -export const exerciseAssessment = pageObjects.assessment.exercise; -export const exerciseResult = pageObjects.exercise.result; - -export const textExerciseCreation = pageObjects.exercise.text.creation; -export const textExerciseEditor = pageObjects.exercise.text.editor; -export const textExerciseAssessment = pageObjects.assessment.text; -export const textExerciseFeedback = pageObjects.exercise.text.feedback; -export const textExerciseExampleSubmissionCreation = pageObjects.exercise.text.exampleSubmissionCreation; -export const textExerciseExampleSubmissions = pageObjects.exercise.text.exampleSubmissions; - -export const modelingExerciseCreation = pageObjects.exercise.modeling.creation; -export const modelingExerciseEditor = pageObjects.exercise.modeling.editor; -export const modelingExerciseAssessment = pageObjects.assessment.modeling; -export const modelingExerciseFeedback = pageObjects.exercise.modeling.feedback; - -export const programmingExerciseCreation = pageObjects.exercise.programming.creation; -export const programmingExerciseEditor = pageObjects.exercise.programming.editor; -export const programmingExerciseAssessment = pageObjects.assessment.programming; -export const programmingExerciseFeedback = pageObjects.exercise.programming.feedback; -export const programmingExercisesScaConfig = pageObjects.exercise.programming.scaConfiguration; -export const programmingExerciseScaFeedback = pageObjects.exercise.programming.scaFeedback; - -export const quizExerciseCreation = pageObjects.exercise.quiz.creation; -export const quizExerciseMultipleChoice = pageObjects.exercise.quiz.multipleChoice; -export const quizExerciseShortAnswerQuiz = pageObjects.exercise.quiz.shortAnswer; -export const quizExerciseDragAndDropQuiz = pageObjects.exercise.quiz.dragAndDrop; - -export const fileUploadExerciseCreation = pageObjects.exercise.fileUpload.creation; -export const fileUploadExerciseEditor = pageObjects.exercise.fileUpload.editor; -export const fileUploadExerciseAssessment = pageObjects.assessment.fileUpload; -export const fileUploadExerciseFeedback = pageObjects.exercise.fileUpload.feedback; diff --git a/src/test/cypress/support/commands.ts b/src/test/cypress/support/commands.ts deleted file mode 100644 index d2bf385fe315..000000000000 --- a/src/test/cypress/support/commands.ts +++ /dev/null @@ -1,156 +0,0 @@ -// *********************************************** -// 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 -// *********************************************** -// -// -// -- 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) => { ... }) - -import { BASE_API, POST } from './constants'; -import { CypressCredentials } from './users'; - -export {}; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - interface Chainable { - login(credentials: CypressCredentials, url?: string): any; - loginWithGUI(credentials: CypressCredentials): any; - getSettled(selector: string, options?: any): Chainable; - reloadUntilFound(selector: string, interval?: number, timeout?: number): Chainable; - formRequest(url: string, method: string, formData: FormData): Chainable; - } - } -} - -/** - * Logs in using API - * */ -Cypress.Commands.add('login', (credentials: CypressCredentials, url) => { - const username = credentials.username; - const password = credentials.password; - - cy.session( - username, - () => { - // IMPORTANT: The "log" and "failOnStatusCode" fields need to be set to false to prevent leakage of the credentials via the Cypress Dashboard! - // log = false does not prevent cypress to log the request if it failed, so failOnStatusCode also needs to be set to false, so that the request is never logged. - // We still want to the test to fail if the authentication is unsuccessful, so we expect the status code in the then block. This only logs the status code, so it is safe. - cy.request({ - url: `${BASE_API}/public/authenticate`, - method: POST, - followRedirect: true, - body: { - username, - password, - rememberMe: true, - }, - log: false, - failOnStatusCode: false, - }).then((response) => { - expect(response.status).to.equal(200); - }); - }, - { - validate: () => { - cy.getCookie('jwt', { log: false }).should('exist'); - }, - cacheAcrossSpecs: true, - }, - ); - if (url) { - cy.visit(url); - } -}); - -/** recursively gets an element, returning only after it's determined to be attached to the DOM for good - * this prevents the "Element is detached from DOM" issue in some cases - */ -Cypress.Commands.add('getSettled', (selector: any, opts: { retries?: number; delay?: number } = {}) => { - const retries = opts.retries || 3; - const delay = opts.delay || 100; - - const isAttached = (resolve: any, count = 0) => { - const el = Cypress.$(selector); - - // is element attached to the DOM? - count = Cypress.dom.isAttached(el) ? count + 1 : 0; - - // hit our base case, return the element - if (count >= retries) { - return resolve(el); - } - - // retry after a bit of a delay - setTimeout(() => isAttached(resolve, count), delay); - }; - - // wrap, so we can chain cypress commands off the result - return cy.wrap(null).then(() => { - return new Cypress.Promise((resolve) => { - return isAttached(resolve, 0); - }).then((el) => { - return cy.wrap(el); - }); - }); -}); - -/** - * Periodically refreshes the page until an element with the specified selector is found. The command fails if the time exceeds the timeout. - */ -Cypress.Commands.add('reloadUntilFound', (selector: string, interval = 2000, timeout = 20000) => { - return cy.waitUntil( - () => { - const found = Cypress.$(selector).length > 0; - if (!found) { - cy.reload(); - } - return found; - }, - { - interval, - timeout, - errorMsg: `Timed out finding an element matching the "${selector}" selector`, - }, - ); -}); - -Cypress.Commands.add('formRequest', (url: string, method: string, formData: FormData) => { - return cy - .intercept(method, url) - .as('formRequest') - .window() - .then((win) => { - const xhr = new win.XMLHttpRequest(); - xhr.open(method, url); - const token = localStorage.getItem('authTokenKey')?.replace(/"/g, ''); - if (token) { - const authHeader = 'Bearer ' + token; - xhr.setRequestHeader('Authorization', authHeader); - } - xhr.send(formData); - }) - .wait('@formRequest') - .then((xhr: any) => { - return cy.wrap({ status: xhr.status, body: xhr.response.body }); - }); -}); diff --git a/src/test/cypress/support/constants.ts b/src/test/cypress/support/constants.ts deleted file mode 100644 index 97ee7440e57b..000000000000 --- a/src/test/cypress/support/constants.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { ExerciseGroup } from 'app/entities/exercise-group.model'; - -import { ProgrammingExerciseSubmission } from './pageobjects/exercises/programming/OnlineEditorPage'; - -// Constants which are used in the test specs - -// Requests -export const DELETE = 'DELETE'; -export const POST = 'POST'; -export const GET = 'GET'; -export const PUT = 'PUT'; -export const PATCH = 'PATCH'; -export const BASE_API = 'api'; -export const EXERCISE_BASE = `${BASE_API}/exercises`; - -export const COURSE_BASE = `${BASE_API}/courses`; -export const COURSE_ADMIN_BASE = `${BASE_API}/admin/courses`; - -export const PROGRAMMING_EXERCISE_BASE = `${BASE_API}/programming-exercises`; -export const QUIZ_EXERCISE_BASE = `${BASE_API}/quiz-exercises`; -export const TEXT_EXERCISE_BASE = `${BASE_API}/text-exercises`; -export const MODELING_EXERCISE_BASE = `${BASE_API}/modeling-exercises`; -export const UPLOAD_EXERCISE_BASE = `${BASE_API}/file-upload-exercises`; - -// Constants -export const USER_ID_SELECTOR = 'USERID'; -export const MODELING_EDITOR_CANVAS = '#modeling-editor-canvas'; - -// Timeformat -export const TIME_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSS'; - -// ExerciseType -// Copied from app/entities/exercise.model -export enum ExerciseType { - PROGRAMMING = 'programming', - MODELING = 'modeling', - QUIZ = 'quiz', - TEXT = 'text', - FILE_UPLOAD = 'file-upload', -} - -// ProgrammingLanguage -// Copied from app/entities/programming-exercise.model -export enum ProgrammingLanguage { - JAVA = 'JAVA', - PYTHON = 'PYTHON', - C = 'C', - HASKELL = 'HASKELL', - KOTLIN = 'KOTLIN', - VHDL = 'VHDL', - ASSEMBLER = 'ASSEMBLER', - SWIFT = 'SWIFT', - OCAML = 'OCAML', - EMPTY = 'EMPTY', -} - -// ProgrammingExerciseAssessmentType -export enum ProgrammingExerciseAssessmentType { - AUTOMATIC, - SEMI_AUTOMATIC, - MANUAL, -} - -// AdditionalData -export class AdditionalData { - quizExerciseID?: number; - submission?: ProgrammingExerciseSubmission; - expectedScore?: number; - textFixture?: string; - practiceMode?: boolean; - progExerciseAssessmentType?: ProgrammingExerciseAssessmentType; -} - -// Exercise -export type Exercise = { - title: string; - type: ExerciseType; - id: number; - additionalData?: AdditionalData; - exerciseGroup?: ExerciseGroup; -}; diff --git a/src/test/cypress/support/index.ts b/src/test/cypress/support/index.ts deleted file mode 100644 index 18be87965603..000000000000 --- a/src/test/cypress/support/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -// *********************************************************** -// This example support/index.js 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: - -// https://github.com/4teamwork/cypress-drag-drop#options adds .drag and .move commands -import '@4tw/cypress-drag-drop'; -// Imports file upload capabilities https://github.com/abramenal/cypress-file-upload -import 'cypress-file-upload'; -// Imports cy.waitUntil https://github.com/NoriSte/cypress-wait-until -import 'cypress-wait-until'; - -import './commands'; -// Imports utility functions -import './utils'; -// Imports cy.pipe https://github.com/NicholasBoll/cypress-pipe -import 'cypress-pipe'; - -/** - * We register hooks on the console.error and console.warn methods and forward their content to the process console to allow better debugging with cypress:run. - */ -/*eslint-disable */ -Cypress.on('window:before:load', (win) => { - cy.stub(win.console, 'error').callsFake((msg) => { - cy.now('task', 'error', msg); - }); - - cy.stub(win.console, 'warn').callsFake((msg) => { - cy.now('task', 'warn', msg); - }); -}); - -/** - * We have to disable all service workers because the test will fail with a security exception and translations will also not be resolved properly otherwise. - * For some reason this does not work when I add it to the hook above. - */ -Cypress.on('window:before:load', (win) => { - delete win.navigator.__proto__.serviceWorker; -}); - -/** - * On development environments (i.e. local environments) sentry is disabled and produces error messages in the console that cause cypress to fail. - * This call tells cypress to ignore errors related to sentry - */ -Cypress.on('uncaught:exception', (err, runnable) => { - return err.name === 'TypeError' && err?.stack?.includes('sentry'); -}); - -/** - * We want to log our test retries to the console to be able to see how often a test is retried. - */ -const config: any = Cypress.config(); -if (!config.isInteractive && config.reporter !== 'spec') { - afterEach(() => { - const test = (cy as any).state('runnable')?.ctx?.currentTest; - if (test?.state === 'failed' && test?._currentRetry < test?._retries) { - cy.task('log', ` RERUN: (Attempt ${test._currentRetry + 1} of ${test._retries + 1}) ${test.title}`, { log: false }); - } - }); -} -/*eslint-enable */ diff --git a/src/test/cypress/support/pageobjects/ArtemisPageobjects.ts b/src/test/cypress/support/pageobjects/ArtemisPageobjects.ts deleted file mode 100644 index 1e2bc2b56b82..000000000000 --- a/src/test/cypress/support/pageobjects/ArtemisPageobjects.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { LoginPage } from './LoginPage'; -import { NavigationBar } from './NavigationBar'; -import { CourseAssessmentDashboardPage } from './assessment/CourseAssessmentDashboardPage'; -import { ExamAssessmentPage } from './assessment/ExamAssessmentPage'; -import { ExerciseAssessmentDashboardPage } from './assessment/ExerciseAssessmentDashboardPage'; -import { FileUploadExerciseAssessmentPage } from './assessment/FileUploadExerciseAssessmentPage'; -import { ModelingExerciseAssessmentEditor } from './assessment/ModelingExerciseAssessmentEditor'; -import { ProgrammingExerciseAssessmentPage } from './assessment/ProgrammingExerciseAssessmentPage'; -import { StudentAssessmentPage } from './assessment/StudentAssessmentPage'; -import { TextExerciseAssessmentPage } from './assessment/TextExerciseAssessmentPage'; -import { CourseCreationPage } from './course/CourseCreationPage'; -import { CourseManagementExercisesPage } from './course/CourseManagementExercisesPage'; -import { CourseManagementPage } from './course/CourseManagementPage'; -import { CourseMessagesPage } from './course/CourseMessages'; -import { CourseOverviewPage } from './course/CourseOverviewPage'; -import { CoursesPage } from './course/CoursesPage'; -import { ExamCreationPage } from './exam/ExamCreationPage'; -import { ExamDetailsPage } from './exam/ExamDetailsPage'; -import { ExamExerciseGroupCreationPage } from './exam/ExamExerciseGroupCreationPage'; -import { ExamExerciseGroupsPage } from './exam/ExamExerciseGroupsPage'; -import { ExamManagementPage } from './exam/ExamManagementPage'; -import { ExamNavigationBar } from './exam/ExamNavigationBar'; -import { ExamParticipation } from './exam/ExamParticipation'; -import { ExamStartEndPage } from './exam/ExamStartEndPage'; -import { ExamTestRunPage } from './exam/ExamTestRunPage'; -import { StudentExamManagementPage } from './exam/StudentExamManagementPage'; -import { ExerciseResultPage } from './exercises/ExerciseResultPage'; -import { FileUploadEditorPage } from './exercises/file-upload/FileUploadEditorPage'; -import { FileUploadExerciseCreationPage } from './exercises/file-upload/FileUploadExerciseCreationPage'; -import { FileUploadExerciseFeedbackPage } from './exercises/file-upload/FileUploadExerciseFeedbackPage'; -import { CreateModelingExercisePage } from './exercises/modeling/CreateModelingExercisePage'; -import { ModelingEditor } from './exercises/modeling/ModelingEditor'; -import { ModelingExerciseFeedbackPage } from './exercises/modeling/ModelingExerciseFeedbackPage'; -import { CodeAnalysisGradingPage } from './exercises/programming/CodeAnalysisGradingPage'; -import { OnlineEditorPage } from './exercises/programming/OnlineEditorPage'; -import { ProgrammingExerciseCreationPage } from './exercises/programming/ProgrammingExerciseCreationPage'; -import { ProgrammingExerciseFeedbackPage } from './exercises/programming/ProgrammingExerciseFeedbackPage'; -import { ScaFeedbackModal } from './exercises/programming/ScaFeedbackModal'; -import { DragAndDropQuiz } from './exercises/quiz/DragAndDropQuiz'; -import { MultipleChoiceQuiz } from './exercises/quiz/MultipleChoiceQuiz'; -import { QuizExerciseCreationPage } from './exercises/quiz/QuizExerciseCreationPage'; -import { ShortAnswerQuiz } from './exercises/quiz/ShortAnswerQuiz'; -import { TextEditorPage } from './exercises/text/TextEditorPage'; -import { TextExerciseCreationPage } from './exercises/text/TextExerciseCreationPage'; -import { TextExerciseExampleSubmissionCreationPage } from './exercises/text/TextExerciseExampleSubmissionCreationPage'; -import { TextExerciseExampleSubmissionsPage } from './exercises/text/TextExerciseExampleSubmissionsPage'; -import { TextExerciseFeedbackPage } from './exercises/text/TextExerciseFeedbackPage'; -import { LectureCreationPage } from './lecture/LectureCreationPage'; -import { LectureManagementPage } from './lecture/LectureManagementPage'; - -/** - * A class which encapsulates all pageobjects, which can be used to automate the Artemis UI. - */ -export class ArtemisPageobjects { - login = new LoginPage(); - navigationBar = new NavigationBar(); - course = { - creation: new CourseCreationPage(), - management: new CourseManagementPage(), - managementExercises: new CourseManagementExercisesPage(), - list: new CoursesPage(), - overview: new CourseOverviewPage(), - messages: new CourseMessagesPage(), - }; - exam = { - details: new ExamDetailsPage(), - creation: new ExamCreationPage(), - management: new ExamManagementPage(), - participation: new ExamParticipation(), - startEnd: new ExamStartEndPage(), - navigationBar: new ExamNavigationBar(), - exerciseGroups: new ExamExerciseGroupsPage(), - exerciseGroupCreation: new ExamExerciseGroupCreationPage(), - studentExamManagement: new StudentExamManagementPage(), - testRun: new ExamTestRunPage(), - }; - exercise = { - result: new ExerciseResultPage(), - programming: { - editor: new OnlineEditorPage(), - creation: new ProgrammingExerciseCreationPage(), - feedback: new ProgrammingExerciseFeedbackPage(), - scaConfiguration: new CodeAnalysisGradingPage(), - scaFeedback: new ScaFeedbackModal(), - }, - text: { - creation: new TextExerciseCreationPage(), - exampleSubmissions: new TextExerciseExampleSubmissionsPage(), - exampleSubmissionCreation: new TextExerciseExampleSubmissionCreationPage(), - editor: new TextEditorPage(), - feedback: new TextExerciseFeedbackPage(), - }, - modeling: { - creation: new CreateModelingExercisePage(), - editor: new ModelingEditor(), - feedback: new ModelingExerciseFeedbackPage(), - }, - quiz: { - creation: new QuizExerciseCreationPage(), - multipleChoice: new MultipleChoiceQuiz(), - shortAnswer: new ShortAnswerQuiz(), - dragAndDrop: new DragAndDropQuiz(), - }, - fileUpload: { - creation: new FileUploadExerciseCreationPage(), - editor: new FileUploadEditorPage(), - feedback: new FileUploadExerciseFeedbackPage(), - }, - }; - assessment = { - exam: new ExamAssessmentPage(), - course: new CourseAssessmentDashboardPage(), - exercise: new ExerciseAssessmentDashboardPage(), - text: new TextExerciseAssessmentPage(), - programming: new ProgrammingExerciseAssessmentPage(), - modeling: new ModelingExerciseAssessmentEditor(), - fileUpload: new FileUploadExerciseAssessmentPage(), - student: new StudentAssessmentPage(), - }; - lecture = { - management: new LectureManagementPage(), - creation: new LectureCreationPage(), - }; -} diff --git a/src/test/cypress/support/pageobjects/LoginPage.ts b/src/test/cypress/support/pageobjects/LoginPage.ts deleted file mode 100644 index d7383b7b2372..000000000000 --- a/src/test/cypress/support/pageobjects/LoginPage.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { CypressCredentials } from '../users'; - -/** - * A class which encapsulates UI selectors and actions for the Login Page. - */ -export class LoginPage { - enterUsername(name: string) { - cy.get('#username').type(name, { log: false }); - } - - enterPassword(password: string) { - cy.get('#password').type(password, { log: false }); - } - - clickLoginButton() { - cy.get('#login-button').click(); - } - - login(credentials: CypressCredentials) { - this.enterUsername(credentials.username); - this.enterPassword(credentials.password); - this.clickLoginButton(); - } - - shouldShowFooter() { - cy.get('#footer').should('be.visible'); - } - - shouldShowAboutUsInFooter() { - cy.get('#about').should('be.visible').and('have.attr', 'href', '/about'); - } - - shouldShowRequestChangeInFooter() { - cy.get('#feedback').should('be.visible').and('have.attr', 'href'); - } - - shouldShowReleaseNotesInFooter() { - cy.get('#releases').should('be.visible').and('have.attr', 'href'); - } - - shouldShowPrivacyStatementInFooter() { - cy.get('#privacy').should('be.visible').and('have.attr', 'href', '/privacy'); - } - - shouldShowImprintInFooter() { - cy.get('#imprint').should('be.visible').and('have.attr', 'href', '/imprint'); - } -} diff --git a/src/test/cypress/support/pageobjects/NavigationBar.ts b/src/test/cypress/support/pageobjects/NavigationBar.ts deleted file mode 100644 index 18fd393a8bd4..000000000000 --- a/src/test/cypress/support/pageobjects/NavigationBar.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { COURSE_BASE, GET } from '../constants'; - -/** - * A class which encapsulates UI selectors and actions for the navigation bar at the top. - */ -export class NavigationBar { - /** - * Opens the course management page via the menu at the top and waits until it is loaded. - */ - openCourseManagement() { - cy.log('Opening course-management page...'); - cy.intercept(GET, `${COURSE_BASE}/course-management-overview*`).as('courseManagementQuery'); - cy.get('#course-admin-menu').click(); - cy.wait('@courseManagementQuery', { timeout: 30000 }); - cy.url().should('include', '/course-management'); - } - - openNotificationPanel() { - cy.get('.navbar .notification-button').click(); - } - - getNotifications() { - return cy.get('.notification-sidebar .notification-item'); - } - - getAccountItem() { - return cy.get('#account-menu'); - } - - logout() { - this.getAccountItem().click().get('#logout').click(); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/AbstractExerciseAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/AbstractExerciseAssessmentPage.ts deleted file mode 100644 index 6082ff502a56..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/AbstractExerciseAssessmentPage.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BASE_API, ExerciseType, PATCH, PUT } from '../../constants'; - -/** - * Parent class for all exercise assessment pages. - */ -export abstract class AbstractExerciseAssessmentPage { - readonly UNREFERENCED_FEEDBACK_SELECTOR = '.unreferenced-feedback-detail'; - - addNewFeedback(points: number, feedback?: string) { - cy.get('.add-unreferenced-feedback').click(); - cy.get(this.UNREFERENCED_FEEDBACK_SELECTOR).find('#feedback-points').clear().type(points.toString()); - if (feedback) { - cy.get(this.UNREFERENCED_FEEDBACK_SELECTOR).find('#feedback-textarea').clear().type(feedback); - } - } - - submitWithoutInterception() { - cy.get('#submit').click(); - } - - submit() { - cy.intercept(PUT, `${BASE_API}/participations/*/manual-results?submit=true`).as('submitAssessment'); - this.submitWithoutInterception(); - return cy.wait('@submitAssessment'); - } - - rejectComplaint(response: string, examMode: boolean, exerciseType: ExerciseType) { - return this.handleComplaint(response, false, exerciseType, examMode); - } - - acceptComplaint(response: string, examMode: boolean, exerciseType: ExerciseType) { - return this.handleComplaint(response, true, exerciseType, examMode); - } - - private handleComplaint(response: string, accept: boolean, exerciseType: ExerciseType, examMode: boolean) { - if (exerciseType !== ExerciseType.MODELING && !examMode) { - cy.get('#show-complaint').click(); - } - cy.get('#responseTextArea').type(response, { parseSpecialCharSequences: false }); - switch (exerciseType) { - case ExerciseType.PROGRAMMING: - cy.intercept(PUT, `${BASE_API}/programming-submissions/*/assessment-after-complaint`).as('complaintAnswer'); - break; - case ExerciseType.TEXT: - cy.intercept(PUT, `${BASE_API}/participations/*/submissions/*/text-assessment-after-complaint`).as('complaintAnswer'); - break; - case ExerciseType.MODELING: - cy.intercept(PATCH, `${BASE_API}/complaints/*/response`).as('complaintAnswer'); - break; - case ExerciseType.FILE_UPLOAD: - cy.intercept(PUT, `${BASE_API}/file-upload-submissions/*/assessment-after-complaint`).as('complaintAnswer'); - break; - default: - throw new Error(`Exercise type '${exerciseType}' is not supported yet!`); - } - if (accept) { - cy.get('#acceptComplaintButton').click(); - } else { - cy.get('#rejectComplaintButton').click(); - } - return cy.wait('@complaintAnswer'); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/CourseAssessmentDashboardPage.ts b/src/test/cypress/support/pageobjects/assessment/CourseAssessmentDashboardPage.ts deleted file mode 100644 index da24b01bcfb2..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/CourseAssessmentDashboardPage.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { COURSE_BASE, POST } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the course assessment dashboard page. - */ -export class CourseAssessmentDashboardPage { - openComplaints() { - cy.get('#open-complaints').click(); - } - - showTheComplaint() { - cy.get('#show-complaint').click(); - } - - clickExerciseDashboardButton() { - // Sometimes the page does not load properly, so we reload it if the button is not found - cy.reloadUntilFound('#open-exercise-dashboard'); - cy.get('#open-exercise-dashboard').click(); - } - - clickEvaluateQuizzes() { - cy.intercept(POST, `${COURSE_BASE}/*/exams/*/student-exams/evaluate-quiz-exercises`).as('evaluateQuizzes'); - cy.get('#evaluateQuizExercisesButton').click(); - return cy.wait('@evaluateQuizzes'); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/ExamAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/ExamAssessmentPage.ts deleted file mode 100644 index 444d6fa743c1..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/ExamAssessmentPage.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BASE_API, POST, PUT } from '../../constants'; -import { AbstractExerciseAssessmentPage } from './AbstractExerciseAssessmentPage'; - -export class ExamAssessmentPage extends AbstractExerciseAssessmentPage { - submitModelingAssessment() { - cy.intercept(PUT, `${BASE_API}/modeling-submissions/*/result/*/assessment*`).as('submitAssessment'); - super.submitWithoutInterception(); - return cy.wait('@submitAssessment'); - } - - submitTextAssessment() { - cy.intercept(POST, `${BASE_API}/participations/*/results/*/submit-text-assessment`).as('submitFeedback'); - super.submitWithoutInterception(); - return cy.wait('@submitFeedback'); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/ExerciseAssessmentDashboardPage.ts b/src/test/cypress/support/pageobjects/assessment/ExerciseAssessmentDashboardPage.ts deleted file mode 100644 index 97b1cef4b06a..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/ExerciseAssessmentDashboardPage.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the exercise assessment dashboard page. - */ -export class ExerciseAssessmentDashboardPage { - readonly START_ASSESSING_SELECTOR = '#start-new-assessment'; - - clickHaveReadInstructionsButton() { - cy.get('#participate-in-assessment').click(); - } - - clickStartNewAssessment() { - cy.reloadUntilFound(this.START_ASSESSING_SELECTOR); - cy.get(this.START_ASSESSING_SELECTOR).click(); - } - - clickOpenAssessment() { - cy.get('#open-assessment').click(); - } - - clickEvaluateComplaint() { - cy.get('#evaluate-complaint').click(); - } - - getComplaintText() { - return cy.get('#complaintTextArea'); - } - - getLockedMessage() { - return cy.get('#assessmentLockedCurrentUser'); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/FileUploadExerciseAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/FileUploadExerciseAssessmentPage.ts deleted file mode 100644 index d8e1ebae54ba..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/FileUploadExerciseAssessmentPage.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ExerciseType } from '../../constants'; -import { AbstractExerciseAssessmentPage } from './AbstractExerciseAssessmentPage'; - -/** - * A class which encapsulates UI selectors and actions for the file upload exercise assessment page. - */ -export class FileUploadExerciseAssessmentPage extends AbstractExerciseAssessmentPage { - getInstructionsRootElement() { - return cy.get('#instructions-card'); - } - - submitFeedback() { - cy.get('#submit').click(); - } - - rejectComplaint(response: string, examMode: boolean) { - return super.rejectComplaint(response, examMode, ExerciseType.FILE_UPLOAD); - } - - acceptComplaint(response: string, examMode: boolean) { - return super.acceptComplaint(response, examMode, ExerciseType.FILE_UPLOAD); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/ModelingExerciseAssessmentEditor.ts b/src/test/cypress/support/pageobjects/assessment/ModelingExerciseAssessmentEditor.ts deleted file mode 100644 index 917d80b47b2f..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/ModelingExerciseAssessmentEditor.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { BASE_API, ExerciseType, MODELING_EDITOR_CANVAS, PUT } from '../../constants'; -import { AbstractExerciseAssessmentPage } from './AbstractExerciseAssessmentPage'; - -const ASSESSMENT_CONTAINER = '#modeling-assessment-container'; - -/** - * A class which encapsulates UI selectors and actions for the Modeling Exercise Assessment editor - */ -export class ModelingExerciseAssessmentEditor extends AbstractExerciseAssessmentPage { - openAssessmentForComponent(componentNumber: number) { - cy.get('#apollon-assessment-row').getSettled(`${MODELING_EDITOR_CANVAS} >>> :nth-child(${componentNumber})`).children().eq(0).dblclick('top', { force: true }); - } - - assessComponent(points: number, feedback: string) { - this.getPointAssessmentField().type(`${points}`); - this.getFeedbackAssessmentField().type(`${feedback}`); - } - - clickNextAssessment() { - this.getNextAssessmentField().click(); - } - - rejectComplaint(response: string, examMode: false) { - return super.rejectComplaint(response, examMode, ExerciseType.MODELING); - } - - acceptComplaint(response: string, examMode: false) { - return super.acceptComplaint(response, examMode, ExerciseType.MODELING); - } - - submitExample() { - cy.intercept(PUT, `${BASE_API}/modeling-submissions/*/example-assessment`).as('createExampleSubmission'); - cy.contains('Save Example Assessment').click(); - return cy.wait('@createExampleSubmission').its('response.statusCode').should('eq', 200); - } - - submit() { - cy.intercept(PUT, `${BASE_API}/modeling-submissions/*/result/*/assessment*`).as('submitModelingAssessment'); - super.submitWithoutInterception(); - return cy.wait('@submitModelingAssessment').its('response.statusCode').should('eq', 200); - } - - private getNextAssessmentField() { - return this.getAssessmentContainer().children().last(); - } - - private getPointAssessmentField() { - return this.getAssessmentContainer().children().eq(1).children().children().eq(1); - } - - private getFeedbackAssessmentField() { - return this.getAssessmentContainer().children().eq(3); - } - - private getAssessmentContainer() { - return cy.get(`${ASSESSMENT_CONTAINER}`); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/ProgrammingExerciseAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/ProgrammingExerciseAssessmentPage.ts deleted file mode 100644 index 961934b5ba14..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/ProgrammingExerciseAssessmentPage.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ExerciseType } from '../../constants'; -import { AbstractExerciseAssessmentPage } from './AbstractExerciseAssessmentPage'; - -/** - * A class which encapsulates UI selectors and actions for the programming exercise assessment page. - */ -export class ProgrammingExerciseAssessmentPage extends AbstractExerciseAssessmentPage { - readonly feedbackEditorSelector = '#test-'; - - provideFeedbackOnCodeLine(lineIndex: number, points: number, feedback: string) { - // We can't change elements from the ace editor, so we can't use custom ids here - cy.get('.ace_gutter-cell').eq(lineIndex).find('svg').click({ force: true }); - this.typeIntoFeedbackEditor(feedback, lineIndex); - this.typePointsIntoFeedbackEditor(points, lineIndex); - this.saveFeedback(lineIndex); - } - - private typeIntoFeedbackEditor(text: string, index: number) { - this.getInlineFeedback(index).find('#feedback-textarea').type(text, { parseSpecialCharSequences: false }); - } - - private typePointsIntoFeedbackEditor(points: number, index: number) { - this.getInlineFeedback(index).find('#feedback-points').clear().type(points.toString()); - } - - private saveFeedback(index: number) { - this.getInlineFeedback(index).find('#feedback-save').click(); - } - - /** - * Every code line in the ace editor has an attached inline feedback - * @param line the code line where the inline feedback is attached - * @returns the root element of the inline feedback component - */ - private getInlineFeedback(line: number) { - return cy.get('#code-editor-inline-feedback-' + line); - } - - rejectComplaint(response: string, examMode: boolean) { - return super.rejectComplaint(response, examMode, ExerciseType.PROGRAMMING); - } - - acceptComplaint(response: string, examMode: boolean) { - return super.acceptComplaint(response, examMode, ExerciseType.PROGRAMMING); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/StudentAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/StudentAssessmentPage.ts deleted file mode 100644 index 43a6bc0e2388..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/StudentAssessmentPage.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the student assessment page. - */ -export class StudentAssessmentPage { - startComplaint() { - cy.get('#complain', { timeout: 30000 }).click(); - } - - enterComplaint(text: string) { - cy.get('#complainTextArea').type(text); - } - - submitComplaint() { - cy.get('#submit-complaint').click(); - } - - getComplaintBadge() { - return cy.get('jhi-complaint-request .badge'); - } - - getComplaintResponse() { - return cy.get('#complainResponseTextArea'); - } -} diff --git a/src/test/cypress/support/pageobjects/assessment/TextExerciseAssessmentPage.ts b/src/test/cypress/support/pageobjects/assessment/TextExerciseAssessmentPage.ts deleted file mode 100644 index 93c8ac8a388b..000000000000 --- a/src/test/cypress/support/pageobjects/assessment/TextExerciseAssessmentPage.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { BASE_API, ExerciseType, POST } from '../../constants'; -import { AbstractExerciseAssessmentPage } from './AbstractExerciseAssessmentPage'; - -/** - * A class which encapsulates UI selectors and actions for the text exercise assessment page. - */ -export class TextExerciseAssessmentPage extends AbstractExerciseAssessmentPage { - getInstructionsRootElement() { - return cy.get('#instructions-card'); - } - - provideFeedbackOnTextSection(sectionIndex: number, points: number, feedback: string) { - this.getFeedbackSection(sectionIndex).click(); - this.typeIntoFeedbackEditor(sectionIndex, feedback); - this.typePointsIntoFeedbackEditor(sectionIndex, points); - } - - private typeIntoFeedbackEditor(sectionIndex: number, feedbackText: string) { - this.getFeedbackSection(sectionIndex).find('#feedback-editor-text-input').type(feedbackText, { parseSpecialCharSequences: false }); - } - - private typePointsIntoFeedbackEditor(sectionIndex: number, feedbackPoints: number) { - this.getFeedbackSection(sectionIndex).find('#feedback-editor-points-input').clear().type(feedbackPoints.toString()); - } - - private getFeedbackSection(sectionIndex: number) { - return cy.get('#text-feedback-block-' + sectionIndex); - } - - submit() { - // Feedback route is special for text exercises, so we override parent here... - cy.intercept(POST, `${BASE_API}/participations/*/results/*/submit-text-assessment`).as('submitFeedback'); - cy.get('#submit').click(); - return cy.wait('@submitFeedback'); - } - - rejectComplaint(response: string, examMode: boolean) { - return super.rejectComplaint(response, examMode, ExerciseType.TEXT); - } - - acceptComplaint(response: string, examMode: boolean) { - return super.acceptComplaint(response, examMode, ExerciseType.TEXT); - } - - getWordCountElement() { - return cy.get('#text-assessment-word-count'); - } - - getCharacterCountElement() { - return cy.get('#text-assessment-character-count'); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CourseCreationPage.ts b/src/test/cypress/support/pageobjects/course/CourseCreationPage.ts deleted file mode 100644 index 24db4af3cb9c..000000000000 --- a/src/test/cypress/support/pageobjects/course/CourseCreationPage.ts +++ /dev/null @@ -1,243 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { COURSE_ADMIN_BASE, COURSE_BASE, POST, PUT } from '../../constants'; -import { enterDate } from '../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the course creation page. - */ -export class CourseCreationPage { - /** - * Sets the title of the course. - * @param title the exam title - */ - setTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - /** - * Sets the short name of the course. - * @param shortName the course short name - */ - setShortName(shortName: string) { - cy.get('#field_shortName').clear().type(shortName); - } - - /** - * Sets the description of the course. - * @param description the course description - */ - setDescription(description: string) { - cy.get('#field_description').clear().type(description); - } - - /** - * @param date the date when the exam starts - */ - setStartDate(date: dayjs.Dayjs) { - enterDate('#field_startDate', date); - } - - /** - * @param date the date when the exam will end - */ - setEndDate(date: dayjs.Dayjs) { - enterDate('#field_endDate', date); - } - - /** - * Sets course to be a test course - * @param testCourse if is a test course - */ - setTestCourse(testCourse: boolean) { - if (testCourse) { - cy.get('#field_testCourse').check(); - } else { - cy.get('#field_testCourse').uncheck(); - } - } - - /** - * Sets semester for the course - * @param semester the semester of the course - */ - setSemester(semester: string) { - cy.get('#semester').select(semester); - } - - /** - * Sets the maximum achievable points in the course. - * @param courseMaxPoints the max points - */ - setCourseMaxPoints(courseMaxPoints: number) { - cy.get('#field_maxPoints').clear().type(courseMaxPoints.toString()); - } - - /** - * Sets the default programming language - * @param programmingLanguage the programming language - */ - setProgrammingLanguage(programmingLanguage: string) { - cy.get('#programmingLanguage').select(programmingLanguage); - } - - /** - * Sets if the course group names should be customized - * @param customizeGroupNames customize the group names - */ - setCustomizeGroupNames(customizeGroupNames: boolean) { - if (customizeGroupNames) { - cy.get('#field_customizeGroupNamesEnabled').check(); - } else { - cy.get('#field_customizeGroupNamesEnabled').uncheck(); - } - } - - /** - * Sets the customized group name for students - * @param groupName the group name - */ - setStudentGroup(groupName: string) { - cy.get('#field_studentGroupName').clear().type(groupName); - } - - /** - * Sets the customized group name for tutors - * @param groupName the group name - */ - setTutorGroup(groupName: string) { - cy.get('#field_teachingAssistantGroupName').clear().type(groupName); - } - - /** - * Sets the customized group name for editors - * @param groupName the group name - */ - setEditorGroup(groupName: string) { - cy.get('#field_editorGroupName').clear().type(groupName); - } - - /** - * Sets the customized group name for editors - * @param groupName the group name - */ - setInstructorGroup(groupName: string) { - cy.get('#field_instructorGroupName').clear().type(groupName); - } - - /** - * Sets if complaints are enabled - * @param complaints if complaints should be enabled - */ - setEnableComplaints(complaints: boolean) { - if (complaints) { - cy.get('#field_maxComplaintSettingEnabled').check(); - } else { - cy.get('#field_maxComplaintSettingEnabled').uncheck(); - } - } - - /** - * Sets maximum amount of complaints - * @param maxComplaints the maximum complaints - */ - setMaxComplaints(maxComplaints: number) { - cy.get('#field_maxComplaints').clear().type(maxComplaints.toString()); - } - - /** - * Sets maximum amount of team complaints - * @param maxTeamComplaints the maximum team complaints - */ - setMaxTeamComplaints(maxTeamComplaints: number) { - cy.get('#field_maxTeamComplaints').clear().type(maxTeamComplaints.toString()); - } - - /** - * Sets the maximal complaints time in days - * @param maxComplaintsTimeDays the maximal complaints time in days - */ - setMaxComplaintsTimeDays(maxComplaintsTimeDays: number) { - cy.get('#field_maxComplaintTimeDays').clear().type(maxComplaintsTimeDays.toString()); - } - - /** - * Sets the maximal complaint text limit - * @param maxComplaintTextLimit the maximal complaint text limit - */ - setMaxComplaintTextLimit(maxComplaintTextLimit: number) { - cy.get('#field_maxComplaintTextLimit').clear().type(maxComplaintTextLimit.toString()); - } - - /** - * Sets the maximal complaint response text limit - * @param maxComplaintResponseTextLimit the maximal complaint response text limit - */ - setMaxComplaintResponseTextLimit(maxComplaintResponseTextLimit: number) { - cy.get('#field_maxComplaintResponseTextLimit').clear().type(maxComplaintResponseTextLimit.toString()); - } - - /** - * Sets if more feedback requests are enabled - * @param moreFeedback if more feedback should be enabled - */ - setEnableMoreFeedback(moreFeedback: boolean) { - if (moreFeedback) { - cy.get('#field_maxRequestMoreFeedbackSettingEnabled').check(); - } else { - cy.get('#field_maxRequestMoreFeedbackSettingEnabled').uncheck(); - } - } - - /** - * Sets the maximal request more feedback time in days - * @param maxRequestMoreFeedbackTimeDays maximal request more feedback time in days - */ - setMaxRequestMoreFeedbackTimeDays(maxRequestMoreFeedbackTimeDays: number) { - cy.get('#field_maxRequestMoreFeedbackTimeDays').clear().type(maxRequestMoreFeedbackTimeDays.toString()); - } - - /** - * Sets if course is an online course - * @param onlineCourse if should be online course - */ - setOnlineCourse(onlineCourse: boolean) { - if (onlineCourse) { - cy.get('#field_onlineCourse').check(); - } else { - cy.get('#field_onlineCourse').uncheck(); - } - } - - /** - * Sets if students can register for this course - * @param registration if registration should be possible - */ - setRegistrationEnabled(registration: boolean) { - if (registration) { - cy.get('#field_registrationEnabled').check(); - } else { - cy.get('#field_registrationEnabled').uncheck(); - } - } - - /** - * Submits the created exam. - * @returns the query chainable if a test needs to access the response - */ - submit() { - cy.intercept(POST, COURSE_ADMIN_BASE).as('createCourseQuery'); - cy.get('#save-entity').click(); - return cy.wait('@createCourseQuery'); - } - - /** - * Updates the created exam. - * @returns the query chainable if a test needs to access the response - */ - update() { - cy.intercept(PUT, `${COURSE_BASE}/*`).as('updateCourseQuery'); - cy.get('#save-entity').click(); - return cy.wait('@updateCourseQuery'); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CourseManagementExercisesPage.ts b/src/test/cypress/support/pageobjects/course/CourseManagementExercisesPage.ts deleted file mode 100644 index 9a7372d2030a..000000000000 --- a/src/test/cypress/support/pageobjects/course/CourseManagementExercisesPage.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Exercise } from 'app/entities/exercise.model'; - -import { DELETE, MODELING_EXERCISE_BASE, PROGRAMMING_EXERCISE_BASE, QUIZ_EXERCISE_BASE, TEXT_EXERCISE_BASE, UPLOAD_EXERCISE_BASE } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the course management exercises page. - */ -export class CourseManagementExercisesPage { - getExercise(exerciseID: number) { - return cy.get(`#exercise-card-${exerciseID}`); - } - - clickDeleteExercise(exerciseID: number) { - this.getExercise(exerciseID).find('#delete-exercise').click(); - } - - clickExampleSubmissionsButton() { - cy.get('#example-submissions-button').click(); - } - - getExerciseTitle() { - return cy.contains('Title').parent().parent().find('dd'); - } - - deleteTextExercise(exercise: Exercise) { - this.getExercise(exercise.id!).find('#delete-exercise').click(); - cy.get('#confirm-entity-name').type(exercise.title!); - cy.intercept(DELETE, `${TEXT_EXERCISE_BASE}/*`).as('deleteTextExercise'); - cy.get('#delete').click(); - cy.wait('@deleteTextExercise'); - } - - deleteModelingExercise(exercise: Exercise) { - this.getExercise(exercise.id!).find('#delete-exercise').click(); - cy.get('#confirm-entity-name').type(exercise.title!); - cy.intercept(DELETE, `${MODELING_EXERCISE_BASE}/*`).as('deleteModelingExercise'); - cy.get('#delete').click(); - cy.wait('@deleteModelingExercise'); - } - - deleteQuizExercise(exercise: Exercise) { - this.getExercise(exercise.id!).find(`#delete-quiz-${exercise.id}`).click(); - cy.get('#confirm-entity-name').type(exercise.title!); - cy.intercept(DELETE, `${QUIZ_EXERCISE_BASE}/*`).as('deleteQuizExercise'); - cy.get('#delete').click(); - cy.wait('@deleteQuizExercise'); - } - - deleteProgrammingExercise(exercise: Exercise) { - this.getExercise(exercise.id!).find('#delete-exercise').click(); - cy.get('#confirm-entity-name').type(exercise.title!); - cy.intercept(DELETE, `${PROGRAMMING_EXERCISE_BASE}/*`).as('deleteProgrammingExercise'); - cy.get('#delete').click(); - cy.wait('@deleteProgrammingExercise'); - } - - deleteFileUploadExercise(exercise: Exercise) { - this.getExercise(exercise.id!).find('#delete-exercise').click(); - cy.get('#confirm-entity-name').type(exercise.title!); - cy.intercept(DELETE, `${UPLOAD_EXERCISE_BASE}/*`).as('deleteFileUploadExercise'); - cy.get('#delete').click(); - cy.wait('@deleteFileUploadExercise'); - } - - createProgrammingExercise() { - cy.get('#create-programming-exercise').click(); - } - - createModelingExercise() { - cy.get('#create-modeling-exercise').click(); - } - - createTextExercise() { - cy.get('#create-text-exercise').click(); - } - - createQuizExercise() { - cy.get('#create-quiz-button').click(); - } - - createFileUploadExercise() { - cy.get('#create-file-upload-exercise').click(); - } - - importProgrammingExercise() { - cy.get('#import-programming-exercise').click(); - } - - importModelingExercise() { - cy.get('#import-modeling-exercise').click(); - } - - importTextExercise() { - cy.get('#import-text-exercise').click(); - } - - importQuizExercise() { - cy.get('#import-quiz-exercise').click(); - } - - clickImportExercise(exerciseID: number) { - return cy.get(`.exercise-${exerciseID}`).find('.import').click(); - } - - startQuiz(quizID: number) { - cy.get(`#instructor-quiz-start-${quizID}`).click(); - } - - shouldContainExerciseWithName(exerciseID: number) { - this.getExercise(exerciseID).scrollIntoView().should('be.visible'); - } - - getModelingExerciseTitle(exerciseID: number) { - return cy.get(`#exercise-card-${exerciseID}`).find(`#modeling-exercise-${exerciseID}-title`); - } - - getModelingExerciseMaxPoints(exerciseID: number) { - return cy.get(`#exercise-card-${exerciseID}`).find(`#modeling-exercise-${exerciseID}-maxPoints`); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CourseManagementPage.ts b/src/test/cypress/support/pageobjects/course/CourseManagementPage.ts deleted file mode 100644 index d18929b71b62..000000000000 --- a/src/test/cypress/support/pageobjects/course/CourseManagementPage.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { Course } from 'app/entities/course.model'; - -import { COURSE_ADMIN_BASE, COURSE_BASE, DELETE, POST, PUT } from '../../constants'; -import { CypressCredentials } from '../../users'; - -/** - * A class which encapsulates UI selectors and actions for the course management page. - */ -export class CourseManagementPage { - openCourseCreation() { - return cy.get('#create-course').click(); - } - - openCourseEdit() { - return cy.get('#edit-course').click(); - } - - /** - * @returns Returns the cypress chainable containing the root element of the course card of our created course. - * This can be used to find specific elements within this course card. - */ - getCourse(courseID: number) { - return cy.get(`#course-${courseID}`); - } - - /** - * Opens the exercises (of the first found course). - */ - openExercisesOfCourse(courseID: number) { - this.getCourse(courseID).find('#course-card-open-exercises').click(); - cy.url().should('include', '/exercises'); - } - - /** - * Opens the students overview page of a course. - * @param courseId the id of the course - */ - openStudentOverviewOfCourse(courseId: number) { - cy.get(`#open-student-management-${courseId}`).click(); - } - - /** - * Opens a course. - * @param courseID - */ - openCourse(courseID: number) { - return this.getCourse(courseID).find('#course-card-header').click(); - } - - deleteCourse(course: Course) { - cy.get('#delete-course').click(); - cy.get('#delete').should('be.disabled'); - cy.get('#confirm-entity-name').type(course.title!); - cy.intercept(DELETE, `${COURSE_ADMIN_BASE}/${course.id}`).as('deleteCourse'); - cy.get('#delete').click(); - cy.wait('@deleteCourse'); - } - - /** - * Adds the user to the student group of the course - * @param credentials the user that gets added to the student group of the course - * */ - addStudentToCourse(credentials: CypressCredentials) { - cy.intercept(POST, `${COURSE_BASE}/*/students/${credentials.username}`).as('addStudentQuery'); - cy.get('#detail-value-artemisApp\\.course\\.studentGroupName').children().first().click({ force: true }); - this.confirmUserIntoGroup(credentials); - cy.wait('@addStudentQuery'); - } - - /** - * Adds the user to the tutor group of the course - * @param credentials the user that gets added to the tutor group of the course - * */ - addTutorToCourse(credentials: CypressCredentials) { - cy.intercept(POST, `${COURSE_BASE}/*/tutors/${credentials.username}`).as('addTutorsQuery'); - cy.get('#add-tutors').click(); - this.confirmUserIntoGroup(credentials); - cy.wait('@addTutorsQuery'); - } - - /** - * Adds the user to the editor group of the course - * @param credentials the user that gets added to the editor group of the course - * */ - addEditorToCourse(credentials: CypressCredentials) { - cy.intercept(POST, `${COURSE_BASE}/*/editors/${credentials.username}`).as('addEditorsQuery'); - cy.get('#add-editors').click(); - this.confirmUserIntoGroup(credentials); - cy.wait('@addEditorsQuery'); - } - - /** - * Adds the user to the instructor group of the course - * @param credentials the user that gets added to the instructor group of the course - * */ - addInstructorToCourse(credentials: CypressCredentials) { - cy.intercept(POST, `${COURSE_BASE}/*/instructors/${credentials.username}`).as('addInstructorQuery'); - cy.get('#add-instructors').click(); - this.confirmUserIntoGroup(credentials); - cy.wait('@addInstructorQuery'); - } - - removeFirstUser() { - cy.get('#registered-students button[jhideletebutton]').click(); - cy.get('.modal #delete').click(); - } - - clickEditCourse() { - cy.get('#edit-course').click(); - } - - updateCourse(course: Course) { - cy.intercept(PUT, `${COURSE_BASE}/${course.id}`).as('updateCourseQuery'); - cy.get('#save-entity').click(); - return cy.wait('@updateCourseQuery'); - } - - checkCourseHasNoIcon() { - cy.get('#delete-course-icon').should('not.exist'); - cy.get('.no-image').should('exist'); - } - - removeIconFromCourse() { - cy.get('#delete-course-icon').click(); - cy.get('#delete-course-icon').should('not.exist'); - cy.get('.no-image').should('exist'); - } - - /** - * helper method to avoid code duplication - * */ - private confirmUserIntoGroup(credentials: CypressCredentials) { - cy.get('#typeahead-basic ').type(credentials.username).type('{enter}'); - cy.get('#ngb-typeahead-0') - .contains(new RegExp('\\(' + credentials.username + '\\)')) - .click(); - cy.get('#bread-crumb-2').click(); - } - - /** - * Opens the exams of a course. - */ - openExamsOfCourse(courseID: number) { - this.getCourse(courseID).find('#course-card-open-exams').click(); - cy.url().should('include', '/exams'); - } - - openAssessmentDashboardOfCourse(courseID: number) { - this.getCourse(courseID).find('#course-card-open-assessment-dashboard').click(); - cy.url().should('include', '/assessment-dashboard'); - } - - /** - * helper methods to get information about the course - * */ - - getRegisteredStudents() { - return cy.get('#registered-students'); - } - - getCourseHeaderTitle() { - return cy.get('#course-header-title'); - } - - getCourseHeaderDescription() { - return cy.get('#course-header-description'); - } - - getCourseTitle() { - return cy.get('#detail-value-artemisApp\\.course\\.title'); - } - - getCourseShortName() { - return cy.get('#detail-value-artemisApp\\.course\\.shortName'); - } - - getCourseStudentGroupName() { - return cy.get('#detail-value-artemisApp\\.course\\.studentGroupName'); - } - - getCourseTutorGroupName() { - return cy.get('#detail-value-artemisApp\\.course\\.teachingAssistantGroupName'); - } - - getCourseEditorGroupName() { - return cy.get('#detail-value-artemisApp\\.course\\.editorGroupName'); - } - - getCourseInstructorGroupName() { - return cy.get('#detail-value-artemisApp\\.course\\.instructorGroupName'); - } - - getCourseStartDate() { - return cy.get('#detail-value-artemisApp\\.course\\.startDate'); - } - - getCourseEndDate() { - return cy.get('#detail-value-artemisApp\\.course\\.endDate'); - } - - getCourseSemester() { - return cy.get('#detail-value-artemisApp\\.course\\.semester'); - } - - getCourseProgrammingLanguage() { - return cy.get('#detail-value-artemisApp\\.course\\.defaultProgrammingLanguage'); - } - - getCourseTestCourse() { - return cy.get('#detail-value-artemisApp\\.course\\.testCourse\\.title'); - } - - getCourseOnlineCourse() { - return cy.get('#detail-value-artemisApp\\.course\\.onlineCourse\\.title'); - } - - getCourseMaxComplaints() { - return cy.get('#detail-value-artemisApp\\.course\\.maxComplaints\\.title'); - } - - getCourseMaxTeamComplaints() { - return cy.get('#detail-value-artemisApp\\.course\\.maxTeamComplaints\\.title'); - } - - getMaxComplaintTimeDays() { - return cy.get('#detail-value-artemisApp\\.course\\.maxComplaintTimeDays\\.title'); - } - - getMaxRequestMoreFeedbackTimeDays() { - return cy.get('#detail-value-artemisApp\\.course\\.maxRequestMoreFeedbackTimeDays\\.title'); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CourseMessages.ts b/src/test/cypress/support/pageobjects/course/CourseMessages.ts deleted file mode 100644 index 721464317634..000000000000 --- a/src/test/cypress/support/pageobjects/course/CourseMessages.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { COURSE_BASE, DELETE, POST, PUT } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the course messages page. - */ -export class CourseMessagesPage { - createChannelButton() { - cy.get('#channelButton').click(); - cy.get('#createChannel').click(); - } - - browseChannelsButton() { - cy.get('#channelButton').click(); - cy.get('#channelOverview').click(); - } - - browseExerciseChannelsButton() { - cy.get('#exerciseChannelButton').click(); - cy.get('#exerciseChannelOverview').click(); - } - - browseLectureChannelsButton() { - cy.get('#lectureChannelButton').click(); - cy.get('#lectureChannelOverview').click(); - } - - browseExamChannelsButton() { - cy.get('#examChannelButton').click(); - cy.get('#examChannelOverview').click(); - } - - checkChannelsExists(name: string) { - cy.get('.channels-overview').find('.list-group-item').contains(name); - } - - getChannelIdByName(name: string) { - return cy - .get('.channels-overview') - .find('.list-group-item') - .filter(`:contains("${name}")`) - .invoke('attr', 'id') - .then((id) => { - return id?.replace('channel-', ''); - }); - } - - joinChannel(channelID: number) { - cy.intercept(POST, `${COURSE_BASE}/*/channels/*/register`).as('joinChannel'); - cy.get(`#channel-${channelID}`).find(`#register${channelID}`).click({ force: true }); - cy.wait('@joinChannel'); - } - - leaveChannel(channelID: number) { - cy.intercept(POST, `${COURSE_BASE}/*/channels/*/deregister`).as('leaveChannel'); - cy.get(`#channel-${channelID}`).find(`#deregister${channelID}`).click({ force: true }); - cy.wait('@leaveChannel'); - } - - checkBadgeJoined(channelID: number) { - return cy.get(`#channel-${channelID}`).find('.badge'); - } - - setName(name: string) { - cy.get('.modal-content').find('#name').clear().type(name); - } - - setDescription(description: string) { - cy.get('.modal-content').find('#description').clear().type(description); - } - - setPrivate() { - cy.get('.modal-content').find('label[for=private]').click(); - } - - setPublic() { - cy.get('.modal-content').find('label[for=public]').click(); - } - - setAnnouncementChannel() { - cy.get('.modal-content').find('label[for=isAnnouncementChannel]').click(); - } - - setUnrestrictedChannel() { - cy.get('.modal-content').find('label[for=isNotAnnouncementChannel]').click(); - } - - createChannel(isAnnouncementChannel: boolean, isPublic: boolean) { - cy.intercept(POST, `${COURSE_BASE}/*/channels`).as('createChannel'); - cy.get('.modal-content').find('#submitButton').click(); - cy.wait('@createChannel').then((interception) => { - const response = interception.response!.body; - cy.url().should('contain', `messages?conversationId=${response.id}`); - expect(response.isAnnouncementChannel).to.eq(isAnnouncementChannel); - expect(response.isPublic).to.eq(isPublic); - }); - } - - getError() { - return cy.get('.modal-body').find('.alert'); - } - - getName() { - return cy.get('h4.d-inline-block'); - } - - getTopic() { - return cy.get('#conversation-topic'); - } - - editName(newName: string) { - cy.get('#name-section').find('.action-button').click(); - cy.get('.channels-overview').find('#name').clear().type(newName); - cy.get('#submitButton').click(); - } - - editTopic(newTopic: string) { - cy.get('#topic-section').find('.action-button').click(); - cy.get('.channels-overview').find('#topic').clear().type(newTopic); - cy.get('#submitButton').click(); - } - - editDescription(newDescription: string) { - cy.get('#description-section').find('.action-button').click(); - cy.get('.channels-overview').find('#description').type(newDescription); - cy.get('#submitButton').click(); - } - - closeEditPanel() { - cy.get('.conversation-detail-dialog').find('.btn-close').click(); - } - - writeMessage(message: string) { - cy.get('.markdown-editor').find('.ace_editor').click().type(message, { delay: 8 }); - } - - checkMessage(messageId: number, message: string) { - this.getSinglePost(messageId).find('.markdown-preview').contains(message); - } - - editMessage(messageId: number, message: string) { - this.getSinglePost(messageId).find('.editIcon').click(); - this.getSinglePost(messageId).find('.markdown-editor').find('.ace_editor').click().type(message, { delay: 8 }); - cy.intercept(PUT, `${COURSE_BASE}/*/messages/*`).as('updateMessage'); - this.getSinglePost(messageId).find('#save').click(); - cy.wait('@updateMessage'); - } - - deleteMessage(messageId: number) { - cy.intercept(DELETE, `${COURSE_BASE}/*/messages/*`).as('deleteMessage'); - this.getSinglePost(messageId).find('.deleteIcon').click(); - this.getSinglePost(messageId).find('.deleteIcon').click(); - cy.wait('@deleteMessage'); - } - - getSinglePost(postID: number) { - return cy.get(`#item-${postID}`); - } - - save(force = false) { - cy.intercept(POST, `${COURSE_BASE}/*/messages`).as('createMessage'); - cy.get('#save').click({ force }); - return cy.wait('@createMessage'); - } - - createGroupChatButton() { - cy.get('#createGroupChat').click(); - } - - createGroupChat() { - cy.intercept(POST, `${COURSE_BASE}/*/group-chats`).as('createGroupChat'); - cy.get('#submitButton').click(); - return cy.wait('@createGroupChat'); - } - - updateGroupChat() { - cy.intercept(POST, `${COURSE_BASE}/*/group-chats/*/register`).as('updateGroupChat'); - cy.get('#submitButton').click(); - cy.wait('@updateGroupChat'); - } - - addUserToGroupChat(user: string) { - cy.get('#users-selector0-user-input').type(user); - cy.get('#ngb-typeahead-0') - .contains(new RegExp('\\(' + user + '\\)')) - .click(); - } - - addUserToGroupChatButton() { - cy.get('.addUsers').click(); - } - - listMembersButton(courseID: number, conversationID: number) { - cy.visit(`/courses/${courseID}/messages?conversationId=${conversationID}`); - cy.get('.members').click(); - } - - checkMemberList(name: string) { - cy.get('jhi-conversation-members').contains(name); - } - - openSettingsTab() { - cy.get('.settings-tab').click(); - } - - leaveGroupChat() { - cy.get('.leave-conversation').click(); - } - - checkGroupChatExists(name: string, exist: boolean) { - if (exist) { - cy.get('.conversation-list').should('contain.text', name); - } else { - cy.get('.conversation-list').should('not.contain.text', name); - } - } - - acceptCodeOfConductButton() { - cy.get('#acceptCodeOfConductButton').click(); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CourseOverviewPage.ts b/src/test/cypress/support/pageobjects/course/CourseOverviewPage.ts deleted file mode 100644 index f8abe30dae09..000000000000 --- a/src/test/cypress/support/pageobjects/course/CourseOverviewPage.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BASE_API, GET } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the course overview page (/courses/*). - */ -export class CourseOverviewPage { - readonly participationRequestId = 'participateInExerciseQuery'; - - search(term: string): void { - cy.get('input[formcontrolname="searchFilter"]').type(term); - } - - startExercise(exerciseId: number, refreshInterval?: number) { - cy.reloadUntilFound('#start-exercise-' + exerciseId, refreshInterval); - cy.get('#start-exercise-' + exerciseId).click(); - } - - openRunningExercise(exerciseId: number) { - cy.reloadUntilFound('#open-exercise-' + exerciseId); - cy.get('#open-exercise-' + exerciseId).click(); - } - - getExercise(exerciseTitle: string) { - return cy.contains('#test-sidebar-card-medium', exerciseTitle); - } - - openExerciseOverview(exerciseTitle: string) { - this.getExercise(exerciseTitle).click(); - } - - getExercises() { - return cy.get('#test-sidebar-card-medium'); - } - - openRunningProgrammingExercise(exerciseID: number) { - cy.intercept(GET, `${BASE_API}/programming-exercise-participations/*/student-participation-with-latest-result-and-feedbacks`).as('initialQuery'); - this.openRunningExercise(exerciseID); - cy.wait('@initialQuery'); - } - - openExamsTab() { - cy.get('#exam-tab').click(); - } - - openExam(examId: number) { - cy.get('#exam-' + examId).click(); - } -} diff --git a/src/test/cypress/support/pageobjects/course/CoursesPage.ts b/src/test/cypress/support/pageobjects/course/CoursesPage.ts deleted file mode 100644 index ffffee9b859c..000000000000 --- a/src/test/cypress/support/pageobjects/course/CoursesPage.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the courses page (/courses). - */ -export class CoursesPage { - openCourse(courseId: number) { - cy.get(`#course-${courseId}-header`, { timeout: 30000 }).click(); - cy.url().should('include', '/exercises'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamCreationPage.ts b/src/test/cypress/support/pageobjects/exam/ExamCreationPage.ts deleted file mode 100644 index e8be5a3a7a6c..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamCreationPage.ts +++ /dev/null @@ -1,124 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { COURSE_BASE, POST, PUT } from '../../constants'; -import { enterDate } from '../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the exam creation page. - */ -export class ExamCreationPage { - /** - * Sets the title of the exam. - * @param title the exam title - */ - setTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - /** - * Sets exam to test mode - */ - setTestMode() { - cy.get('#exam-mode-picker #test-mode').click(); - } - - /** - * @param date the date from when the exam should be visible - */ - setVisibleDate(date: dayjs.Dayjs) { - enterDate('#visibleDate', date); - } - - /** - * @param date the date when the exam starts - */ - setStartDate(date: dayjs.Dayjs) { - enterDate('#startDate', date); - } - - /** - * @param date the date when the exam will end - */ - setEndDate(date: dayjs.Dayjs) { - enterDate('#endDate', date); - } - - /** - * @param time the exam working time - */ - setWorkingTime(time: number) { - cy.get('#workingTimeInMinutes').clear().type(time.toString()); - } - - /** - * Sets the number of exercises in the exam. - * @param amount the amount of exercises - */ - setNumberOfExercises(amount: number) { - cy.get('#numberOfExercisesInExam').clear().type(amount.toString()); - } - - /** - * Sets the maximum achievable points in the exam. - * @param examMaxPoints the max points - */ - setExamMaxPoints(examMaxPoints: number) { - cy.get('#examMaxPoints').clear().type(examMaxPoints.toString()); - } - - /** - * Sets the start text of the exam. - * @param text the start text - */ - setStartText(text: string) { - this.enterText('#startText', text); - } - - /** - * Sets the end text of the exam. - * @param text the end text - */ - setEndText(text: string) { - this.enterText('#endText', text); - } - - /** - * Sets the confirmation start text of the exam. - * @param text the confirmation start text - */ - setConfirmationStartText(text: string) { - this.enterText('#confirmationStartText', text); - } - - /** - * Sets the confirmation end text of the exam. - * @param text the confirmation end text - */ - setConfirmationEndText(text: string) { - this.enterText('#confirmationEndText', text); - } - - /** - * Submits the created exam. - * @returns the query chainable if a test needs to access the response - */ - submit() { - cy.intercept(POST, `${COURSE_BASE}/*/exams`).as('createExamQuery'); - cy.get('#save-exam').click(); - return cy.wait('@createExamQuery'); - } - - /** - * Updates the created exam. - * @returns the query chainable if a test needs to access the response - */ - update() { - cy.intercept(PUT, `${COURSE_BASE}/*/exams`).as('updateExamQuery'); - cy.get('#save-exam').click(); - return cy.wait('@updateExamQuery'); - } - - private enterText(selector: string, text: string) { - cy.get(selector).find('.ace_text-input').focus().clear().type(text); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamDetailsPage.ts b/src/test/cypress/support/pageobjects/exam/ExamDetailsPage.ts deleted file mode 100644 index 79bf50ff997c..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamDetailsPage.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the exam details page. - */ -export class ExamDetailsPage { - /** - * Deletes this exam. - * @param examTitle the exam title to confirm the deletion - */ - deleteExam(examTitle: string) { - cy.get('#exam-delete').click(); - cy.get('#delete').should('be.disabled'); - cy.get('#confirm-entity-name').type(examTitle); - cy.get('#delete').should('not.be.disabled').click(); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts b/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts deleted file mode 100644 index 8f88dad63292..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Exam } from 'app/entities/exam.model'; - -import multipleChoiceTemplate from '../../../fixtures/exercise/quiz/multiple_choice/template.json'; -import { examAPIRequests, exerciseAPIRequest } from '../../artemis'; -import { AdditionalData, COURSE_BASE, ExerciseType, POST, PUT } from '../../constants'; -import { convertModelAfterMultiPart, generateUUID } from '../../utils'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { Exercise } from 'app/entities/exercise.model'; -import { Visibility } from 'app/entities/programming-exercise-test-case.model'; - -/** - * A class which encapsulates UI selectors and actions for the exam exercise group creation page. - */ -export class ExamExerciseGroupCreationPage { - typeTitle(title: string) { - cy.get('#title').clear().type(title); - } - - isMandatoryBoxShouldBeChecked() { - cy.get('#isMandatory').should('be.checked'); - } - - clickSave() { - cy.intercept({ method: POST, url: `${COURSE_BASE}/*/exams/*/exerciseGroups` }).as('createExerciseGroup'); - cy.get('#save-group').click(); - return cy.wait('@createExerciseGroup'); - } - - update() { - cy.intercept({ method: PUT, url: `${COURSE_BASE}/*/exams/*/exerciseGroups` }).as('updateExerciseGroup'); - cy.get('#save-group').click(); - cy.wait('@updateExerciseGroup'); - } - - addGroupWithExercise(exam: Exam, exerciseType: ExerciseType, additionalData: AdditionalData = {}): Promise { - return new Promise((resolve) => { - this.handleAddGroupWithExercise(exam, 'Exercise ' + generateUUID(), exerciseType, additionalData, (response) => { - let exercise = { ...response.body, additionalData }; - if (exerciseType == ExerciseType.QUIZ) { - const quiz = convertModelAfterMultiPart(response) as QuizExercise; - additionalData!.quizExerciseID = quiz.quizQuestions![0].id; - exercise = { ...quiz, additionalData }; - } - - if (exerciseType === ExerciseType.PROGRAMMING) { - const RETRY_NUMBER = 0; - exerciseAPIRequest.changeProgrammingExerciseTestVisibility(exercise, Visibility.Always, RETRY_NUMBER); - } - - resolve(exercise); - }); - }); - } - - handleAddGroupWithExercise(exam: Exam, title: string, exerciseType: ExerciseType, additionalData: AdditionalData, processResponse: (data: any) => void) { - examAPIRequests.addExerciseGroupForExam(exam).then((groupResponse) => { - switch (exerciseType) { - case ExerciseType.TEXT: - exerciseAPIRequest.createTextExercise({ exerciseGroup: groupResponse.body }, title).then((response) => { - processResponse(response); - }); - break; - case ExerciseType.MODELING: - exerciseAPIRequest.createModelingExercise({ exerciseGroup: groupResponse.body }, title).then((response) => { - processResponse(response); - }); - break; - case ExerciseType.QUIZ: - exerciseAPIRequest.createQuizExercise({ exerciseGroup: groupResponse.body }, [multipleChoiceTemplate], title).then((response) => { - processResponse(response); - }); - break; - case ExerciseType.PROGRAMMING: - exerciseAPIRequest - .createProgrammingExercise({ exerciseGroup: groupResponse.body, title, assessmentType: additionalData.progExerciseAssessmentType }) - .then((response) => { - processResponse(response); - }); - break; - } - }); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupsPage.ts b/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupsPage.ts deleted file mode 100644 index 1534a7f2f737..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamExerciseGroupsPage.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the exam exercise groups page. - */ -export class ExamExerciseGroupsPage { - clickCreateNewExerciseGroup() { - cy.get('#create-new-group').click(); - } - - shouldHaveTitle(groupID: number, groupTitle: string) { - cy.get(`#group-${groupID} .group-title`).contains(groupTitle); - } - - shouldNotExist(groupID: number) { - cy.get(`#group-${groupID}`).should('not.exist'); - } - - clickEditGroup(groupID: number) { - cy.get(`#group-${groupID} .edit-group`).click(); - } - - clickDeleteGroup(groupID: number, groupName: string) { - cy.get(`#group-${groupID} .delete-group`).click(); - cy.get('#delete').should('be.disabled'); - cy.get('#confirm-entity-name').type(groupName); - cy.get('#delete').should('not.be.disabled').click(); - } - - shouldShowNumberOfExerciseGroups(numberOfGroups: number) { - cy.get('#number-groups').should('contain.text', numberOfGroups); - } - - clickAddExerciseGroup() { - cy.get('#create-new-group').click(); - } - - clickAddTextExercise(groupID: number) { - cy.get(`#group-${groupID}`).find('.add-text-exercise').click(); - } - - clickAddModelingExercise(groupID: number) { - cy.get(`#group-${groupID}`).find('.add-modeling-exercise').click(); - } - - clickAddQuizExercise(groupID: number) { - cy.get(`#group-${groupID}`).find('.add-quiz-exercise').click(); - } - - clickAddProgrammingExercise(groupID: number) { - cy.get(`#group-${groupID}`).find('.add-programming-exercise').click(); - } - - visitPageViaUrl(courseId: number, examId: number) { - cy.visit(`course-management/${courseId}/exams/${examId}/exercise-groups`); - } - - shouldContainExerciseWithTitle(groupID: number, exerciseTitle: string) { - cy.get(`#group-${groupID}`).find('#exercises').contains(exerciseTitle).scrollIntoView().should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamManagementPage.ts b/src/test/cypress/support/pageobjects/exam/ExamManagementPage.ts deleted file mode 100644 index a3d819025f8d..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamManagementPage.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the exam management page. - */ -export class ExamManagementPage { - /** - * Searches for an exam with the provided title. - * @param examTitle the title of the exam. - * @returns the row element of the found exam - */ - getExamRowRoot(examTitle: string) { - return this.getExamSelector(examTitle).parents('tr'); - } - - /** Opens the exam with this exam id. */ - openExam(examId: number) { - cy.get(`#exam-${examId}-title`).click(); - } - - /** - * Clicks the create new exam button. - */ - createNewExam() { - cy.get('#create-exam').click(); - } - - /** - * Returns the title element of the exam row. - * @param examTitle the title to search for - * @returns the element - */ - getExamSelector(examTitle: string) { - return cy.get('#exams-table').contains(examTitle); - } - - /** - * Opens the exercise groups page. - */ - openExerciseGroups(examId: number) { - cy.get(`#exercises-button-${examId}-groups`).click(); - } - - /** - * Opens the student registration page. - */ - openStudentRegistration(examId: number) { - cy.get(`#student-button-${examId}`).click(); - } - - /** - * Opens the student exams page. - */ - openStudentExams(examId: number) { - cy.get(`#student-exams-${examId}`).click(); - } - - /** - * Opens the exam assessment dashboard - * @param courseID the id of the course - * @param examID the id of the exam - * @param timeout how long to wait for the assessment dashboard button - */ - openAssessmentDashboard(courseID: number, examID: number, timeout = 60000) { - cy.visit(`/course-management/${courseID}/exams`); - cy.get(`#exercises-button-${examID}`, { timeout }).click(); - } - - /** - * Opens the test run page. - */ - openTestRun() { - cy.get(`#testrun-button`).click(); - } - - verifySubmitted(courseID: number, examID: number, username: string) { - cy.visit(`/course-management/${courseID}/exams/${examID}/student-exams`); - cy.get('#student-exam').find('.datatable-body-row').filter(`:contains("${username}")`).find('.submitted').contains('Yes'); - } - - checkQuizSubmission(courseID: number, examID: number, username: string, score: string) { - cy.visit(`/course-management/${courseID}/exams/${examID}/student-exams`); - cy.get('#student-exam').find('.datatable-body-row').filter(`:contains("${username}")`).find('.view-submission').click(); - cy.get('.summery').click(); - cy.get('#exercise-result-score').contains(score); - } - - clickEdit() { - cy.get('#editButton').click(); - } - - /** - * helper methods to get information of course - * */ - getExamTitle() { - return cy.get('#detail-value-artemisApp\\.exam\\.title'); - } - - getExamVisibleDate() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.visibleDate'); - } - - getExamStartDate() { - return cy.get('#detail-value-artemisApp\\.exam\\.startDate'); - } - - getExamEndDate() { - return cy.get('#detail-value-artemisApp\\.exam\\.endDate'); - } - - getExamNumberOfExercises() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.numberOfExercisesInExam'); - } - - getExamMaxPoints() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.maxPoints\\.title'); - } - - getExamStartText() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.startText'); - } - - getExamEndText() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.endText'); - } - - getExamConfirmationStartText() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.confirmationStartText'); - } - - getExamConfirmationEndText() { - return cy.get('#detail-value-artemisApp\\.examManagement\\.confirmationEndText'); - } - - getExamWorkingTime() { - return cy.get('#detail-value-artemisApp\\.exam\\.workingTime'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamNavigationBar.ts b/src/test/cypress/support/pageobjects/exam/ExamNavigationBar.ts deleted file mode 100644 index f939546775d5..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamNavigationBar.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the navigation bar in an open exam. - */ -export class ExamNavigationBar { - /** - * Opens the exercise at the specified index. - * @param index 0-based index - */ - openExerciseAtIndex(index: number) { - cy.get('#exam-exercise-' + index).click(); - } - - openExerciseOverview() { - cy.get('.exam-navigation .navigation-item.overview').click(); - } - - /** - * Presses the hand in early button in the navigation bar. - */ - handInEarly() { - cy.get('#hand-in-early').click(); - } - - clickSave() { - cy.get('#save'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamParticipation.ts b/src/test/cypress/support/pageobjects/exam/ExamParticipation.ts deleted file mode 100644 index 014ef3fbec4f..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamParticipation.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import { - courseList, - courseOverview, - examNavigation, - examStartEnd, - modelingExerciseEditor, - programmingExerciseEditor, - quizExerciseMultipleChoice, - textExerciseEditor, -} from '../../artemis'; -import { AdditionalData, ExerciseType } from '../../constants'; -import { CypressCredentials } from '../../users'; -import { getExercise } from '../../utils'; -import { ProgrammingExerciseSubmission } from '../exercises/programming/OnlineEditorPage'; - -/** - * A class which encapsulates UI selectors and actions for the exam details page. - */ -export class ExamParticipation { - /** - * Makes a submission in a provided exercise - * @param exerciseID the id of the exercise - * @param exerciseType the type of the exercise - * @param additionalData additional data such as the expected score - */ - makeSubmission(exerciseID: number, exerciseType: ExerciseType, additionalData?: AdditionalData) { - switch (exerciseType) { - case ExerciseType.TEXT: - this.makeTextExerciseSubmission(exerciseID, additionalData!.textFixture!); - break; - case ExerciseType.MODELING: - this.makeModelingExerciseSubmission(exerciseID); - break; - case ExerciseType.QUIZ: - this.makeQuizExerciseSubmission(exerciseID, additionalData!.quizExerciseID!); - break; - case ExerciseType.PROGRAMMING: - this.makeProgrammingExerciseSubmission(exerciseID, additionalData!.submission!, additionalData!.practiceMode); - break; - } - } - - makeTextExerciseSubmission(exerciseID: number, textFixture: string) { - cy.fixture(textFixture).then((submissionText) => { - textExerciseEditor.typeSubmission(exerciseID, submissionText); - }); - cy.wait(1000); - } - - private makeProgrammingExerciseSubmission(exerciseID: number, submission: ProgrammingExerciseSubmission, practiceMode = false) { - programmingExerciseEditor.toggleCompressFileTree(exerciseID); - programmingExerciseEditor.deleteFile(exerciseID, 'Client.java'); - programmingExerciseEditor.deleteFile(exerciseID, 'BubbleSort.java'); - programmingExerciseEditor.deleteFile(exerciseID, 'MergeSort.java'); - programmingExerciseEditor.typeSubmission(exerciseID, submission); - if (practiceMode) { - programmingExerciseEditor.submitPractice(exerciseID); - } else { - programmingExerciseEditor.submit(exerciseID); - } - programmingExerciseEditor.getResultScoreFromExercise(exerciseID).contains(submission.expectedResult).and('be.visible'); - } - - makeModelingExerciseSubmission(exerciseID: number) { - modelingExerciseEditor.addComponentToModel(exerciseID, 1, false); - modelingExerciseEditor.addComponentToModel(exerciseID, 2, false); - modelingExerciseEditor.addComponentToModel(exerciseID, 3, false); - } - - makeQuizExerciseSubmission(exerciseID: number, quizExerciseID: number) { - quizExerciseMultipleChoice.tickAnswerOption(exerciseID, 0, quizExerciseID); - quizExerciseMultipleChoice.tickAnswerOption(exerciseID, 2, quizExerciseID); - } - - openExam(student: CypressCredentials, course: Course, exam: Exam) { - cy.login(student, '/'); - cy.visit('/courses'); - courseList.openCourse(course.id!); - courseOverview.openExamsTab(); - courseOverview.openExam(exam.id!); - cy.url().should('contain', `/exams/${exam.id}`); - } - - startParticipation(student: CypressCredentials, course: Course, exam: Exam) { - this.openExam(student, course, exam); - examStartEnd.startExam(true); - } - - selectExerciseOnOverview(index: number) { - cy.get(`.exercise-table tr:nth-child(${index}) a`).click(); - } - - clickSaveAndContinue() { - cy.get('#save').click(); - } - - checkExerciseTitle(exerciseID: number, title: string) { - getExercise(exerciseID).find('.exercise-title').contains(title); - } - - checkExamTitle(title: string) { - cy.get('#exam-title').contains(title); - } - - getResultScore() { - cy.reloadUntilFound('#exercise-result-score'); - return cy.get('#exercise-result-score'); - } - - checkExamFinishedTitle(title: string) { - cy.get('#exam-finished-title').contains(title, { timeout: 40000 }); - } - - checkExamFullnameInputExists() { - cy.get('#fullname', { timeout: 20000 }).should('exist'); - } - - checkYourFullname(name: string) { - cy.get('#your-name', { timeout: 20000 }).contains(name); - } - - handInEarly() { - examNavigation.handInEarly(); - examStartEnd.finishExam().then((request: Interception) => { - expect(request.response!.statusCode).to.eq(200); - }); - } - - verifyExerciseTitleOnFinalPage(exerciseID: number, exerciseTitle: string) { - getExercise(exerciseID).find(`#exercise-group-title-${exerciseID}`).scrollIntoView(); - cy.get(`#exercise-group-title-${exerciseID}`).contains(exerciseTitle).should('be.visible'); - } - - verifyTextExerciseOnFinalPage(textFixture: string) { - cy.fixture(textFixture).then((submissionText) => { - cy.get('textarea').should('have.value', submissionText); - }); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamStartEndPage.ts b/src/test/cypress/support/pageobjects/exam/ExamStartEndPage.ts deleted file mode 100644 index 38c31ca197f1..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamStartEndPage.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { COURSE_BASE, GET, POST } from '../../constants'; -import { users } from '../../users'; - -export class ExamStartEndPage { - enterFirstnameLastname() { - users.getAccountInfo((account: any) => cy.get('#fullname').type((account.firstName ?? '') + ' ' + (account.lastName ?? ''))); - } - - setConfirmCheckmark(timeout?: number) { - cy.get('#confirmBox', { timeout }).check(); - } - - pressStartWithWait() { - cy.intercept(GET, `${COURSE_BASE}/*/exams/*/student-exams/*/conduction`).as('startExam'); - cy.get('#start-exam').click(); - return cy.wait('@startExam'); - } - - pressStart() { - cy.get('#start-exam').click(); - } - - clickContinue() { - cy.get('#continue').click(); - } - - pressFinish() { - cy.intercept(POST, `${COURSE_BASE}/*/exams/*/student-exams/submit`).as('finishExam'); - cy.get('#end-exam').click(); - return cy.wait('@finishExam', { timeout: 10000 }); - } - - startExam(withWait = false) { - this.setConfirmCheckmark(); - this.enterFirstnameLastname(); - if (withWait) { - this.pressStartWithWait(); - } else { - this.pressStart(); - } - } - - finishExam(timeout?: number) { - this.setConfirmCheckmark(timeout ? timeout : Cypress.config('defaultCommandTimeout')); - this.enterFirstnameLastname(); - return this.pressFinish(); - } - - pressShowSummary() { - cy.intercept(GET, `${COURSE_BASE}/*/exams/*/student-exams/*/summary`).as('examSummaryDownload'); - cy.get('#showExamSummaryButton').should('be.visible').should('not.have.attr', 'disabled', { timeout: 15000 }).click(); - cy.wait('@examSummaryDownload'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/ExamTestRunPage.ts b/src/test/cypress/support/pageobjects/exam/ExamTestRunPage.ts deleted file mode 100644 index afb5056cfd4c..000000000000 --- a/src/test/cypress/support/pageobjects/exam/ExamTestRunPage.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; - -import { examStartEnd } from '../../artemis'; -import { COURSE_BASE, DELETE, PATCH, POST } from '../../constants'; -import { CypressCredentials } from '../../users'; - -/** - * A class which encapsulates UI selectors and actions for the exam details page. - */ -export class ExamTestRunPage { - /** - * Creates a test run - * @returns the query chainable if a test needs to access the response - */ - confirmTestRun() { - cy.intercept(POST, `${COURSE_BASE}/*/exams/*/test-run`).as('createTestRunQuery'); - cy.get('.modal-dialog #createTestRunButton').click(); - return cy.wait('@createTestRunQuery'); - } - - startParticipation(user: CypressCredentials, course: Course, exam: Exam, testRunId: number) { - cy.login(user); - this.openTestRunPage(course, exam); - this.startTestRun(testRunId); - cy.url().should('contain', `/course-management/${course.id}/exams/${exam.id}/test-runs/${testRunId}/conduction`); - examStartEnd.startExam(); - } - - createTestRun() { - cy.get('#createTestRunButton').click(); - } - - saveTestRun() { - cy.intercept(PATCH, `${COURSE_BASE}/*/exams/*/student-exams/*/working-time`).as('updateTestRunQuery'); - cy.get('#save').click(); - return cy.wait('@updateTestRunQuery'); - } - - getTestRun(testRunId: number) { - return cy.get(`#testrun-${testRunId}`); - } - - getTestRunRibbon() { - return cy.get('#testRunRibbon'); - } - - openTestRunPage(course: Course, exam: Exam) { - cy.visit('/course-management/' + course.id + '/exams/' + exam.id + '/test-runs'); - } - - setWorkingTimeHours(hours: number) { - cy.get('#workingTimeHours').clear().type(hours.toString()); - } - - setWorkingTimeMinutes(minutes: number) { - cy.get('#workingTimeMinutes').clear().type(minutes.toString()); - } - - setWorkingTimeSeconds(seconds: number) { - cy.get('#workingTimeSeconds').clear().type(seconds.toString()); - } - - getWorkingTime(testRunId: number) { - return this.getTestRun(testRunId).find('.working-time'); - } - - getStarted(testRunId: number) { - return this.getTestRun(testRunId).find('.started'); - } - - getSubmitted(testRunId: number) { - return this.getTestRun(testRunId).find('.submitted'); - } - - getTestRunIdElement(testRunId: number) { - return this.getTestRun(testRunId).find('.testrun-id'); - } - - changeWorkingTime(testRunId: number) { - cy.get(`#testrun-${testRunId}`).find('.manage-worktime').click(); - } - - startTestRun(testRunId: number) { - cy.get(`#testrun-${testRunId}`).find('.start-testrun').click(); - } - - deleteTestRun(testRunId: number) { - cy.get(`#testrun-${testRunId}`).find('.delete-testrun').click(); - cy.get('#confirm-entity-name').type('Test Run'); - cy.intercept(DELETE, `${COURSE_BASE}/*/exams/*/test-run/*`).as('deleteTestRunQuery'); - cy.get('#delete').click(); - return cy.wait('@deleteTestRunQuery'); - } -} diff --git a/src/test/cypress/support/pageobjects/exam/StudentExamManagementPage.ts b/src/test/cypress/support/pageobjects/exam/StudentExamManagementPage.ts deleted file mode 100644 index 58f6cb5d2ed8..000000000000 --- a/src/test/cypress/support/pageobjects/exam/StudentExamManagementPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { COURSE_BASE, POST } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the student exam management page. - */ -export class StudentExamManagementPage { - clickGenerateStudentExams() { - cy.intercept(POST, `${COURSE_BASE}/*/exams/*/generate-student-exams`).as('generateStudentExams'); - this.getGenerateStudentExamsButton().click(); - return cy.wait('@generateStudentExams'); - } - - clickRegisterCourseStudents() { - cy.intercept(POST, `${COURSE_BASE}/*/exams/*/register-course-students`).as('registerCourseStudents'); - cy.get('#register-course-students').click(); - return cy.wait('@registerCourseStudents'); - } - - getGenerateStudentExamsButton() { - return cy.get('#generateStudentExamsButton'); - } - - getRegisteredStudents() { - return cy.get('#registered-students'); - } - - checkExamStudent(username: string) { - return cy.get('#student-exam').find(`[href$="user-management/${username}"]`).should('have.length', 1); - } - - getStudentExamRows() { - return cy.get('#student-exam').find('.datatable-body-row'); - } - - getStudentExamRow(username: string) { - return cy.get('#student-exam').find(`a[href$="user-management/${username}"]`).closest('.datatable-body-row'); - } - - getExamProperty(username: string, property: string) { - cy.get('.datatable-header-cell').each(($headerCell, index) => { - if ($headerCell.text().includes(property)) { - this.getStudentExamRow(username).within(() => { - cy.get('.datatable-body-cell-label').eq(index).as('bodyCellLabel'); - }); - } - }); - return cy.get('@bodyCellLabel'); - } - - checkExamProperty(username: string, property: string, value: string) { - this.getExamProperty(username, property).contains(value); - } - - typeSearchText(text: string) { - cy.get('#typeahead-basic').focus().clear().type(text); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/AbstractExerciseFeedbackPage.ts b/src/test/cypress/support/pageobjects/exercises/AbstractExerciseFeedbackPage.ts deleted file mode 100644 index f98a9e74100e..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/AbstractExerciseFeedbackPage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Interception } from 'cypress/types/net-stubbing'; - -import { BASE_API, POST } from '../../constants'; - -/** - * Parent class for all exercise feedback pages (/course/./exercise/./participate/.) - */ -export abstract class AbstractExerciseFeedback { - readonly RESULT_SELECTOR = '#result'; - readonly ADDITIONAL_FEEDBACK_SELECTOR = '#additional-feedback'; - readonly COMPLAIN_BUTTON_SELECTOR = '#complain'; - - shouldShowAdditionalFeedback(points: number, feedbackText: string) { - if (Math.abs(points) === 1) { - cy.get(this.ADDITIONAL_FEEDBACK_SELECTOR).contains(`${points} Point: ${feedbackText}`).should('be.visible'); - } else { - cy.get(this.ADDITIONAL_FEEDBACK_SELECTOR).contains(`${points} Points: ${feedbackText}`).should('be.visible'); - } - } - - shouldShowScore(percentage: number) { - cy.get(this.RESULT_SELECTOR).contains(`${percentage}%`); - } - - complain(complaint: string) { - cy.reloadUntilFound(this.COMPLAIN_BUTTON_SELECTOR); - cy.get(this.COMPLAIN_BUTTON_SELECTOR).click(); - cy.get('#complainTextArea').type(complaint, { parseSpecialCharSequences: false }); - cy.intercept(POST, `${BASE_API}/complaints`).as('postComplaint'); - cy.get('#submit-complaint').click(); - return cy.wait('@postComplaint').then((request: Interception) => { - expect(request.response!.statusCode).to.eq(201); - }); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/ExerciseResultPage.ts b/src/test/cypress/support/pageobjects/exercises/ExerciseResultPage.ts deleted file mode 100644 index dde4299f3265..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/ExerciseResultPage.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { BASE_API, GET } from '../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the exercise result page. - */ -export class ExerciseResultPage { - shouldShowProblemStatement(problemStatement: string) { - cy.get('#problem-statement').contains(problemStatement).should('be.visible'); - } - - shouldShowExerciseTitle(title: string) { - cy.get('#exercise-header').contains(title).should('be.visible'); - } - - shouldShowScore(percentage: number) { - cy.reloadUntilFound('jhi-course-exercise-details #submission-result-graded'); - cy.contains('.tab-bar-exercise-details', `${percentage}%`).should('be.visible'); - } - - clickOpenExercise(exerciseId: number) { - cy.intercept(GET, `${BASE_API}/results/*/rating`).as('getResults'); - cy.get('#open-exercise-' + exerciseId).click(); - return cy.wait('@getResults'); - } - - clickOpenCodeEditor(exerciseId: number) { - cy.get('#open-exercise-' + exerciseId).click(); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadEditorPage.ts b/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadEditorPage.ts deleted file mode 100644 index b5762a614587..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadEditorPage.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { EXERCISE_BASE, POST, PUT } from '../../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the file upload editor page. - */ -export class FileUploadEditorPage { - attachFile(filePath: string) { - cy.get('#fileUploadInput').attachFile(filePath); - } - - attachFileExam(filePath: string) { - cy.get('#fileUploadInput').attachFile(filePath); - cy.get('#file-upload-submit').click(); - } - - /** - * Saves the file upload submission and continues to the next exercise in the exam. This button is only available in exam mode! - */ - saveAndContinue() { - cy.intercept(PUT, `${EXERCISE_BASE}/*/file-upload-submissions`).as('savedSubmission'); - cy.get('#save').click(); - return cy.wait('@savedSubmission'); - } - - submit() { - cy.intercept(POST, `${EXERCISE_BASE}/*/file-upload-submissions`).as('fileUploadSubmission'); - cy.get('#submit').click(); - return cy.wait('@fileUploadSubmission'); - } - - shouldShowExerciseTitleInHeader(exerciseTitle: string) { - cy.get('#participation-header').contains(exerciseTitle).should('be.visible'); - } - - shouldShowProblemStatement() { - cy.get('#problem-statement').should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseCreationPage.ts b/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseCreationPage.ts deleted file mode 100644 index 21cfaecba175..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseCreationPage.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Dayjs } from 'dayjs/esm'; - -import { UPLOAD_EXERCISE_BASE } from '../../../constants'; -import { POST } from '../../../constants'; -import { enterDate } from '../../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the file upload exercise creation page. - */ -export class FileUploadExerciseCreationPage { - /** - * @param title the title of the text exercise - */ - typeTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - setReleaseDate(date: Dayjs) { - enterDate('#pick-releaseDate', date); - } - - setDueDate(date: Dayjs) { - enterDate('#pick-dueDate', date); - } - - setAssessmentDueDate(date: Dayjs) { - enterDate('#pick-assessmentDueDate', date); - } - - typeMaxPoints(maxPoints: number) { - cy.get('#field_points').type(maxPoints.toString()); - } - - setFilePattern(pattern: string) { - this.typeText('#field_filePattern', pattern); - } - - typeProblemStatement(statement: string) { - this.typeText('#field_problemStatement', statement); - } - - typeExampleSolution(statement: string) { - this.typeText('#field_exampleSolution', statement); - } - - typeAssessmentInstructions(statement: string) { - this.typeText('#gradingInstructions', statement); - } - - create() { - cy.intercept(POST, UPLOAD_EXERCISE_BASE).as('fileUploadExerciseCreation'); - cy.get('#save-entity').click(); - return cy.wait('@fileUploadExerciseCreation'); - } - - private typeText(selector: string, text: string) { - cy.get(selector).find('.ace_content').type(text); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseFeedbackPage.ts b/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseFeedbackPage.ts deleted file mode 100644 index edb314a9f10e..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/file-upload/FileUploadExerciseFeedbackPage.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { AbstractExerciseFeedback } from '../AbstractExerciseFeedbackPage'; - -/** - * A class which encapsulates UI selectors and actions for a file upload exercise feedback page. - */ -export class FileUploadExerciseFeedbackPage extends AbstractExerciseFeedback { - shouldShowTextFeedback(feedbackIndex: number, feedback: string) { - cy.get('#text-feedback-' + feedbackIndex) - .contains(feedback) - .should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/modeling/CreateModelingExercisePage.ts b/src/test/cypress/support/pageobjects/exercises/modeling/CreateModelingExercisePage.ts deleted file mode 100644 index ab31ee707a7b..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/modeling/CreateModelingExercisePage.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Dayjs } from 'dayjs/esm'; - -import { MODELING_EXERCISE_BASE, POST } from '../../../constants'; -import { enterDate } from '../../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the Modeling Exercise Creation Page. - * Path: /course-management/{courseID}/modeling-exercises/{exerciseID} - */ -export class CreateModelingExercisePage { - setTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - addCategories(categories: string[]) { - categories.forEach((category) => { - cy.get('#field_categories').type(category).type('{enter}'); - }); - } - - setPoints(points: number) { - cy.get('#field_points').clear().type(points.toString()); - } - - save() { - cy.intercept(MODELING_EXERCISE_BASE).as('createModelingExercise'); - cy.get('#save-entity').click(); - return cy.wait('@createModelingExercise'); - } - - import() { - cy.intercept(POST, `${MODELING_EXERCISE_BASE}/import/*`).as('modelingExerciseImport'); - cy.get('#save-entity').click(); - return cy.wait('@modelingExerciseImport'); - } - - /** - * Sets the release Date field - * @param date should be in Format: YYYY-MM-DDTHH:mm:ss.SSS - * */ - setReleaseDate(date: Dayjs) { - enterDate('#pick-releaseDate', date); - } - - /** - * Sets the Due Date field - * @param date should be in Format: YYYY-MM-DDTHH:mm:ss.SSS - * */ - setDueDate(date: Dayjs) { - enterDate('#pick-dueDate', date); - } - - /** - * Sets the Assessment Due Date field - * @param date should be in Format: YYYY-MM-DDTHH:mm:ss.SSS - * */ - setAssessmentDueDate(date: Dayjs) { - enterDate('#pick-assessmentDueDate', date); - } - - includeInOverallScore() { - cy.get('#modeling-includeInScore-picker').children().eq(0).children().eq(2).click({ force: true }); - } - - pickDifficulty(options: { hard?: boolean; medium?: boolean; easy?: boolean }) { - if (options.hard) { - this.getDiffifultyBar().children().eq(3).click(); - } else if (options.medium) { - this.getDiffifultyBar().children().eq(2).click(); - } else if (options.easy) { - this.getDiffifultyBar().children().eq(1).click(); - } else { - this.getDiffifultyBar().children().eq(0).click(); - } - } - - private getDiffifultyBar() { - return cy.get('#modeling-difficulty-picker').children().eq(0); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/modeling/ModelingEditor.ts b/src/test/cypress/support/pageobjects/exercises/modeling/ModelingEditor.ts deleted file mode 100644 index 1e052f6144aa..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/modeling/ModelingEditor.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { EXERCISE_BASE, MODELING_EDITOR_CANVAS, PUT } from '../../../constants'; -import { getExercise } from '../../../utils'; - -import scrollBehaviorOptions = Cypress.scrollBehaviorOptions; - -/** - * This provides functions for interacting with the modeling editor - * */ -export class ModelingEditor { - /** - * Adds a Modeling Component to the Example Solution - * */ - addComponentToModel(exerciseID: number, componentNumber: number, scrollBehavior: scrollBehaviorOptions = 'center', x?: number, y?: number) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore https://github.com/4teamwork/cypress-drag-drop/issues/103 - getExercise(exerciseID) - .find('#modeling-editor-sidebar') - .children() - .eq(componentNumber) - .drag(`#exercise-${exerciseID} ${MODELING_EDITOR_CANVAS}`, { target: { x, y }, scrollBehavior, timeout: 1000 }); - getExercise(exerciseID).find(MODELING_EDITOR_CANVAS).trigger('pointerup'); - } - - getModelingCanvas() { - return cy.get('#modeling-editor-canvas'); - } - - addComponentToExampleSolutionModel(componentNumber: number, scrollBehavior: scrollBehaviorOptions = 'center') { - cy.get('#modeling-editor-sidebar').children().eq(componentNumber).drag(MODELING_EDITOR_CANVAS, { scrollBehavior, timeout: 1000 }); - cy.get(MODELING_EDITOR_CANVAS).trigger('mouseup').trigger('pointerup'); - } - - submit() { - cy.intercept(PUT, `${EXERCISE_BASE}/*/modeling-submissions`).as('createModelingSubmission'); - cy.get('#submit-modeling-submission').first().click(); - return cy.wait('@createModelingSubmission'); - } - - clickCreateNewExampleSubmission() { - cy.get('#new-modeling-example-submission').click(); - } - - clickCreateExampleSubmission() { - cy.get('#create-example-submission').click(); - } - - showExampleAssessment() { - cy.get('#show-modeling-example-assessment').click(); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/modeling/ModelingExerciseFeedbackPage.ts b/src/test/cypress/support/pageobjects/exercises/modeling/ModelingExerciseFeedbackPage.ts deleted file mode 100644 index d89dde814633..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/modeling/ModelingExerciseFeedbackPage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { AbstractExerciseFeedback } from '../AbstractExerciseFeedbackPage'; - -/** - * A class which encapsulates UI selectors and actions for a text exercise feedback page. - */ -export class ModelingExerciseFeedbackPage extends AbstractExerciseFeedback { - shouldShowComponentFeedback(component: number, points: number, feedback: string) { - cy.get('#component-feedback-table').children().eq(1).children().eq(component).should('contain.text', feedback).and('contain.text', `${points}`); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/programming/CodeAnalysisGradingPage.ts b/src/test/cypress/support/pageobjects/exercises/programming/CodeAnalysisGradingPage.ts deleted file mode 100644 index 06c6c17c6983..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/programming/CodeAnalysisGradingPage.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { PATCH, PROGRAMMING_EXERCISE_BASE } from '../../../constants'; - -/** - * A class which encapsulates UI selectors and actions for static code analysis grading configuration page. - */ -export class CodeAnalysisGradingPage { - visit(courseId: number, exerciseId: number) { - cy.visit(`course-management/${courseId}/programming-exercises/${exerciseId}/grading/code-analysis`); - } - - makeEveryScaCategoryInfluenceGrading() { - // Using ids here would make the test more instable. Its unlikely that this selector will break in the future. - cy.get('select').each((category) => { - cy.wrap(category).select('GRADED'); - }); - } - - saveChanges() { - cy.intercept(PATCH, `${PROGRAMMING_EXERCISE_BASE}/*/static-code-analysis-categories`).as('scaConfigurationRequest'); - cy.get('#save-table-button').click(); - return cy.wait('@scaConfigurationRequest'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/programming/OnlineEditorPage.ts b/src/test/cypress/support/pageobjects/exercises/programming/OnlineEditorPage.ts deleted file mode 100644 index 78df0503d47b..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/programming/OnlineEditorPage.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { courseList, courseOverview } from '../../../artemis'; -import { DELETE } from '../../../constants'; -import { BASE_API, POST } from '../../../constants'; -import { CypressCredentials } from '../../../users'; -import { getExercise } from '../../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the Online Editor Page. - */ -export class OnlineEditorPage { - /** - * @param exerciseID the ID of the exercise - * @returns the root element of the file browser. Useful for further querying. - */ - findFileBrowser(exerciseID: number) { - return getExercise(exerciseID).find('#cardFiles'); - } - - /** - * Writes all the content in the corresponding files in the online editor. NOTE: This does not create non-existing files. - * It only opens existing files and writes the content there! - * @param exerciseID the ID of the exercise - * @param submission object which contains the information about which files need to be edited with what content - */ - typeSubmission(exerciseID: number, submission: ProgrammingExerciseSubmission) { - for (const newFile of submission.files) { - if (submission.createFilesInRootFolder) { - this.createFileInRootFolder(exerciseID, newFile.name); - } else { - this.createFileInRootPackage(exerciseID, newFile.name, submission.packageName!); - } - cy.fixture(newFile.path) - .then(($fileContent) => { - const clipboardData = new DataTransfer(); - const format = 'text/plain'; - clipboardData.setData(format, $fileContent); - const pasteEvent = new ClipboardEvent('paste', { clipboardData }); - const editorElement = getExercise(exerciseID).find('.view-lines').first(); - editorElement - .click() - .then((element) => { - element[0].dispatchEvent(pasteEvent); - }) - .wait(500); - }) - .wait(500); - } - cy.wait(500); - } - - /** - * Deletes a file in the filebrowser. - * @param exerciseID the ID of the exercise - * @param name the file name - */ - deleteFile(exerciseID: number, name: string) { - cy.intercept(DELETE, `${BASE_API}/repository/*/**`).as('deleteFile'); - this.findFile(exerciseID, name).find('#file-browser-file-delete').click(); - cy.get('#delete-file').click(); - cy.wait('@deleteFile').its('response.statusCode').should('eq', 200); - this.findFileBrowser(exerciseID).contains(name).should('not.exist'); - } - - /** - * @param exerciseID the ID of the exercise - * @param name the file name - * @returns the root element of a file in the filebrowser - */ - private findFile(exerciseID: number, name: string) { - return this.findFileBrowser(exerciseID).contains(name).parents('#file-browser-file'); - } - - /** - * Opens a file in the file browser by clicking on it. - */ - openFileWithName(exerciseID: number, name: string) { - this.findFile(exerciseID, name).click().wait(2000); - } - - /** - * Submits the currently saved files by clicking on the submit button and expect GRADED result label. - * @param exerciseID the ID of the exercise - */ - submit(exerciseID: number) { - getExercise(exerciseID).find('#submit_button').click(); - getExercise(exerciseID).find('#result-score-badge', { timeout: 200000 }).should('contain.text', 'GRADED').and('be.visible'); - } - - /** - * Submits the currently saved files by clicking on the submit button and expect PRACTICE result label. - * @param exerciseID the ID of the exercise - */ - submitPractice(exerciseID: number) { - getExercise(exerciseID).find('#submit_button').click(); - getExercise(exerciseID).find('#result-score-badge', { timeout: 200000 }).should('contain.text', 'PRACTICE').and('be.visible'); - } - - /** - * Creates a file at root level in the file browser. - * @param exerciseID the ID of the exercise - * @param fileName the name of the new file (e.g. "Policy.java") - */ - createFileInRootFolder(exerciseID: number, fileName: string) { - const postRequestId = 'createFile' + fileName; - const requestPath = `${BASE_API}/repository/*/file?file=${fileName}`; - getExercise(exerciseID).find('[id="create_file_root"]').click().wait(500); - cy.intercept(POST, requestPath).as(postRequestId); - getExercise(exerciseID).find('#file-browser-create-node').type(fileName).wait(500).type('{enter}'); - cy.wait('@' + postRequestId) - .its('response.statusCode') - .should('eq', 200); - this.findFileBrowser(exerciseID).contains(fileName).should('be.visible').wait(500); - } - - /** - * Creates a file at root level (in the main package) in the file browser. - * @param exerciseID the ID of the exercise - * @param fileName the name of the new file (e.g. "Policy.java") - * @param packageName the name of the package (e.g. "de.test") - */ - createFileInRootPackage(exerciseID: number, fileName: string, packageName: string) { - const packagePath = packageName.replace(/\./g, '/'); - const filePath = `src/${packagePath}/${fileName}`; - const postRequestId = 'createFile' + fileName; - const requestPath = `${BASE_API}/repository/*/file?file=${filePath}`; - getExercise(exerciseID).find('[id="file-browser-folder-create-file"]').eq(2).click().wait(500); - cy.intercept(POST, requestPath).as(postRequestId); - getExercise(exerciseID).find('#file-browser-create-node').type(fileName).wait(500).type('{enter}'); - cy.wait('@' + postRequestId) - .its('response.statusCode') - .should('eq', 200); - this.findFileBrowser(exerciseID).contains(fileName).should('be.visible').wait(500); - } - - /** - * @returns the root element of the result panel. This can be used for further querying inside this panel - */ - getResultPanel() { - return cy.get('#result'); - } - - /** - * @returns the element containing the result score percentage. - */ - getResultScore() { - cy.reloadUntilFound('#result-score'); - return cy.get('#result-score'); - } - - /** - * @param exerciseID the ID of the exercise - * @returns the element containing the result score percentage. - */ - getResultScoreFromExercise(exerciseID: number) { - return getExercise(exerciseID).find('#result-score'); - } - - /** - * @returns the root element of the panel, which shows the CI build output. - */ - getBuildOutput() { - return cy.get('#cardBuildOutput'); - } - - toggleCompressFileTree(exerciseID: number) { - return getExercise(exerciseID).find('#compress_tree').click(); - } - - /** - * General method for entering, submitting and verifying something in the online editor. - */ - makeSubmissionAndVerifyResults(exerciseID: number, submission: ProgrammingExerciseSubmission, verifyOutput: () => void) { - // Decompress the file tree to access the parent folder - this.toggleCompressFileTree(exerciseID); - // We delete all existing files, so we can create new files and don't have to delete their already existing content - for (const deleteFile of submission.deleteFiles) { - this.deleteFile(exerciseID, deleteFile); - } - this.typeSubmission(exerciseID, submission); - this.submit(exerciseID); - verifyOutput(); - } - - /** - * Starts the participation in the test programming exercise. - */ - startParticipation(courseId: number, exerciseId: number, credentials: CypressCredentials) { - // For shorter intervals, the reload may come before the app can render the elements. - const reloadInterval = 4000; - cy.login(credentials, '/'); - cy.url().should('include', '/courses'); - cy.log('Participating in the programming exercise as a student...'); - courseList.openCourse(courseId!); - cy.url().should('include', '/exercises'); - courseOverview.startExercise(exerciseId, reloadInterval); - cy.reloadUntilFound('#open-exercise-' + exerciseId, reloadInterval); - courseOverview.openRunningProgrammingExercise(exerciseId); - } -} - -/** - * A class which encapsulates a programming exercise submission taken from the k6 resources. - * - * @param files An array of containers, which contain the file path of the changed file as well as its name. - */ -export class ProgrammingExerciseSubmission { - deleteFiles: string[]; - createFilesInRootFolder: boolean; - files: ProgrammingExerciseFile[]; - expectedResult: string; - packageName?: string; -} - -class ProgrammingExerciseFile { - name: string; - path: string; -} diff --git a/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseCreationPage.ts b/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseCreationPage.ts deleted file mode 100644 index aba55301646a..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseCreationPage.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { POST, PROGRAMMING_EXERCISE_BASE, ProgrammingLanguage } from '../../../constants'; -import { Dayjs } from 'dayjs/esm'; - -const OWL_DATEPICKER_ARIA_LABEL_DATE_FORMAT = 'MMMM D, YYYY'; - -/** - * A class which encapsulates UI selectors and actions for the programming exercise creation page. - */ -export class ProgrammingExerciseCreationPage { - /** - * @param title the title of the programming exercise - */ - setTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - /** - * @param shortName the short name of the programming exercise - */ - setShortName(shortName: string) { - cy.get('#field_shortName').clear().type(shortName); - } - - /** - * @param programmingLanguage the programming language of the programming exercise - */ - setProgrammingLanguage(programmingLanguage: ProgrammingLanguage) { - cy.get('#field_programmingLanguage').select(programmingLanguage); - } - - /** - * @param packageName the package name of the programming exercise - */ - setPackageName(packageName: string) { - cy.get('#field_packageName').clear().type(packageName); - } - - /** - * @param points Achievable points in the exercise - */ - setPoints(points: number) { - cy.get('#field_points').clear().type(points.toString()); - } - - /** - * Allows the usage of the online editor. - */ - checkAllowOnlineEditor() { - cy.get('#field_allowOnlineEditor').check(); - } - - /** - * Generates the programming exercise. - * @returns the chainable of the request to make further verifications - */ - generate() { - cy.intercept(POST, `${PROGRAMMING_EXERCISE_BASE}/setup`).as('createProgrammingExercise'); - cy.get('#save-entity').click(); - // Creating a programming exercise can take quite a while, so we increase the default timeout here - return cy.wait('@createProgrammingExercise', { timeout: 60000 }); - } - - import() { - cy.intercept(POST, `${PROGRAMMING_EXERCISE_BASE}/import/*`).as('programmingExerciseImport'); - cy.get('#save-entity').click(); - // Creating a programming exercise can take quite a while, so we increase the default timeout here - return cy.wait('@programmingExerciseImport', { timeout: 60000 }); - } - - /** - * Sets the Due Date field by using the owl datepicker - * @param date - * */ - setDueDate(date: Dayjs) { - cy.get('#programming-exercise-due-date-picker').click(); - - // Makes sure that popup is visible before we choose a date - cy.get('.owl-dt-popup').should('be.visible'); - - const ariaLabelDate = date.format(OWL_DATEPICKER_ARIA_LABEL_DATE_FORMAT); - cy.get(`td[aria-label="${ariaLabelDate}"]`).click(); - - // There is a race condition, where an event listener already attaches and is ready to process the actual click on a date element - // but no event listener has been attached yet to close the modal. Thus, we need to keep clicking until the calendar modal is closed. - // Another idea would be to use cy.wait(), however it's not recommended by the developers. - // Cypress-pipe does not retry any Cypress commands so we need to click on the element using jQuery method "$el.trigger('click')" and not "cy.click()" - // See https://www.cypress.io/blog/2019/01/22/when-can-the-test-click for more information - const click = ($el: JQuery) => $el.trigger('click'); - - cy.get('.owl-dt-control-content.owl-dt-control-button-content') - .should('be.visible') - .contains('Set') - .pipe(click) - .should(($el) => { - expect($el).to.not.be.visible; - }); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseFeedbackPage.ts b/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseFeedbackPage.ts deleted file mode 100644 index 966f558dafa9..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/programming/ProgrammingExerciseFeedbackPage.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { AbstractExerciseFeedback } from '../AbstractExerciseFeedbackPage'; -import { OnlineEditorPage } from './OnlineEditorPage'; - -/** - * A class which encapsulates UI selectors and actions for a programming exercise feedback page. - */ -export class ProgrammingExerciseFeedbackPage extends AbstractExerciseFeedback { - shouldShowAdditionalFeedback(points: number, feedbackText: string) { - cy.reloadUntilFound(this.ADDITIONAL_FEEDBACK_SELECTOR); - cy.get(this.ADDITIONAL_FEEDBACK_SELECTOR).contains(`${points} Points: ${feedbackText}`).should('be.visible'); - } - - shouldShowCodeFeedback(exerciseID: number, filename: string, feedback: string, points: string, editorPage: OnlineEditorPage) { - editorPage.openFileWithName(exerciseID, filename); - this.findVisibleInlineFeedback().contains(feedback).should('be.visible'); - this.findVisibleInlineFeedback().contains(`${points}P`).should('be.visible'); - } - - private findVisibleInlineFeedback() { - return cy.get('[id*="code-editor-inline-feedback-"]').should('be.visible'); - } - - shouldShowRepositoryLockedWarning() { - cy.get('#repository-locked-warning').should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/programming/ScaFeedbackModal.ts b/src/test/cypress/support/pageobjects/exercises/programming/ScaFeedbackModal.ts deleted file mode 100644 index 14306c2a3fd5..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/programming/ScaFeedbackModal.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the static code analysis feedback modal in the online editor. - */ -export class ScaFeedbackModal { - shouldShowPointChart() { - cy.get('#feedback-chart').should('be.visible'); - } - - shouldShowCodeIssue(feedbackText: string, pointReduction: string) { - // This is a workaround to avoid Cypress only returning the first element matching the id - cy.get('.feedback-list') - .contains(feedbackText) - .scrollIntoView() - .should('be.visible') - .parents('.feedback-item') - .find('.feedback-item__credits') - .scrollIntoView() - .should('contain.text', `-${pointReduction}P`) - .and('be.visible'); - } - - closeModal() { - cy.get('.feedback-header__close').click(); - cy.get('.result-detail-container').should('not.exist'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/quiz/DragAndDropQuiz.ts b/src/test/cypress/support/pageobjects/exercises/quiz/DragAndDropQuiz.ts deleted file mode 100644 index 07a3ec72439e..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/quiz/DragAndDropQuiz.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { EXERCISE_BASE, MODELING_EDITOR_CANVAS, POST } from '../../../constants'; - -export class DragAndDropQuiz { - createDnDQuiz(title: string) { - cy.get('#quiz-import-apollon-dnd-question').should('be.visible').click(); - cy.get('#create-apollon-diagram').should('be.visible').click(); - cy.get('#field_diagram_title').type(title); - cy.get('#save-dnd-quiz').click(); - } - - dragItemIntoDragArea(itemIndex: number) { - cy.get('#drag-item-' + itemIndex).drag('#drop-location'); - } - - setTitle(title: string) { - cy.get('#field_title').type(title); - } - - /** - * Drags an element to the modeling editor canvas - * - * @param x drop location on X-axis - * @param y drop location on Y-axis - */ - dragUsingCoordinates(x: number, y: number) { - cy.get('#modeling-editor-sidebar').children().eq(2).drag(MODELING_EDITOR_CANVAS, { target: { x, y } }); - cy.wait(200); - cy.get(MODELING_EDITOR_CANVAS).trigger('pointerup'); - } - - /** - * Returns minimum and maximum X value of drop location elements - * - * @param $els selected drop location elements - */ - getXAxis($els: JQuery) { - let minX = Number.MAX_SAFE_INTEGER; - let maxX = Number.MIN_SAFE_INTEGER; - $els.each((index, el) => { - const rect = el.getBoundingClientRect(); - if (rect.x < minX) { - minX = rect.x; - } - if (rect.x + rect.width > maxX) { - maxX = rect.x + rect.width; - } - }); - return { minX, maxX }; - } - - activateInteractiveMode() { - const modelingEditorSidebar = cy.get('#modeling-editor-sidebar'); - const container = modelingEditorSidebar.children().eq(0); - const interactiveButton = container.children().eq(1); - interactiveButton.click(); - } - - markElementAsInteractive(nthElementOnCanvas: number, nthChildOfElement: number) { - cy.get(MODELING_EDITOR_CANVAS).children().children().children().eq(nthElementOnCanvas).children().eq(0).children().eq(nthChildOfElement).click(); - } - - generateQuizExercise() { - cy.get('#generate-quiz-exercise').click(); - cy.get('#quiz-save').should('be.visible'); - cy.wait(100); - cy.get('#quiz-save').click(); - } - - waitForQuizExerciseToBeGenerated() { - cy.get('#jhi-text-exercise-heading-edit').should('be.visible'); - } - - previewQuiz() { - cy.get('#preview-quiz').click(); - } - - waitForQuizPreviewToLoad() { - cy.get('.drag-and-drop-area').should('be.visible'); - cy.wait(200); - } - - submit() { - cy.intercept(POST, `${EXERCISE_BASE}/*/submissions/live`).as('createQuizExercise'); - cy.get('#submit-quiz').contains('Submit').click(); - return cy.wait('@createQuizExercise'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/quiz/MultipleChoiceQuiz.ts b/src/test/cypress/support/pageobjects/exercises/quiz/MultipleChoiceQuiz.ts deleted file mode 100644 index d94e033d0306..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/quiz/MultipleChoiceQuiz.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { EXERCISE_BASE, POST } from '../../../constants'; -import { getExercise } from '../../../utils'; - -export class MultipleChoiceQuiz { - tickAnswerOption(exerciseID: number, optionNumber: number, quizQuestionId = 0) { - getExercise(exerciseID) - .find('#question' + quizQuestionId) - .find('#answer-option-' + optionNumber) - .find('#mc-answer-selection-' + optionNumber) - .click(); - } - - submit() { - cy.intercept(POST, `${EXERCISE_BASE}/*/submissions/live`).as('createQuizExercise'); - cy.get('#submit-quiz').scrollIntoView(); - cy.get('#submit-quiz').click(); - return cy.wait('@createQuizExercise'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/quiz/QuizExerciseCreationPage.ts b/src/test/cypress/support/pageobjects/exercises/quiz/QuizExerciseCreationPage.ts deleted file mode 100644 index c102704a3e0b..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/quiz/QuizExerciseCreationPage.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Dayjs } from 'dayjs/esm'; - -import { BASE_API, POST, QUIZ_EXERCISE_BASE } from '../../../constants'; -import { enterDate } from '../../../utils'; - -export class QuizExerciseCreationPage { - setTitle(title: string) { - cy.get('#field_title').type(title); - } - - setVisibleFrom(date: Dayjs) { - enterDate('#pick-releaseDate', date); - } - - addMultipleChoiceQuestion(title: string, points = 1) { - cy.get('#quiz-add-mc-question').click(); - cy.get('#mc-question-title').type(title); - cy.get('#score').clear().type(points.toString()); - cy.fixture('exercise/quiz/multiple_choice/question.txt').then((fileContent) => { - cy.get('.ace_text-input').focus().clear().type(fileContent); - }); - } - - addShortAnswerQuestion(title: string) { - cy.get('#quiz-add-short-answer-question').click(); - cy.get('#short-answer-question-title').type(title); - cy.fixture('exercise/quiz/short_answer/question.txt').then((fileContent) => { - cy.get('.ace_text-input').focus().clear().type(fileContent); - cy.get('#short-answer-show-visual').click(); - }); - } - - addDragAndDropQuestion(title: string) { - cy.get('#quiz-add-dnd-question').click(); - cy.get('#drag-and-drop-question-title').type(title); - this.uploadDragAndDropBackground().then(() => { - cy.wait(2000); - cy.get('.click-layer').trigger('mousedown', { x: 50, y: 50 }).trigger('mousemove', { x: 500, y: 300 }).trigger('mouseup'); - }); - this.createDragAndDropItem('Rick Astley'); - cy.get('#drag-item-0').drag('#drop-location'); - cy.fixture('exercise/quiz/drag_and_drop/question.txt').then((fileContent) => { - cy.get('.ace_text-input').focus().clear().type(fileContent); - }); - } - - createDragAndDropItem(text: string) { - cy.get('#add-text-drag-item').click(); - cy.get('#drag-item-0-text').clear().type(text); - } - - uploadDragAndDropBackground() { - cy.get('#background-image-input-form').children().eq(0).attachFile('exercise/quiz/drag_and_drop/background.jpg'); - cy.intercept(POST, `${BASE_API}/fileUpload*`).as('uploadBackground'); - cy.get('#background-image-input-form').children().eq(1).click(); - return cy.wait('@uploadBackground'); - } - - /** - * @return the response of the request - */ - saveQuiz() { - cy.intercept(POST, QUIZ_EXERCISE_BASE).as('createQuizExercise'); - cy.get('#quiz-save').click(); - return cy.wait('@createQuizExercise'); - } - - import() { - cy.intercept(POST, `${QUIZ_EXERCISE_BASE}/import/*`).as('quizExerciseImport'); - cy.get('#quiz-save').click(); - return cy.wait('@quizExerciseImport'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/quiz/ShortAnswerQuiz.ts b/src/test/cypress/support/pageobjects/exercises/quiz/ShortAnswerQuiz.ts deleted file mode 100644 index a4003d260067..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/quiz/ShortAnswerQuiz.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EXERCISE_BASE, POST } from '../../../constants'; - -export class ShortAnswerQuiz { - getQuizBody() { - return cy.get('#question0').children().first(); - } - - typeAnswer(line: number, column: number, quizQuestionId: number, answer: string) { - this.getQuizBody().find(`#solution-${line}-${column}-${quizQuestionId}`).type(answer); - } - - submit() { - cy.intercept(POST, `${EXERCISE_BASE}/*/submissions/live`).as('createQuizExercise'); - cy.get('#submit-quiz').scrollIntoView(); - cy.get('#submit-quiz').click(); - return cy.wait('@createQuizExercise'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/text/TextEditorPage.ts b/src/test/cypress/support/pageobjects/exercises/text/TextEditorPage.ts deleted file mode 100644 index aa53321732c2..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/text/TextEditorPage.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { EXERCISE_BASE, PUT } from '../../../constants'; -import { getExercise } from '../../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the text editor page. - */ -export class TextEditorPage { - typeSubmission(exerciseID: number, submission: string) { - getExercise(exerciseID).find('#text-editor').type(submission, { parseSpecialCharSequences: false }); - } - - clearSubmission(exerciseID: number) { - getExercise(exerciseID).find('#text-editor').clear(); - } - - checkCurrentContent(exerciseID: number, expectedContent: string) { - cy.fixture(expectedContent).then((text) => { - getExercise(exerciseID).find('#text-editor').should('have.value', text); - }); - } - - /** - * Saves the text submission and continues to the next exercise in the exam. This button is only available in exam mode! - */ - saveAndContinue() { - cy.intercept(PUT, `${EXERCISE_BASE}/*/text-submissions`).as('savedSubmission'); - cy.get('#save').click(); - return cy.wait('@savedSubmission'); - } - - submit() { - cy.intercept(PUT, `${EXERCISE_BASE}/*/text-submissions`).as('textSubmission'); - cy.get('#submit button').click(); - return cy.wait('@textSubmission'); - } - - shouldShowExerciseTitleInHeader(exerciseTitle: string) { - cy.get('#participation-header').contains(exerciseTitle).should('be.visible'); - } - - shouldShowProblemStatement() { - cy.get('#problem-statement').should('be.visible'); - } - - shouldShowNumberOfWords(numberOfWords: number) { - cy.get('#word-count').should('contain.text', numberOfWords).and('be.visible'); - } - - shouldShowNumberOfCharacters(numberOfWords: number) { - cy.get('#character-count').should('contain.text', numberOfWords).and('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseCreationPage.ts b/src/test/cypress/support/pageobjects/exercises/text/TextExerciseCreationPage.ts deleted file mode 100644 index 20c01d7f6b52..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseCreationPage.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Dayjs } from 'dayjs/esm'; - -import { TEXT_EXERCISE_BASE } from '../../../constants'; -import { POST } from '../../../constants'; -import { enterDate } from '../../../utils'; - -/** - * A class which encapsulates UI selectors and actions for the text exercise creation page. - */ -export class TextExerciseCreationPage { - /** - * @param title the title of the text exercise - */ - typeTitle(title: string) { - cy.get('#field_title').clear().type(title); - } - - setReleaseDate(date: Dayjs) { - enterDate('#pick-releaseDate', date); - } - - setDueDate(date: Dayjs) { - enterDate('#pick-dueDate', date); - } - - setAssessmentDueDate(date: Dayjs) { - enterDate('#pick-assessmentDueDate', date); - } - - typeMaxPoints(maxPoints: number) { - cy.get('#field_points').type(maxPoints.toString()); - } - - typeProblemStatement(statement: string) { - this.typeText('#problemStatement', statement); - } - - typeExampleSolution(statement: string) { - this.typeText('#exampleSolution', statement); - } - - typeAssessmentInstructions(statement: string) { - this.typeText('#gradingInstructions', statement); - } - - create() { - cy.intercept(POST, TEXT_EXERCISE_BASE).as('textExerciseCreation'); - cy.get('#save-entity').click(); - return cy.wait('@textExerciseCreation'); - } - - import() { - cy.intercept(POST, `${TEXT_EXERCISE_BASE}/import/*`).as('textExerciseImport'); - cy.get('#save-entity').click(); - return cy.wait('@textExerciseImport'); - } - - private typeText(selector: string, text: string) { - cy.get(selector).find('.ace_content').type(text); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionCreationPage.ts b/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionCreationPage.ts deleted file mode 100644 index 812c7d63b998..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionCreationPage.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { EXERCISE_BASE, POST } from '../../../constants'; - -/** - * A class which encapsulates UI selectors and actions for the text exercise example submission creation page. - */ -export class TextExerciseExampleSubmissionCreationPage { - getInstructionsRootElement() { - return cy.get('#instructions'); - } - - typeExampleSubmission(example: string) { - cy.get('#example-text-submission-input').type(example, { parseSpecialCharSequences: false }); - } - - clickCreateNewExampleSubmission() { - cy.intercept(POST, `${EXERCISE_BASE}/*/example-submissions`).as('createExampleSubmission'); - cy.get('#create-example-submission').click(); - return cy.wait('@createExampleSubmission'); - } - - showsExerciseTitle(exerciseTitle: string) { - this.getInstructionsRootElement().contains(exerciseTitle).should('be.visible'); - } - - showsProblemStatement(problemStatement: string) { - this.getInstructionsRootElement().contains(problemStatement).should('be.visible'); - } - - showsExampleSolution(exampleSolution: string) { - this.getInstructionsRootElement().contains(exampleSolution).should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionsPage.ts b/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionsPage.ts deleted file mode 100644 index 33be82d683c5..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseExampleSubmissionsPage.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * A class which encapsulates UI selectors and actions for the text exercise example submissions page. - */ -export class TextExerciseExampleSubmissionsPage { - clickCreateExampleSubmission() { - cy.get('#create-example-submission').click(); - } -} diff --git a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseFeedbackPage.ts b/src/test/cypress/support/pageobjects/exercises/text/TextExerciseFeedbackPage.ts deleted file mode 100644 index 2ebb6d738ee7..000000000000 --- a/src/test/cypress/support/pageobjects/exercises/text/TextExerciseFeedbackPage.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { AbstractExerciseFeedback } from '../AbstractExerciseFeedbackPage'; - -/** - * A class which encapsulates UI selectors and actions for a text exercise feedback page. - */ -export class TextExerciseFeedbackPage extends AbstractExerciseFeedback { - shouldShowTextFeedback(feedbackIndex: number, feedback: string) { - cy.get('#text-feedback-' + feedbackIndex) - .contains(feedback) - .should('be.visible'); - } -} diff --git a/src/test/cypress/support/pageobjects/lecture/LectureCreationPage.ts b/src/test/cypress/support/pageobjects/lecture/LectureCreationPage.ts deleted file mode 100644 index 7415fef89ade..000000000000 --- a/src/test/cypress/support/pageobjects/lecture/LectureCreationPage.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Dayjs } from 'dayjs/esm'; - -import { BASE_API, POST } from '../../constants'; - -export class LectureCreationPage { - setTitle(title: string) { - cy.get('#field_title').type(title); - } - - save() { - cy.intercept(POST, `${BASE_API}/lectures`).as('createLecture'); - cy.get('#save-entity').click(); - return cy.wait('@createLecture'); - } - - typeDescription(description: string) { - cy.get('.ace_content').type(description, { parseSpecialCharSequences: false }); - } - - setVisibleDate(date: Dayjs) { - cy.get('#visible-date').find('#date-input-field').type(date.toString()); - } - - setStartDate(date: Dayjs) { - cy.get('#start-date').find('#date-input-field').type(date.toString()); - } - - setEndDate(date: Dayjs) { - cy.get('#end-date').find('#date-input-field').type(' ').clear().type(date.toString()); - } -} diff --git a/src/test/cypress/support/pageobjects/lecture/LectureManagementPage.ts b/src/test/cypress/support/pageobjects/lecture/LectureManagementPage.ts deleted file mode 100644 index 215e2122afbc..000000000000 --- a/src/test/cypress/support/pageobjects/lecture/LectureManagementPage.ts +++ /dev/null @@ -1,73 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Lecture } from 'app/entities/lecture.model'; - -import { BASE_API, DELETE, POST } from '../../constants'; - -export class LectureManagementPage { - clickCreateLecture() { - cy.get('#jh-create-entity').click(); - } - - deleteLecture(lecture: Lecture) { - this.getLecture(lecture.id!).find('#delete-lecture').click(); - cy.get('#delete').should('be.disabled'); - cy.get('#confirm-entity-name').type(lecture.title!); - cy.intercept(DELETE, `${BASE_API}/lectures/*`).as('deleteLecture'); - cy.get('#delete').click(); - return cy.wait('@deleteLecture'); - } - - getLectures() { - return cy.get('#lectures'); - } - - getLecture(lectureId: number) { - return cy.get(`#lecture-${lectureId}`); - } - - getLectureContainer() { - return cy.get('#lecture-preview'); - } - - openUnitsPage(lectureId: number) { - this.getLecture(lectureId).find('#units').click(); - } - - openCreateUnit(type: UnitType) { - this.getUnitCreationCard().find(type).click(); - } - - getUnitCreationCard() { - return cy.get('#unit-creation'); - } - - addTextUnit(name: string, text: string, releaseDate = dayjs()) { - this.openCreateUnit(UnitType.TEXT); - cy.get('#name').type(name); - cy.get('#pick-releaseDate').find('#date-input-field').type(releaseDate.toString()); - cy.get('.ace_content').type(text, { parseSpecialCharSequences: false }); - return this.submitUnit(); - } - - addExerciseUnit(exerciseId: number) { - this.openCreateUnit(UnitType.EXERCISE); - const exerciseRow = '#exercise-' + exerciseId; - cy.reloadUntilFound(exerciseRow); - cy.get(exerciseRow).click(); - return this.submitUnit('#createButton'); - } - - submitUnit(buttonId = '#submitButton') { - cy.intercept(POST, `${BASE_API}/lectures/*/*`).as('createUnit'); - cy.get(buttonId).click(); - return cy.wait('@createUnit'); - } -} - -enum UnitType { - TEXT = '#createTextUnitButton', - EXERCISE = '#createExerciseUnitButton', - VIDEO = '#createVideoUnitButton', - FILE = '#createFileUploadUnitButton', -} diff --git a/src/test/cypress/support/requests/ArtemisRequests.ts b/src/test/cypress/support/requests/ArtemisRequests.ts deleted file mode 100644 index 6172cd6fc253..000000000000 --- a/src/test/cypress/support/requests/ArtemisRequests.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CommunicationAPIRequests } from './CommunicationAPIRequests'; -import { CourseManagementAPIRequests } from './CourseManagementAPIRequests'; -import { ExamAPIRequests } from './ExamAPIRequests'; -import { ExerciseAPIRequests } from './ExerciseAPIRequests'; -import { UserManagementAPIRequests } from './UserManagementAPIRequest'; - -/** - * A class which encapsulates all cypress requests, which can be sent to Artemis. - */ -export class ArtemisRequests { - communication = new CommunicationAPIRequests(); - courseManagement = new CourseManagementAPIRequests(); - exam = new ExamAPIRequests(); - exercise = new ExerciseAPIRequests(); - userManagement = new UserManagementAPIRequests(); -} diff --git a/src/test/cypress/support/requests/CommunicationAPIRequests.ts b/src/test/cypress/support/requests/CommunicationAPIRequests.ts deleted file mode 100644 index c405b574d7cf..000000000000 --- a/src/test/cypress/support/requests/CommunicationAPIRequests.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { Course } from 'app/entities/course.model'; -import { Exercise } from 'app/entities/exercise.model'; -import { Lecture } from 'app/entities/lecture.model'; -import { ChannelDTO, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; -import { GroupChat } from 'app/entities/metis/conversation/group-chat.model'; -import { Post } from 'app/entities/metis/post.model'; - -import { COURSE_BASE, GET, POST, PUT } from '../constants'; -import { CypressCredentials } from '../users'; -import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; -import Chainable = Cypress.Chainable; - -/** - * A class which encapsulates all API requests related to communications. - */ -export class CommunicationAPIRequests { - /** - * Creates a new course post. - * - * @param course - The course to which the post belongs. - * @param content - The content of the post. - * @param channel - The channel the post belongs to - * @returns A Cypress.Chainable> representing the API request response. - */ - createCoursePost(course: Course, content: string, channel: ChannelDTO) { - const body = { - content, - course: { - id: course.id, - title: course.title, - }, - conversation: { - id: channel.id, - type: channel.type, - }, - displayPriority: 'NONE', - visibleForStudents: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/messages`, body }); - } - - /** - * Creates a new course message channel. - * - * @param course - The course to which the message channel belongs. - * @param name - The name of the message channel. - * @param description - The description of the message channel. - * @param isAnnouncementChannel - Set to true if the channel is an announcement channel. - * @param isPublic - Set to true if the channel is public. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseMessageChannel(course: Course, name: string, description: string, isAnnouncementChannel: boolean, isPublic: boolean) { - const body = { - description, - isAnnouncementChannel, - isPublic, - name, - type: 'channel', - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/channels`, body }); - } - - /** - * Get course-wide channels of a course - * - * @param courseId - The id of the course - * @returns A Cypress.Chainable with the course-wide channels of the course - */ - getCourseWideChannels(courseId: number): Chainable { - return cy - .request({ method: GET, url: `${COURSE_BASE}/${courseId}/conversations` }) - .then((response) => response.body.filter((conv: ConversationDTO) => getAsChannelDTO(conv)?.isCourseWide === true)); - } - - /** - * Retrieves the exercise channel for a given course and exercise. - * - * @param courseId - The ID of the course. - * @param exerciseId - The ID of the exercise. - * @returns A Cypress.Chainable> representing the API request response. - */ - getExerciseChannel(courseId: number, exerciseId: number) { - return cy.request({ method: GET, url: `${COURSE_BASE}/${courseId}/exercises/${exerciseId}/channel` }); - } - - /** - * Retrieves the lecture channel for a given course and lecture. - * - * @param courseId - The ID of the course. - * @param lectureId - The ID of the lecture. - * @returns A Cypress.Chainable> representing the API request response. - */ - getLectureChannel(courseId: number, exerciseId: number) { - return cy.request({ method: GET, url: `${COURSE_BASE}/${courseId}/lectures/${exerciseId}/channel` }); - } - - /** - * Creates a new course message group chat. - * - * @param course - The course to which the group chat belongs. - * @param users - An array of usernames of users to add to the group chat. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseMessageGroupChat(course: Course, users: Array) { - const body = users; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/group-chats`, body }); - } - - /** - * Creates a new course message. - * - * @param course - The course to which the message belongs. - * @param targetId - The ID of the conversation target (channel or group chat). - * @param type - The type of conversation target (e.g., 'channel' or 'groupChat'). - * @param message - The content of the message. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseMessage(course: Course, targetId: number, type: string, message: string) { - const body = { - content: message, - conversation: { - id: targetId, - type, - }, - displayPriority: 'NONE', - visibleForStudents: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/messages`, body }); - } - - /** - * Creates a new course message. - * - * @param course - The course to which the message belongs. - * @param targetId - The ID of the conversation target channel. - * @param message - The content of the message. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseWideMessage(course: Course, targetId: number, message: string) { - const body = { - content: message, - conversation: { - id: targetId, - type: 'channel', - }, - displayPriority: 'NONE', - visibleForStudents: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/messages`, body }); - } - - /** - * Updates the name of a course message group chat. - * - * @param course - The course to which the group chat belongs. - * @param groupChat - The group chat to update. - * @param name - The new name of the group chat. - * @returns A Cypress.Chainable> representing the API request response. - */ - updateCourseMessageGroupChatName(course: Course, groupChat: GroupChat, name: string) { - const body = { - name, - type: 'groupChat', - }; - return cy.request({ method: PUT, url: `${COURSE_BASE}/${course.id}/group-chats/${groupChat.id}`, body }); - } - - /** - * Joins a user into a channel. - * - * @param course - The course to which the channel belongs. - * @param channelId - The id of the channel to join. - * @param user - The user's credentials. - * @returns A Cypress.Chainable> representing the API request response. - */ - joinUserIntoChannel(course: Course, channelId: number, user: CypressCredentials) { - const body = [user.username]; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/channels/${channelId}/register`, body }); - } - - /** - * Creates a new course post reply. - * - * @param course - The course to which the post belongs. - * @param post - The post to which the reply is made. - * @param content - The content of the post reply. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseMessageReply(course: Course, post: Post, content: string) { - const body = { - content, - post, - resolvesPost: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/answer-messages`, body }); - } - - /** - * Creates a new course exercise post. - * - * @param course - The course to which the post belongs. - * @param exercise - The exercise to which the post is associated. - * @param title - The title of the post. - * @param content - The content of the post. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseExercisePost(course: Course, exercise: Exercise, title: string, content: string) { - const body = { - content, - displayPriority: 'NONE', - exercise: { - id: exercise.id, - title: exercise.title, - type: exercise.type, - }, - tags: [], - title, - visibleForStudents: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}/${course.id}/posts`, body }); - } - - /** - * Creates a new course lecture post. - * - * @param course - The course to which the post belongs. - * @param lecture - The lecture to which the post is associated. - * @param title - The title of the post. - * @param content - The content of the post. - * @returns A Cypress.Chainable> representing the API request response. - */ - createCourseLecturePost(course: Course, lecture: Lecture, title: string, content: string) { - const body = { - content, - displayPriority: 'NONE', - lecture: { - id: lecture.id, - title: lecture.title, - }, - tags: [], - title, - visibleForStudents: true, - }; - return cy.request({ method: POST, url: `${COURSE_BASE}${course.id}/posts`, body }); - } -} diff --git a/src/test/cypress/support/requests/CourseManagementAPIRequests.ts b/src/test/cypress/support/requests/CourseManagementAPIRequests.ts deleted file mode 100644 index 60fc4002baed..000000000000 --- a/src/test/cypress/support/requests/CourseManagementAPIRequests.ts +++ /dev/null @@ -1,181 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Course, CourseInformationSharingConfiguration } from 'app/entities/course.model'; - -import lectureTemplate from '../../fixtures/lecture/template.json'; -import { BASE_API, COURSE_ADMIN_BASE, COURSE_BASE, DELETE, POST } from '../constants'; -import { CypressCredentials } from '../users'; -import { generateUUID, titleLowercase } from '../utils'; - -/** - * A class which encapsulates all API requests related to course management. - */ -export class CourseManagementAPIRequests { - /** - * Creates a course with the specified title and short name. - * @param options An object containing the options for creating the course - * - customizeGroups: whether the predefined groups should be used (so we don't have to wait more than a minute between course and programming exercise creation) - * - courseName: the title of the course (will generate default name if not provided) - * - courseShortName: the short name (will generate default name if not provided) - * - start: the start date of the course (default: now() - 2 hours) - * - end: the end date of the course (default: now() + 2 hours) - * - iconFileName: the course icon file name (default: undefined) - * - iconFile: the course icon file blob (default: undefined) - * - allowCommunication: if communication should be enabled for the course - * - allowMessaging: if messaging should be enabled for the course - * @returns request response - */ - createCourse( - options: { - customizeGroups?: boolean; - courseName?: string; - courseShortName?: string; - start?: dayjs.Dayjs; - end?: dayjs.Dayjs; - iconFileName?: string; - iconFile?: Blob; - allowCommunication?: boolean; - allowMessaging?: boolean; - } = {}, - ): Cypress.Chainable> { - const { - customizeGroups = false, - courseName = 'Course ' + generateUUID(), - courseShortName = 'cypress' + generateUUID(), - start = dayjs().subtract(2, 'hours'), - end = dayjs().add(2, 'hours'), - iconFileName, - iconFile, - allowCommunication = true, - allowMessaging = true, - } = options; - - const course = new Course(); - course.title = courseName; - course.shortName = courseShortName; - course.testCourse = true; - course.startDate = start; - course.endDate = end; - - if (allowCommunication && allowMessaging) { - course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_AND_MESSAGING; - course.courseInformationSharingMessagingCodeOfConduct = 'Code of Conduct'; - } else if (allowCommunication) { - course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_ONLY; - } else { - course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.DISABLED; - } - - const allowGroupCustomization: boolean = Cypress.env('allowGroupCustomization'); - if (customizeGroups && allowGroupCustomization) { - course.studentGroupName = Cypress.env('studentGroupName'); - course.teachingAssistantGroupName = Cypress.env('tutorGroupName'); - course.editorGroupName = Cypress.env('editorGroupName'); - course.instructorGroupName = Cypress.env('instructorGroupName'); - } - - const formData = new FormData(); - formData.append('course', new File([JSON.stringify(course)], 'course', { type: 'application/json' })); - - if (iconFile) { - formData.append('file', iconFile, iconFileName); - } - - return cy.request({ - url: COURSE_ADMIN_BASE, - method: POST, - body: formData, - }); - } - - /** - * Deletes the course with the specified id. - * - * @param course the course - * @param admin the admin user - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteCourse(course: Course, admin: CypressCredentials) { - if (course) { - cy.login(admin); - // Sometimes the server fails with a ConstraintViolationError if we delete the course immediately after a login - cy.wait(500); - return cy.request({ method: DELETE, url: `${COURSE_ADMIN_BASE}/${course.id}`, retryOnStatusCodeFailure: true }); - } - } - - /** - * Adds the specified student to the course. - * - * @param course - The course to which the student will be added. - * @param user - The user (student) to be added to the course. - * @returns A Cypress.Chainable> representing the API request response. - */ - addStudentToCourse(course: Course, user: CypressCredentials) { - return this.addUserToCourse(course.id!, user.username, 'students'); - } - - /** - * Adds the specified user to the tutor group in the course. - * - * @param course - The course to which the tutor will be added. - * @param user - The user (tutor) to be added to the course. - * @returns A Cypress.Chainable> representing the API request response. - */ - addTutorToCourse(course: Course, user: CypressCredentials) { - return this.addUserToCourse(course.id!, user.username, 'tutors'); - } - - /** - * Adds the specified user to the instructor group in the course. - * - * @param course - The course to which the instructor will be added. - * @param user - The user (instructor) to be added to the course. - * @returns A Cypress.Chainable> representing the API request response. - */ - addInstructorToCourse(course: Course, user: CypressCredentials) { - return this.addUserToCourse(course.id!, user.username, 'instructors'); - } - - private addUserToCourse(courseId: number, username: string, roleIdentifier: string) { - return cy.request({ method: POST, url: `${COURSE_BASE}/${courseId}/${roleIdentifier}/${username}` }); - } - - /** - * Deletes a lecture with the specified lecture ID. - * - * @param lectureId - The ID of the lecture to be deleted. - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteLecture(lectureId: number) { - return cy.request({ - url: `${BASE_API}/lectures/${lectureId}`, - method: DELETE, - }); - } - - /** - * Creates a new lecture for the specified course with various options. - * - * @param course - The course to which the lecture belongs. - * @param title - The title of the lecture (optional, default: auto-generated). - * @param startDate - The start date and time of the lecture (optional, default: current date and time). - * @param endDate - The end date and time of the lecture (optional, default: current date and time + 10 minutes). - * @returns A Cypress.Chainable> representing the API request response. - */ - createLecture(course: Course, title = 'Lecture ' + generateUUID(), startDate = dayjs(), endDate = dayjs().add(10, 'minutes')) { - const body = { - ...lectureTemplate, - course, - title, - startDate, - endDate, - channelName: 'lecture-' + titleLowercase(title), - }; - return cy.request({ - url: `${BASE_API}/lectures`, - method: POST, - body, - }); - } -} diff --git a/src/test/cypress/support/requests/ExamAPIRequests.ts b/src/test/cypress/support/requests/ExamAPIRequests.ts deleted file mode 100644 index 86c92cbbc762..000000000000 --- a/src/test/cypress/support/requests/ExamAPIRequests.ts +++ /dev/null @@ -1,168 +0,0 @@ -import examTemplate from '../../fixtures/exam/template.json'; -import { COURSE_BASE, DELETE, Exercise, POST } from '../constants'; -import { CypressCredentials } from '../users'; -import { dayjsToString, generateUUID, titleLowercase } from '../utils'; -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import dayjs from 'dayjs/esm'; - -/** - * A class which encapsulates all API requests related to exams. - */ -export class ExamAPIRequests { - /** - * Creates an exam for the specified course with various options. - * @param options - An object containing the options for creating the exam. - * - course: The course to which the exam belongs (optional, default: undefined). - * - title: The title of the exam (optional, default: auto-generated). - * - testExam: Set to true to create a test exam (optional, default: false). - * - visibleDate: The date when the exam becomes visible (optional, default: current date). - * - startDate: The start date of the exam (optional, default: current date + 1 day). - * - endDate: The end date of the exam (optional, default: current date + 2 days). - * - examMaxPoints: The maximum points achievable in the exam (optional, default: undefined). - * - numberOfExercisesInExam: The number of exercises in the exam (optional, default: undefined). - * - numberOfCorrectionRoundsInExam: The number of correction rounds for the exam (optional, default: undefined). - * - workingTime: The allowed working time for the exam in seconds (optional, default: 86400 seconds or 1 day). - * - examStudentReviewStart: The date when students can start reviewing their exam (optional, default: undefined). - * - examStudentReviewEnd: The date when students can no longer review their exam (optional, default: undefined). - * - publishResultsDate: The date when exam results will be published (optional, default: undefined). - * - gracePeriod: The grace period in seconds for late submissions (optional, default: undefined). - * - channelName: The channel name for the exam (optional, default: auto-generated based on title). - * @returns A Cypress.Chainable> representing the API request response. - */ - createExam(options: { - course?: Course; - title?: string; - testExam?: boolean; - visibleDate?: dayjs.Dayjs; - startDate?: dayjs.Dayjs; - endDate?: dayjs.Dayjs; - examMaxPoints?: number; - numberOfExercisesInExam?: number; - numberOfCorrectionRoundsInExam?: number; - workingTime?: number; - examStudentReviewStart?: dayjs.Dayjs; - examStudentReviewEnd?: dayjs.Dayjs; - publishResultsDate?: dayjs.Dayjs; - gracePeriod?: number; - channelName?: string; - }): Cypress.Chainable> { - const tempTitle = 'exam' + generateUUID(); - - const { - course, - title = tempTitle, - testExam = false, - visibleDate = dayjsToString(dayjs().subtract(1, 'day')), - startDate = dayjsToString(dayjs().add(1, 'day')), - endDate = dayjsToString(dayjs().add(2, 'day')), - examMaxPoints = 10, - numberOfExercisesInExam = 1, - numberOfCorrectionRoundsInExam = 1, - workingTime = 86400, - examStudentReviewStart = null, - examStudentReviewEnd = null, - publishResultsDate = null, - gracePeriod = 30, - } = options; - - const exam = { - ...examTemplate, - course, - title, - testExam, - visibleDate, - startDate, - endDate, - examMaxPoints, - numberOfExercisesInExam, - numberOfCorrectionRoundsInExam, - workingTime, - examStudentReviewStart, - examStudentReviewEnd, - publishResultsDate, - gracePeriod, - channelName: titleLowercase(title), - } as Exam; - - if (testExam) { - exam.numberOfCorrectionRoundsInExam = 0; - } - - return cy.request({ - url: `${COURSE_BASE}/${exam.course!.id}/exams`, - method: POST, - body: exam, - }); - } - - /** - * Deletes the exam with the given parameters - * @param exam the exam object - * @returns A Cypress.Chainable> representing the API request response. - * */ - deleteExam(exam: Exam) { - return cy.request({ method: DELETE, url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}` }); - } - - /** - * Register the student for the exam - * @param exam the exam object - * @returns A Cypress.Chainable> representing the API request response. - */ - registerStudentForExam(exam: Exam, student: CypressCredentials) { - return cy.request({ method: POST, url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}/students/${student.username}` }); - } - - /** - * Creates an exam with the provided settings. - * @param exam the exam object - * @param exerciseArray an array of exercises - * @param workingTime the working time in seconds - * @returns A Cypress.Chainable> representing the API request response. - */ - createExamTestRun(exam: Exam, exerciseArray: Array, workingTime = 1080) { - const courseId = exam.course!.id; - const examId = exam.id!; - const body = { - exam, - exerciseArray, - workingTime, - }; - return cy.request({ url: `${COURSE_BASE}/${courseId}/exams/${examId}/test-run`, method: POST, body }); - } - - /** - * Add exercise group to exam - * @param exam the exam to which the group is added - * @param title the title of the group - * @param mandatory if the exercise group is mandatory - * @returns A Cypress.Chainable> representing the API request response. - * */ - addExerciseGroupForExam(exam: Exam, title = 'Group ' + generateUUID(), mandatory = true) { - const exerciseGroup = new ExerciseGroup(); - exerciseGroup.exam = exam; - exerciseGroup.title = title; - exerciseGroup.isMandatory = mandatory; - return cy.request({ method: POST, url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}/exerciseGroups`, body: exerciseGroup }); - } - - /** - * Generate all missing individual exams - * @param exam the exam for which the missing exams are generated - * @returns A Cypress.Chainable> representing the API request response. - */ - generateMissingIndividualExams(exam: Exam) { - return cy.request({ method: POST, url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}/generate-missing-student-exams` }); - } - - /** - * Prepares individual exercises for exam start - * @param exam the exam for which the exercises are prepared - * @returns A Cypress.Chainable> representing the API request response. - */ - prepareExerciseStartForExam(exam: Exam) { - return cy.request({ method: POST, url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}/student-exams/start-exercises` }); - } -} diff --git a/src/test/cypress/support/requests/ExerciseAPIRequests.ts b/src/test/cypress/support/requests/ExerciseAPIRequests.ts deleted file mode 100644 index eb1fc38eeec9..000000000000 --- a/src/test/cypress/support/requests/ExerciseAPIRequests.ts +++ /dev/null @@ -1,616 +0,0 @@ -import dayjs from 'dayjs/esm'; - -import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; -import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { Exercise } from 'app/entities/exercise.model'; -import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { Participation } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; - -import fileUploadExerciseTemplate from '../../fixtures/exercise/file-upload/template.json'; -import modelingExerciseSubmissionTemplate from '../../fixtures/exercise/modeling/submission.json'; -import modelingExerciseTemplate from '../../fixtures/exercise/modeling/template.json'; -import cProgrammingExerciseTemplate from '../../fixtures/exercise/programming/c/template.json'; -import javaAssessmentSubmission from '../../fixtures/exercise/programming/java/assessment/submission.json'; -import javaProgrammingExerciseTemplate from '../../fixtures/exercise/programming/java/template.json'; -import pythonProgrammingExerciseTemplate from '../../fixtures/exercise/programming/python/template.json'; -import multipleChoiceSubmissionTemplate from '../../fixtures/exercise/quiz/multiple_choice/submission.json'; -import shortAnswerSubmissionTemplate from '../../fixtures/exercise/quiz/short_answer/submission.json'; -import quizTemplate from '../../fixtures/exercise/quiz/template.json'; -import textExerciseTemplate from '../../fixtures/exercise/text/template.json'; -import { - BASE_API, - COURSE_BASE, - DELETE, - EXERCISE_BASE, - ExerciseType, - GET, - MODELING_EXERCISE_BASE, - PATCH, - POST, - PROGRAMMING_EXERCISE_BASE, - PUT, - ProgrammingExerciseAssessmentType, - ProgrammingLanguage, - QUIZ_EXERCISE_BASE, - TEXT_EXERCISE_BASE, - UPLOAD_EXERCISE_BASE, -} from '../constants'; -import { dayjsToString, generateUUID, titleLowercase } from '../utils'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; - -type PatchProgrammingExerciseTestVisibilityDto = { - id: number; - weight: number; - bonusPoints: number; - bonusMultiplier: number; - visibility: Visibility; -}[]; - -const MAX_RETRIES: number = 10; -const RETRY_DELAY: number = 3000; - -/** - * A class which encapsulates all API requests related to exercises. - */ -export class ExerciseAPIRequests { - /** - * Creates a programming exercise with the specified title and other data - * @param options An object containing the options for creating the programming exercise - * - course: The course the exercise will be added to - * - exerciseGroup: The exercise group the exercise will be added to - * - scaMaxPenalty: The max percentage (0-100) static code analysis can reduce from the points - * If sca should be disabled, pass null or omit this property - * - recordTestwiseCoverage: Enable testwise coverage analysis for this exercise - * - releaseDate: When the programming exercise should be available - * - dueDate: When the programming exercise should be due - * - title: The title of the programming exercise - * - programmingShortName: The short name of the programming exercise - * - programmingLanguage: The programming language for the exercise - * - packageName: The package name of the programming exercise - * - assessmentDate: The due date of the assessment - * - assessmentType: The assessment type of the exercise - * @returns request response - */ - createProgrammingExercise(options: { - course?: Course; - exerciseGroup?: ExerciseGroup; - scaMaxPenalty?: number | null; - recordTestwiseCoverage?: boolean; - releaseDate?: dayjs.Dayjs; - dueDate?: dayjs.Dayjs; - title?: string; - programmingShortName?: string; - programmingLanguage?: ProgrammingLanguage; - packageName?: string; - assessmentDate?: dayjs.Dayjs; - assessmentType?: ProgrammingExerciseAssessmentType; - }): Cypress.Chainable> { - const { - course, - exerciseGroup, - scaMaxPenalty = null, - recordTestwiseCoverage = false, - releaseDate = dayjs(), - dueDate = dayjs().add(1, 'day'), - title = 'Programming ' + generateUUID(), - programmingShortName = 'programming' + generateUUID(), - programmingLanguage = ProgrammingLanguage.JAVA, - packageName = 'de.test', - assessmentDate = dayjs().add(2, 'days'), - assessmentType = ProgrammingExerciseAssessmentType.AUTOMATIC, - } = options; - - let programmingExerciseTemplate = {}; - - if (programmingLanguage == ProgrammingLanguage.PYTHON) { - programmingExerciseTemplate = pythonProgrammingExerciseTemplate; - } else if (programmingLanguage == ProgrammingLanguage.C) { - programmingExerciseTemplate = cProgrammingExerciseTemplate; - } else if (programmingLanguage == ProgrammingLanguage.JAVA) { - programmingExerciseTemplate = javaProgrammingExerciseTemplate; - } - - const exercise = { - ...programmingExerciseTemplate, - title, - shortName: programmingShortName, - packageName, - channelName: 'exercise-' + titleLowercase(title), - assessmentType: ProgrammingExerciseAssessmentType[assessmentType], - ...(course ? { course } : {}), - ...(exerciseGroup ? { exerciseGroup } : {}), - } as ProgrammingExercise; - - if (!exerciseGroup) { - exercise.releaseDate = releaseDate; - exercise.dueDate = dueDate; - exercise.assessmentDueDate = assessmentDate; - } - - if (scaMaxPenalty) { - exercise.staticCodeAnalysisEnabled = true; - exercise.maxStaticCodeAnalysisPenalty = scaMaxPenalty; - } - - exercise.programmingLanguage = programmingLanguage; - exercise.testwiseCoverageEnabled = recordTestwiseCoverage; - - return cy.request({ - url: `${PROGRAMMING_EXERCISE_BASE}/setup`, - method: POST, - body: exercise, - }); - } - - /** - * Retrieves the test cases for passed exercise and adjusts their visibility according. - *
- * Note: test cases are not available before the tests of the solution have completely run through - * -> we need to do retries until the tests have been executed - * - * @param programmingExercise for which the test cases shall be set to {@link newVisibility} - * @param newVisibility that is applied for all found test cases - * @param retryNumber - */ - changeProgrammingExerciseTestVisibility(programmingExercise: ProgrammingExercise, newVisibility: Visibility, retryNumber: number) { - if (retryNumber >= MAX_RETRIES) { - throw new Error('Could not find test cases (tests for solution might not be finished yet)'); - } - - cy.request({ - url: `${PROGRAMMING_EXERCISE_BASE}/${programmingExercise.id}/test-cases`, - method: GET, - }).then((response) => { - const testCases = response.body as ProgrammingExerciseTestCase[]; - - if (retryNumber > 0) { - cy.log(`Could not find test cases yet, retrying... (${retryNumber} / ${MAX_RETRIES})`); - } - - cy.wait(RETRY_DELAY); - - if (testCases.length > 0) { - this.updateProgrammingExerciseTestCaseVisibility(programmingExercise.id!, testCases, newVisibility); - } else { - this.changeProgrammingExerciseTestVisibility(programmingExercise, newVisibility, retryNumber + 1); - } - }); - } - - /** - * Submits the example submission to the specified repository. - * - * @param repositoryId - The repository ID. The repository ID is equal to the participation ID. - * @returns A Cypress.Chainable> representing the API request response. - */ - makeProgrammingExerciseSubmission(repositoryId: number) { - // TODO: For now it is enough to submit the one prepared json file, but in the future this method should support different package names and submissions. - return cy.request({ - url: `${BASE_API}/repository/${repositoryId}/files?commit=yes`, - method: PUT, - body: javaAssessmentSubmission, - }); - } - - /** - * Adds a text exercise to an exercise group in an exam or to a course. - * - * @param body - An object containing either the course or exercise group the exercise will be added to. - * @param title - The title for the text exercise (optional, default: auto-generated). - * @returns A Cypress.Chainable> representing the API request response. - */ - createTextExercise(body: { course: Course } | { exerciseGroup: ExerciseGroup }, title = 'Text ' + generateUUID()): Cypress.Chainable> { - const template = { - ...textExerciseTemplate, - title, - channelName: 'exercise-' + titleLowercase(title), - }; - const textExercise = Object.assign({}, template, body); - return cy.request({ method: POST, url: TEXT_EXERCISE_BASE, body: textExercise }); - } - - /** - * Deletes a text exercise with the specified exercise ID. - * - * @param exerciseId - The ID of the text exercise to be deleted. - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteTextExercise(exerciseId: number) { - return cy.request({ - url: `${TEXT_EXERCISE_BASE}/${exerciseId}`, - method: DELETE, - }); - } - - /** - * Makes a text exercise submission for the specified exercise ID with the given text content. - * - * @param exerciseId - The ID of the text exercise for which the submission is made. - * @param text - The text content of the submission. - * @returns A Cypress.Chainable> representing the API request response. - */ - makeTextExerciseSubmission(exerciseId: number, text: string) { - return cy.request({ - url: `${EXERCISE_BASE}/${exerciseId}/text-submissions`, - method: PUT, - body: { submissionExerciseType: 'text', text }, - }); - } - - /** - * Creates a file upload exercise. - * - * @param body - An object containing either the course or exercise group the exercise will be added to. - * @param title - The title for the exercise (optional, default: auto-generated). - * @returns A Cypress.Chainable> representing the API request response. - */ - createFileUploadExercise( - body: { course: Course } | { exerciseGroup: ExerciseGroup }, - title = 'Upload ' + generateUUID(), - ): Cypress.Chainable> { - const template = { - ...fileUploadExerciseTemplate, - title, - channelName: 'exercise-' + titleLowercase(title), - }; - const uploadExercise = Object.assign({}, template, body); - return cy.request({ method: POST, url: UPLOAD_EXERCISE_BASE, body: uploadExercise }); - } - - /** - * Deletes a file upload exercise with the specified exercise ID. - * - * @param exerciseID - The ID of the file upload exercise to be deleted. - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteFileUploadExercise(exerciseID: number) { - return cy.request({ - url: `${UPLOAD_EXERCISE_BASE}/${exerciseID}`, - method: DELETE, - }); - } - - /** - * Makes a file upload exercise submission for the specified exercise ID with the given file. - * - * @param exerciseId - The ID of the file upload exercise for which the submission is made. - * @param file - The file content of the submission. - * @returns A Cypress.Chainable> representing the API request response. - */ - makeFileUploadExerciseSubmission(exerciseId: number, file: string) { - return cy.request({ - url: `${EXERCISE_BASE}/${exerciseId}/file-upload-submissions`, - method: POST, - body: { submissionExerciseType: 'file-upload', file }, - }); - } - - /** - * Creates a modeling exercise. - * - * @param body - An object containing either the course or exercise group the exercise will be added to. - * @param title - The title for the exercise (optional, default: auto-generated). - * @param releaseDate - The release date of the exercise (optional, default: current date). - * @param dueDate - The due date of the exercise (optional, default: current date + 1 day). - * @param assessmentDueDate - The assessment due date of the exercise (optional, default: current date + 2 days). - * @returns A Cypress.Chainable> representing the API request response. - */ - createModelingExercise( - body: { course: Course } | { exerciseGroup: ExerciseGroup }, - title = 'Modeling ' + generateUUID(), - releaseDate = dayjs(), - dueDate = dayjs().add(1, 'days'), - assessmentDueDate = dayjs().add(2, 'days'), - ): Cypress.Chainable> { - const templateCopy = { - ...modelingExerciseTemplate, - title, - channelName: 'exercise-' + titleLowercase(title), - }; - const dates = { - releaseDate: dayjsToString(releaseDate), - dueDate: dayjsToString(dueDate), - assessmentDueDate: dayjsToString(assessmentDueDate), - }; - let newModelingExercise; - if (body.hasOwnProperty('course')) { - newModelingExercise = Object.assign({}, templateCopy, dates, body); - } else { - newModelingExercise = Object.assign({}, templateCopy, body); - } - return cy.request({ - url: MODELING_EXERCISE_BASE, - method: POST, - body: newModelingExercise, - }); - } - - /** - * Updates the assessment due date of a modeling exercise. - * - * @param exercise - The modeling exercise to update. - * @param due - The new assessment due date (optional, default: current date). - * @returns A Cypress.Chainable> representing the API request response. - */ - updateModelingExerciseAssessmentDueDate(exercise: ModelingExercise, due = dayjs()) { - exercise.assessmentDueDate = due; - return this.updateExercise(exercise, ExerciseType.MODELING); - } - - /** - * Deletes a modeling exercise with the specified exercise ID. - * - * @param exerciseID - The ID of the modeling exercise to be deleted. - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteModelingExercise(exerciseID: number) { - return cy.request({ - url: `${MODELING_EXERCISE_BASE}/${exerciseID}`, - method: DELETE, - }); - } - - /** - * Makes a modeling exercise submission for the specified exercise ID and participation. - * - * @param exerciseID - The ID of the modeling exercise for which the submission is made. - * @param participation - The participation data for the submission. - * @returns A Cypress.Chainable> representing the API request response. - */ - makeModelingExerciseSubmission(exerciseID: number, participation: Participation) { - return cy.request({ - url: `${EXERCISE_BASE}/${exerciseID}/modeling-submissions`, - method: PUT, - body: { - ...modelingExerciseSubmissionTemplate, - id: participation.submissions![0].id, - participation, - }, - }); - } - - /** - * Updates the due date of a modeling exercise. - * - * @param exercise - The modeling exercise to update. - * @param due - The new due date (optional, default: current date). - * @returns A Cypress.Chainable> representing the API request response. - */ - updateModelingExerciseDueDate(exercise: ModelingExercise, due = dayjs()) { - exercise.dueDate = due; - return this.updateExercise(exercise, ExerciseType.MODELING); - } - - /** - * Creates a quiz exercise. - * - * @param body - An object containing either the course or exercise group the exercise will be added to. - * @param quizQuestions - A list of quiz question objects that make up the quiz (e.g., multiple choice, short answer, or drag and drop). - * @param title - The title for the quiz exercise (optional, default: auto-generated). - * @param releaseDate - The release date of the quiz exercise (optional, default: current date + 1 year). - * @param duration - The duration in seconds that students get to complete the quiz (optional, default: 600 seconds). - * @returns A Cypress.Chainable> representing the API request response. - */ - createQuizExercise( - body: { course: Course } | { exerciseGroup: ExerciseGroup }, - quizQuestions: [any], - title = 'Quiz ' + generateUUID(), - releaseDate = dayjs().add(1, 'year'), - duration = 600, - ): Cypress.Chainable> { - const quizExercise: any = { - ...quizTemplate, - title, - quizQuestions, - duration, - channelName: 'exercise-' + titleLowercase(title), - }; - let newQuizExercise; - const dates = { - releaseDate: dayjsToString(releaseDate), - }; - if (body.hasOwnProperty('course')) { - newQuizExercise = Object.assign({}, quizExercise, dates, body); - } else { - newQuizExercise = Object.assign({}, quizExercise, body); - } - - const formData = new FormData(); - formData.append('exercise', new File([JSON.stringify(newQuizExercise)], 'exercise', { type: 'application/json' })); - - return cy.request({ - url: QUIZ_EXERCISE_BASE, - method: POST, - body: formData, - }); - } - - /** - * Deletes a quiz exercise with the specified exercise ID. - * - * @param exerciseId - The ID of the quiz exercise to be deleted. - * @returns A Cypress.Chainable> representing the API request response. - */ - deleteQuizExercise(exerciseId: number) { - return cy.request({ - url: `${QUIZ_EXERCISE_BASE}/${exerciseId}`, - method: DELETE, - }); - } - - /** - * Sets a quiz exercise as visible. - * - * @param quizId - The ID of the quiz exercise to be set as visible. - * @returns A Cypress.Chainable> representing the API request response. - */ - setQuizVisible(quizId: number) { - return cy.request({ - url: `${QUIZ_EXERCISE_BASE}/${quizId}/set-visible`, - method: PUT, - }); - } - - /** - * Starts a quiz exercise immediately. - * - * @param quizId - The ID of the quiz exercise to be started immediately. - * @returns A Cypress.Chainable> representing the API request response. - */ - startQuizNow(quizId: number) { - return cy.request({ - url: `${QUIZ_EXERCISE_BASE}/${quizId}/start-now`, - method: PUT, - }); - } - - /** - * Evaluates the quiz exercises in an exam. - * - * @param exam - The exam for which to evaluate the quiz exercises. - * @returns A Cypress.Chainable> representing the API request response. - */ - evaluateExamQuizzes(exam: Exam) { - return cy.request({ - url: `${COURSE_BASE}/${exam.course!.id}/exams/${exam.id}/student-exams/evaluate-quiz-exercises`, - method: POST, - }); - } - - /** - * Creates a submission for a quiz with only one multiple-choice quiz question. - * - * @param quizExercise - The response body of a quiz exercise. - * @param tickOptions - A list describing which of the 0..n boxes are to be ticked in the submission. - * @returns A Cypress.Chainable> representing the API request response. - */ - createMultipleChoiceSubmission(quizExercise: any, tickOptions: number[]) { - const submittedAnswers = [ - { - ...multipleChoiceSubmissionTemplate.submittedAnswers[0], - quizQuestion: quizExercise.quizQuestions![0], - selectedOptions: tickOptions.map((option) => quizExercise.quizQuestions[0].answerOptions[option]), - }, - ]; - const multipleChoiceSubmission = { - ...multipleChoiceSubmissionTemplate, - submittedAnswers, - }; - return cy.request({ - url: `${EXERCISE_BASE}/${quizExercise.id}/submissions/live`, - method: POST, - body: multipleChoiceSubmission, - }); - } - - /** - * Creates a submission for a quiz with only one short-answer quiz question. - * - * @param quizExercise - The response body of the quiz exercise. - * @param textAnswers - A list containing the answers to be filled into the gaps. In numerical order. - * @returns A Cypress.Chainable> representing the API request response. - */ - createShortAnswerSubmission(quizExercise: any, textAnswers: string[]) { - const submittedTexts = textAnswers.map((answer, index) => { - return { - text: answer, - spot: { - id: quizExercise.quizQuestions[0].spots[index].id, - spotNr: quizExercise.quizQuestions[0].spots[index].spotNr, - width: quizExercise.quizQuestions[0].spots[index].width, - invalid: quizExercise.quizQuestions[0].spots[index].invalid, - }, - }; - }); - const submittedAnswers = [ - { - ...shortAnswerSubmissionTemplate.submittedAnswers[0], - quizQuestion: quizExercise.quizQuestions[0], - submittedTexts, - }, - ]; - const shortAnswerSubmission = { - ...shortAnswerSubmissionTemplate, - submittedAnswers, - }; - return cy.request({ - url: `${EXERCISE_BASE}/${quizExercise.id}/submissions/live`, - method: POST, - body: shortAnswerSubmission, - }); - } - - /** - * Gets the participation data for an exercise with the specified exercise ID. - * - * @param exerciseId - The ID of the exercise for which to retrieve the participation data. - * @returns A Cypress.Chainable> representing the API request response. - */ - getExerciseParticipation(exerciseId: number) { - return cy.request({ - url: `${EXERCISE_BASE}/${exerciseId}/participation`, - method: GET, - }); - } - - /** - * Starts a participation for an exercise with the specified exercise ID. - * - * @param exerciseId - The ID of the exercise for which to start the participation. - * @returns A Cypress.Chainable> representing the API request response. - */ - startExerciseParticipation(exerciseId: number) { - return cy.request({ - url: `${EXERCISE_BASE}/${exerciseId}/participations`, - method: POST, - }); - } - - private updateProgrammingExerciseTestCaseVisibility(programmingExerciseId: number, programmingExerciseTestCases: ProgrammingExerciseTestCase[], newVisibility: Visibility) { - const updatedTestCaseSettings: PatchProgrammingExerciseTestVisibilityDto = []; - - for (const testCase of programmingExerciseTestCases) { - updatedTestCaseSettings.push({ - id: testCase.id!, - weight: testCase.weight!, - bonusPoints: testCase.bonusPoints!, - bonusMultiplier: testCase.bonusMultiplier!, - visibility: newVisibility, - }); - } - - return cy.request({ - url: `${PROGRAMMING_EXERCISE_BASE}/${programmingExerciseId}/update-test-cases`, - method: PATCH, - body: updatedTestCaseSettings, - }); - } - - private updateExercise(exercise: Exercise, type: ExerciseType) { - let url: string; - switch (type) { - case ExerciseType.PROGRAMMING: - url = PROGRAMMING_EXERCISE_BASE; - break; - case ExerciseType.TEXT: - url = TEXT_EXERCISE_BASE; - break; - case ExerciseType.MODELING: - url = MODELING_EXERCISE_BASE; - break; - case ExerciseType.QUIZ: - default: - throw new Error(`Exercise type '${type}' is not supported yet!`); - } - return cy.request({ - url, - method: PUT, - body: exercise, - }); - } -} diff --git a/src/test/cypress/support/requests/UserManagementAPIRequest.ts b/src/test/cypress/support/requests/UserManagementAPIRequest.ts deleted file mode 100644 index a3f224f5d5f0..000000000000 --- a/src/test/cypress/support/requests/UserManagementAPIRequest.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BASE_API, GET, POST } from '../constants'; -import { UserRole } from '../users'; - -/** - * A class which encapsulates all API requests related to user management. - */ -export class UserManagementAPIRequests { - /** - * Creates a new user - * @param username the username of the new user - * @param password the password of the new user - * @param role the role of the new user - */ - createUser(username: string, password: string, role: UserRole) { - const user = { - login: username, - password, - firstName: username, - lastName: username, - email: username + '@example.com', - authorities: [role], - }; - return cy.request({ - url: `${BASE_API}/admin/users`, - method: POST, - body: user, - }); - } - - getUser(username: string) { - return cy.request({ - url: `${BASE_API}/admin/users/${username}`, - method: GET, - failOnStatusCode: false, - }); - } -} diff --git a/src/test/cypress/support/users.ts b/src/test/cypress/support/users.ts deleted file mode 100644 index 94d89a08140c..000000000000 --- a/src/test/cypress/support/users.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { BASE_API, GET, USER_ID_SELECTOR } from './constants'; - -export enum UserRole { - Instructor = 'ROLE_INSTRUCTOR', - Tutor = 'ROLE_TA', - Student = 'ROLE_USER', -} - -export const USER_ID = { - studentOne: 100, - studentTwo: 102, - studentThree: 104, - studentFour: 106, - instructor: 103, - tutor: 101, -}; - -export const USER_ROLE = { - studentOne: UserRole.Student, - studentTwo: UserRole.Student, - studentThree: UserRole.Student, - studentFour: UserRole.Student, - instructor: UserRole.Instructor, - tutor: UserRole.Tutor, -}; - -/** - * Class to encompass user management logic for cypress tests. - */ -export class CypressUserManagement { - /** - * @returns admin credentials. - */ - public getAdmin(): CypressCredentials { - const adminUsername = Cypress.env('adminUsername') ?? 'admin'; - const adminPassword = Cypress.env('adminPassword') ?? 'admin'; - return { username: adminUsername, password: adminPassword }; - } - - /** - * @returns the first testing account with student rights. - */ - public getStudentOne(): CypressCredentials { - return this.getUserWithId(USER_ID.studentOne); - } - - /** - * @returns the second testing account with student rights. - */ - public getStudentTwo(): CypressCredentials { - return this.getUserWithId(USER_ID.studentTwo); - } - - /** - * @returns the third testing account with student rights. - */ - public getStudentThree(): CypressCredentials { - return this.getUserWithId(USER_ID.studentThree); - } - - /** - * @returns the fourth testing account with student rights. - */ - public getStudentFour(): CypressCredentials { - return this.getUserWithId(USER_ID.studentFour); - } - - /** - * @returns an instructor account. - */ - public getInstructor(): CypressCredentials { - return this.getUserWithId(USER_ID.instructor); - } - - /** - * @returns a tutor account. - */ - public getTutor(): CypressCredentials { - return this.getUserWithId(USER_ID.tutor); - } - - public getUserWithId(userId: number): CypressCredentials { - const username = this.getUsernameTemplate().replace(USER_ID_SELECTOR, userId.toString()); - const password = this.getPasswordTemplate().replace(USER_ID_SELECTOR, userId.toString()); - return { username, password }; - } - - /** - * @returns the username template. - */ - private getUsernameTemplate(): string { - return Cypress.env('username') ?? 'user_' + USER_ID_SELECTOR; - } - - /** - * @returns the password template. - */ - private getPasswordTemplate(): string { - return Cypress.env('password') ?? 'password_' + USER_ID_SELECTOR; - } - - /** - * Provides the entire account info for the user that is currently logged in - * Use like this: artemis.users.getAccountInfo((account) => { someFunction(account); }); - * */ - public getAccountInfo(func: (response: any) => void) { - cy.request({ method: GET, url: `${BASE_API}/public/account`, log: false }).then((response) => { - func(response.body); - }); - } - - public getUserInfo(username: string, func: (response: any) => void) { - cy.request({ method: GET, url: `${BASE_API}/admin/users/${username}`, log: false }).then((response) => { - func(response.body); - }); - } -} - -// Users -export const users = new CypressUserManagement(); -export const admin = users.getAdmin(); -export const instructor = users.getInstructor(); -export const tutor = users.getTutor(); -export const studentOne = users.getStudentOne(); -export const studentTwo = users.getStudentTwo(); -export const studentThree = users.getStudentThree(); -export const studentFour = users.getStudentFour(); - -/** - * Container class for user credentials. - */ -export interface CypressCredentials { - username: string; - password: string; -} diff --git a/src/test/cypress/support/utils.ts b/src/test/cypress/support/utils.ts deleted file mode 100644 index 0f3cbf51c7df..000000000000 --- a/src/test/cypress/support/utils.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { TIME_FORMAT } from './constants'; -import dayjs from 'dayjs/esm'; -import utc from 'dayjs/esm/plugin/utc'; -import { v4 as uuidv4 } from 'uuid'; - -// Add utc plugin to use the utc timezone -dayjs.extend(utc); - -/** - * This file contains all of the global utility functions not directly related to cypress. - */ - -/** - * Generates a unique identifier. - * */ -export function generateUUID() { - const uuid = uuidv4().replace(/-/g, ''); - return uuid.substr(0, 9); -} - -/** - * Allows to enter date into the UI - * */ -export function enterDate(selector: string, date: dayjs.Dayjs) { - const dateInputField = cy.get(selector).find('#date-input-field'); - dateInputField.should('not.be.disabled'); - dateInputField.clear().type(dayjsToString(date), { force: true }); -} - -/** - * Allows to check a specified input field for a value - * */ -export function checkField(field: string, value: any) { - cy.get(field).should('have.value', value); -} - -/** - * Formats the day object with the time format which the server uses. Also makes sure that day uses the utc timezone. - * @param day the day object - * @returns a formatted string representing the date with utc timezone - */ -export function dayjsToString(day: dayjs.Dayjs) { - // We need to add the Z at the end. Otherwise, the server can't parse it. - return day.utc().format(TIME_FORMAT) + 'Z'; -} - -/** - * Converts the response object obtained from a multipart request to an entity object. - * @param response - The Cypress.Response object obtained from a multipart request. - * @returns The entity object parsed from the response. - */ -export function convertModelAfterMultiPart(response: Cypress.Response): T { - // Cypress currently has some issues with our multipart request, parsing this not as an object but as an ArrayBuffer - // Once this is fixed (and hence the expect statements below fail), we can remove the additional parsing - expect(response.body).not.to.be.an('object'); - expect(response.body).to.be.an('ArrayBuffer'); - - return parseArrayBufferAsJsonObject(response.body as ArrayBuffer); -} - -/** - * This function is necessary to make the server and the client date comparable. - * The server sometimes has 3 digit on the milliseconds and sometimes only 1 digit. - * With this function we always cut the date string after the first digit. - * @param date the date as a string - * @returns a date string with only one digit for the milliseconds - */ -export function trimDate(date: string) { - return date.slice(0, 19); -} - -/** - * Converts a snake_case word to Title Case (each word's first letter capitalized and spaces in between). - * @param str - The snake_case word to be converted to Title Case. - * @returns The word in Title Case. - */ -export function titleCaseWord(str: string) { - str = str.replace('_', ' '); - const sentence = str.toLowerCase().split(' '); - for (let i = 0; i < sentence.length; i++) { - sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1); - } - return sentence.join(' '); -} - -/** - * Converts a title to lowercase and replaces spaces with hyphens. - * @param title - The title to be converted to lowercase with hyphens. - * @returns The converted title in lowercase with hyphens. - */ -export function titleLowercase(title: string) { - return title.replace(' ', '-').toLowerCase(); -} - -/** - * Retrieves the DOM element representing the exercise with the specified ID. - * @param exerciseId - The ID of the exercise for which to retrieve the DOM element. - * @returns A Cypress.Chainable that yields the DOM element representing the exercise. - */ -export function getExercise(exerciseId: number) { - return cy.get(`#exercise-${exerciseId}`); -} - -/** - * Converts a boolean value to its related icon class. - * @param boolean - The boolean value to be converted. - * @returns The corresponding ".checked" or ".unchecked" string. - */ -export function convertBooleanToCheckIconClass(boolean: boolean) { - return boolean ? '.checked' : '.unchecked'; -} - -/** - * Parses an ArrayBuffer as a JSON object. - * @param buffer - The ArrayBuffer to be parsed as a JSON object. - * @returns The parsed JSON object. - */ -function parseArrayBufferAsJsonObject(buffer: ArrayBuffer) { - const bodyString = Cypress.Blob.arrayBufferToBinaryString(buffer); - return JSON.parse(bodyString); -} diff --git a/src/test/cypress/tsconfig.json b/src/test/cypress/tsconfig.json deleted file mode 100644 index 65dece962d13..000000000000 --- a/src/test/cypress/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "include": ["**/**/**/*.ts"], - "exclude": ["node_modules"], - "compilerOptions": { - "ignoreDeprecations": "5.0", - "target": "es2019", - "sourceMap": false, - "types": ["cypress", "@4tw/cypress-drag-drop", "node", "cypress-wait-until", "cypress-file-upload"] - } -} diff --git a/src/test/java/de/tum/in/www1/artemis/AbstractSpringIntegrationIndependentTest.java b/src/test/java/de/tum/in/www1/artemis/AbstractSpringIntegrationIndependentTest.java index c72bb2a37e0f..d2b41249721c 100644 --- a/src/test/java/de/tum/in/www1/artemis/AbstractSpringIntegrationIndependentTest.java +++ b/src/test/java/de/tum/in/www1/artemis/AbstractSpringIntegrationIndependentTest.java @@ -2,6 +2,7 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; import static de.tum.in.www1.artemis.config.Constants.PROFILE_SCHEDULING; +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; import static tech.jhipster.config.JHipsterConstants.SPRING_PROFILE_TEST; import java.util.Set; @@ -34,7 +35,7 @@ */ @ResourceLock("AbstractSpringIntegrationIndependentTest") // NOTE: we use a common set of active profiles to reduce the number of application launches during testing. This significantly saves time and memory! -@ActiveProfiles({ SPRING_PROFILE_TEST, "artemis", PROFILE_SCHEDULING, "athena", "apollon", "lti", "aeolus", PROFILE_CORE }) +@ActiveProfiles({ SPRING_PROFILE_TEST, "artemis", PROFILE_SCHEDULING, "athena", "apollon", "lti", "aeolus", PROFILE_THEIA, PROFILE_CORE }) @TestPropertySource(properties = { "artemis.user-management.use-external=false" }) public abstract class AbstractSpringIntegrationIndependentTest extends AbstractArtemisIntegrationTest { diff --git a/src/test/java/de/tum/in/www1/artemis/authentication/UserLocalVcIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/authentication/UserAccountLocalVcsIntegrationTest.java similarity index 64% rename from src/test/java/de/tum/in/www1/artemis/authentication/UserLocalVcIntegrationTest.java rename to src/test/java/de/tum/in/www1/artemis/authentication/UserAccountLocalVcsIntegrationTest.java index b419cbf57260..29630b917676 100644 --- a/src/test/java/de/tum/in/www1/artemis/authentication/UserLocalVcIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/authentication/UserAccountLocalVcsIntegrationTest.java @@ -9,7 +9,7 @@ import de.tum.in.www1.artemis.AbstractSpringIntegrationLocalCILocalVCTest; import de.tum.in.www1.artemis.user.UserTestService; -class UserLocalVcIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { +class UserAccountLocalVcsIntegrationTest extends AbstractSpringIntegrationLocalCILocalVCTest { private static final String TEST_PREFIX = "userlvc"; // shorter prefix as user's name is limited to 50 chars @@ -31,4 +31,16 @@ void teardown() throws Exception { void addAndDeleteSshPublicKeyByUser() throws Exception { userTestService.addAndDeleteSshPublicKey(); } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void getAndCreateParticipationVcsAccessTokenByUser() throws Exception { + userTestService.getAndCreateParticipationVcsAccessToken(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void createAndDeleteUserVcsAccessTokenByUser() throws Exception { + userTestService.createAndDeleteUserVcsAccessToken(); + } } diff --git a/src/test/java/de/tum/in/www1/artemis/config/TheiaConfigurationTest.java b/src/test/java/de/tum/in/www1/artemis/config/TheiaConfigurationTest.java new file mode 100644 index 000000000000..41852515b971 --- /dev/null +++ b/src/test/java/de/tum/in/www1/artemis/config/TheiaConfigurationTest.java @@ -0,0 +1,41 @@ +package de.tum.in.www1.artemis.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import de.tum.in.www1.artemis.AbstractSpringIntegrationIndependentTest; +import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage; + +class TheiaConfigurationTest extends AbstractSpringIntegrationIndependentTest { + + @Autowired + private TheiaConfiguration theiaConfiguration; + + @Test + void testAutowired() { + assertThat(theiaConfiguration).isNotNull(); + } + + @Test + void testAmountOfLanguageImages() { + assertThat(theiaConfiguration.getImagesForAllLanguages()).hasSize(2); + } + + @Test + void testFlavorsForLanguage() { + Map images = theiaConfiguration.getImagesForLanguage(ProgrammingLanguage.valueOf("JAVA")); + assertThat(images).hasSize(2); + assertThat(images).containsKey("Java-17"); + assertThat(images).containsValue("ghcr.io/ls1intum/theia/java-17:latest"); + assertThat(images.get("Java-Non-Existent")).isEqualTo("this-is-not-a-valid-image"); + } + + @Test + void testNonExistentLanguage() { + assertThat(theiaConfiguration.getImagesForLanguage(null)).isNull(); + } +} diff --git a/src/test/java/de/tum/in/www1/artemis/exam/ExamStartTest.java b/src/test/java/de/tum/in/www1/artemis/exam/ExamStartTest.java index 3086bb22234e..5181383dbf1f 100644 --- a/src/test/java/de/tum/in/www1/artemis/exam/ExamStartTest.java +++ b/src/test/java/de/tum/in/www1/artemis/exam/ExamStartTest.java @@ -36,7 +36,6 @@ import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.domain.enumeration.DiagramType; import de.tum.in.www1.artemis.domain.exam.Exam; -import de.tum.in.www1.artemis.domain.exam.ExamUser; import de.tum.in.www1.artemis.domain.exam.ExerciseGroup; import de.tum.in.www1.artemis.domain.exam.StudentExam; import de.tum.in.www1.artemis.domain.modeling.ModelingExercise; @@ -124,7 +123,6 @@ void initTestCase() throws GitAPIException { User student1 = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); User student2 = userUtilService.getUserByLogin(TEST_PREFIX + "student2"); registeredUsers = Set.of(student1, student2); - exam.setExamUsers(Set.of(new ExamUser())); // setting dates exam.setStartDate(ZonedDateTime.now().plusHours(2)); exam.setEndDate(ZonedDateTime.now().plusHours(3)); @@ -137,6 +135,7 @@ void tearDown() throws Exception { programmingExerciseTestService.tearDown(); } + // TODO: why do we remove the student exams here? This is not really necessary // Cleanup of Bidirectional Relationships for (StudentExam studentExam : createdStudentExams) { exam.removeStudentExam(studentExam); diff --git a/src/test/java/de/tum/in/www1/artemis/localvcci/LocalVCLocalCIParticipationIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/localvcci/LocalVCLocalCIParticipationIntegrationTest.java index 190d02485212..cbd957d51dd1 100644 --- a/src/test/java/de/tum/in/www1/artemis/localvcci/LocalVCLocalCIParticipationIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/localvcci/LocalVCLocalCIParticipationIntegrationTest.java @@ -57,7 +57,7 @@ void testStartParticipation() throws Exception { LocalVCRepositoryUri studentAssignmentRepositoryUri = new LocalVCRepositoryUri(projectKey, projectKey.toLowerCase() + "-" + TEST_PREFIX + "student1", localVCBaseUrl); assertThat(studentAssignmentRepositoryUri.getLocalRepositoryPath(localVCBasePath)).exists(); - var vcsAccessToken = request.get("/api/users/vcsToken?participationId=" + participation.getId(), HttpStatus.OK, String.class); + var vcsAccessToken = request.get("/api/account/participation-vcs-access-token?participationId=" + participation.getId(), HttpStatus.OK, String.class); assertThat(vcsAccessToken).isNotNull(); assertThat(vcsAccessToken).startsWith("vcpat"); diff --git a/src/test/java/de/tum/in/www1/artemis/theia/TheiaInfoContributorTest.java b/src/test/java/de/tum/in/www1/artemis/theia/TheiaInfoContributorTest.java index 04c75c487a6f..8b3b6738b219 100644 --- a/src/test/java/de/tum/in/www1/artemis/theia/TheiaInfoContributorTest.java +++ b/src/test/java/de/tum/in/www1/artemis/theia/TheiaInfoContributorTest.java @@ -23,14 +23,9 @@ class TheiaInfoContributorTest { void testContribute() { Info.Builder builder = new Info.Builder(); theiaInfoContributor = new TheiaInfoContributor(); - try { - theiaInfoContributor.contribute(builder); - } - catch (NullPointerException e) { - } + theiaInfoContributor.contribute(builder); Info info = builder.build(); assertThat(info.getDetails().get(Constants.THEIA_PORTAL_URL)).isEqualTo(expectedValue); - } } diff --git a/src/test/java/de/tum/in/www1/artemis/user/UserTestService.java b/src/test/java/de/tum/in/www1/artemis/user/UserTestService.java index 394200342127..7902d3348d51 100644 --- a/src/test/java/de/tum/in/www1/artemis/user/UserTestService.java +++ b/src/test/java/de/tum/in/www1/artemis/user/UserTestService.java @@ -27,13 +27,18 @@ import de.tum.in.www1.artemis.course.CourseUtilService; import de.tum.in.www1.artemis.domain.Authority; import de.tum.in.www1.artemis.domain.Course; +import de.tum.in.www1.artemis.domain.ProgrammingSubmission; import de.tum.in.www1.artemis.domain.User; +import de.tum.in.www1.artemis.domain.enumeration.SubmissionType; import de.tum.in.www1.artemis.domain.science.ScienceEvent; import de.tum.in.www1.artemis.domain.science.ScienceEventType; import de.tum.in.www1.artemis.exercise.programming.MockDelegate; import de.tum.in.www1.artemis.exercise.programming.ProgrammingExerciseUtilService; import de.tum.in.www1.artemis.repository.AuthorityRepository; import de.tum.in.www1.artemis.repository.CourseRepository; +import de.tum.in.www1.artemis.repository.ParticipationRepository; +import de.tum.in.www1.artemis.repository.ParticipationVCSAccessTokenRepository; +import de.tum.in.www1.artemis.repository.SubmissionRepository; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.repository.science.ScienceEventRepository; import de.tum.in.www1.artemis.security.Role; @@ -106,6 +111,15 @@ public class UserTestService { private static final int NUMBER_OF_INSTRUCTORS = 1; + @Autowired + private ParticipationVCSAccessTokenRepository participationVCSAccessTokenRepository; + + @Autowired + private ParticipationRepository participationRepository; + + @Autowired + private SubmissionRepository submissionRepository; + public void setup(String testPrefix, MockDelegate mockDelegate) throws Exception { this.TEST_PREFIX = testPrefix; this.mockDelegate = mockDelegate; @@ -817,18 +831,73 @@ public void addAndDeleteSshPublicKey() throws Exception { // adding invalid key should fail String invalidSshKey = "invalid key"; - request.putWithResponseBody("/api/users/sshpublickey", invalidSshKey, String.class, HttpStatus.BAD_REQUEST, true); + request.putWithResponseBody("/api/account/ssh-public-key", invalidSshKey, String.class, HttpStatus.BAD_REQUEST, true); // adding valid key should work correctly String validSshKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEbgjoSpKnry5yuMiWh/uwhMG2Jq5Sh8Uw9vz+39or2i email@abc.de"; - request.putWithResponseBody("/api/users/sshpublickey", validSshKey, String.class, HttpStatus.OK, true); + request.putWithResponseBody("/api/account/ssh-public-key", validSshKey, String.class, HttpStatus.OK, true); assertThat(userRepository.getUser().getSshPublicKey()).isEqualTo(validSshKey); // deleting the key shoul work correctly - request.delete("/api/users/sshpublickey", HttpStatus.OK); + request.delete("/api/account/ssh-public-key", HttpStatus.OK); assertThat(userRepository.getUser().getSshPublicKey()).isEqualTo(null); } + // Test + public void getAndCreateParticipationVcsAccessToken() throws Exception { + User user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + + // try to get token for non existent participation + request.get("/api/account/participation-vcs-access-token?participationId=11", HttpStatus.NOT_FOUND, String.class); + + var course = courseUtilService.addEmptyCourse(); + var exercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); + courseRepository.save(course); + + var submission = (ProgrammingSubmission) new ProgrammingSubmission().commitHash("abc").type(SubmissionType.MANUAL).submitted(true); + submission = programmingExerciseUtilService.addProgrammingSubmission(exercise, submission, user.getLogin()); + // request existing token + var token = request.get("/api/account/participation-vcs-access-token?participationId=" + submission.getParticipation().getId(), HttpStatus.OK, String.class); + assertThat(token).isNotNull(); + + // delete all tokens + participationVCSAccessTokenRepository.deleteAll(); + + // check that token was deleted + request.get("/api/account/participation-vcs-access-token?participationId=" + submission.getParticipation().getId(), HttpStatus.NOT_FOUND, String.class); + var newToken = request.putWithResponseBody("/api/account/participation-vcs-access-token?participationId=" + submission.getParticipation().getId(), null, String.class, + HttpStatus.OK); + assertThat(newToken).isNotEqualTo(token); + + submissionRepository.delete(submission); + participationVCSAccessTokenRepository.deleteAll(); + participationRepository.deleteById(submission.getParticipation().getId()); + } + + // Test + public void createAndDeleteUserVcsAccessToken() throws Exception { + User user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + assertThat(user.getVcsAccessToken()).isNull(); + + // Set expiry date to already past date -> Bad Request + ZonedDateTime expiryDate = ZonedDateTime.now().minusMonths(1); + var userDTO = request.putWithResponseBody("/api/account/user-vcs-access-token?expiryDate=" + expiryDate, null, UserDTO.class, HttpStatus.BAD_REQUEST); + assertThat(userDTO).isNull(); + + // Correct expiry date -> OK + expiryDate = ZonedDateTime.now().plusMonths(1); + userDTO = request.putWithResponseBody("/api/account/user-vcs-access-token?expiryDate=" + expiryDate, null, UserDTO.class, HttpStatus.OK); + user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + assertThat(user.getVcsAccessToken()).isEqualTo(userDTO.getVcsAccessToken()); + assertThat(user.getVcsAccessTokenExpiryDate()).isEqualTo(userDTO.getVcsAccessTokenExpiryDate()); + + // Delete token + request.delete("/api/account/user-vcs-access-token", HttpStatus.OK); + user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + assertThat(user.getVcsAccessToken()).isNull(); + assertThat(user.getVcsAccessTokenExpiryDate()).isNull(); + } + public UserRepository getUserRepository() { return userRepository; } diff --git a/src/test/javascript/spec/component/account/vcs-access-token-settings.component.spec.ts b/src/test/javascript/spec/component/account/vcs-access-token-settings.component.spec.ts new file mode 100644 index 000000000000..71439b28cfba --- /dev/null +++ b/src/test/javascript/spec/component/account/vcs-access-token-settings.component.spec.ts @@ -0,0 +1,187 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AccountService } from 'app/core/auth/account.service'; +import { of, throwError } from 'rxjs'; +import { By } from '@angular/platform-browser'; +import { MockComponent, MockPipe } from 'ng-mocks'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { User } from 'app/core/user/user.model'; +import { ArtemisTestModule } from '../../test.module'; +import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal.service'; +import { MockTranslateService, TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { VcsAccessTokensSettingsComponent } from 'app/shared/user-settings/vcs-access-tokens-settings/vcs-access-tokens-settings.component'; +import dayjs from 'dayjs/esm'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { AlertService } from 'app/core/util/alert.service'; + +describe('VcsAccessTokensSettingsComponent', () => { + let fixture: ComponentFixture; + let comp: VcsAccessTokensSettingsComponent; + + let accountServiceMock: { getAuthenticationState: jest.Mock; deleteUserVcsAccessToken: jest.Mock; addNewVcsAccessToken: jest.Mock }; + const alertServiceMock = { error: jest.fn(), addAlert: jest.fn() }; + let translateService: TranslateService; + + const token = 'initial-token'; + + beforeEach(async () => { + accountServiceMock = { + getAuthenticationState: jest.fn(), + deleteUserVcsAccessToken: jest.fn(), + addNewVcsAccessToken: jest.fn(), + }; + + await TestBed.configureTestingModule({ + imports: [ArtemisTestModule], + declarations: [ + VcsAccessTokensSettingsComponent, + TranslatePipeMock, + MockPipe(ArtemisDatePipe), + MockComponent(ButtonComponent), + MockComponent(FormDateTimePickerComponent), + ], + providers: [ + { provide: AccountService, useValue: accountServiceMock }, + { provide: TranslateService, useClass: MockTranslateService }, + { provide: NgbModal, useClass: MockNgbModalService }, + { provide: AlertService, useValue: alertServiceMock }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(VcsAccessTokensSettingsComponent); + comp = fixture.componentInstance; + + translateService = TestBed.inject(TranslateService); + translateService.currentLang = 'en'; + + accountServiceMock.getAuthenticationState.mockReturnValue(of({ id: 1, vcsAccessToken: token, vcsAccessTokenExpiryDate: '11:20' } as User)); + accountServiceMock.addNewVcsAccessToken.mockReturnValue(of({ id: 1, vcsAccessToken: token, vcsAccessTokenExpiryDate: '11:20' } as User)); + accountServiceMock.deleteUserVcsAccessToken.mockReturnValue(of({})); + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + it('should cancel token creation', () => { + accountServiceMock.getAuthenticationState.mockReturnValue(of({ id: 1 } as User)); + + startTokenCreation(); + + // click button to send expiry date to server, to create the new token + const createTokenButton = fixture.debugElement.query(By.css('#cancel-vcs-token-creation-button')); + createTokenButton.triggerEventHandler('onClick', null); + fixture.detectChanges(); + expect(comp.edit).toBeFalsy(); + }); + + it('should fail token creation with invalid date', () => { + accountServiceMock.getAuthenticationState.mockReturnValue(of({ id: 1 } as User)); + startTokenCreation(); + + // add an invalid expiry date + comp.expiryDate = dayjs().subtract(7, 'day'); + comp.validateDate(); + fixture.detectChanges(); + + // click button to send expiry date to server, to create the new token + const createTokenButton = fixture.debugElement.query(By.css('#create-vcs-token-button')); + createTokenButton.triggerEventHandler('onClick', null); + fixture.detectChanges(); + expect(comp.edit).toBeTruthy(); + expect(comp.currentUser?.vcsAccessToken).toBeUndefined(); + expect(alertServiceMock.error).toHaveBeenCalled(); + }); + + it('should handle failed token creation', () => { + accountServiceMock.addNewVcsAccessToken.mockImplementation(() => { + return throwError(() => new Error('Internal Server error')); + }); + + accountServiceMock.getAuthenticationState.mockReturnValue(of({ id: 1 } as User)); + startTokenCreation(); + + // add an invalid expiry date + comp.expiryDate = dayjs().add(7, 'day'); + comp.validExpiryDate = true; + + // click button to send expiry date to server, to create the new token + const createTokenButton = fixture.debugElement.query(By.css('#create-vcs-token-button')); + createTokenButton.triggerEventHandler('onClick', null); + fixture.detectChanges(); + expect(comp.edit).toBeTruthy(); + expect(alertServiceMock.error).toHaveBeenCalled(); + }); + + it('should create new vcs access token', () => { + const newToken = 'new-token'; + const tokenExpiryDate = dayjs().add(7, 'day'); + + accountServiceMock.getAuthenticationState.mockReturnValue(of({ id: 1 } as User)); + accountServiceMock.addNewVcsAccessToken.mockReturnValue(of({ body: { id: 1, vcsAccessToken: newToken, vcsAccessTokenExpiryDate: tokenExpiryDate.toISOString() } as User })); + startTokenCreation(); + + // add an expiry date + comp.expiryDate = tokenExpiryDate; + comp.validateDate(); + fixture.detectChanges(); + + // click button to send expiry date to server, to create the new token + const createTokenButton = fixture.debugElement.query(By.css('#create-vcs-token-button')); + createTokenButton.triggerEventHandler('onClick', null); + fixture.detectChanges(); + + expect(comp.edit).toBeFalsy(); + expect(accountServiceMock.addNewVcsAccessToken).toHaveBeenCalled(); + expect(comp.currentUser!.vcsAccessToken).toEqual(newToken); + }); + + it('should delete vcs access token', () => { + accountServiceMock.deleteUserVcsAccessToken.mockImplementation(() => { + return throwError(() => new Error('Internal Server error')); + }); + comp.ngOnInit(); + comp.deleteVcsAccessToken(); + expect(accountServiceMock.deleteUserVcsAccessToken).toHaveBeenCalled(); + expect(alertServiceMock.error).toHaveBeenCalled(); + }); + + it('should handle error when delete vcs access token fails', () => { + const newToken = 'new-token'; + accountServiceMock.addNewVcsAccessToken.mockReturnValue(of({ id: 1, vcsAccessToken: newToken, vcsAccessTokenExpiryDate: '11:20' } as User)); + comp.ngOnInit(); + expect(comp.currentUser!.vcsAccessToken).toEqual(token); + comp.deleteVcsAccessToken(); + expect(accountServiceMock.deleteUserVcsAccessToken).toHaveBeenCalled(); + expect(comp.currentUser!.vcsAccessToken).toBeUndefined(); + }); + + it('should set wasCopied to true and back to false after 3 seconds on successful copy', () => { + comp.ngOnInit(); + + jest.useFakeTimers(); + comp.onCopyFinished(true); + expect(comp.wasCopied).toBeTruthy(); + jest.advanceTimersByTime(3000); + expect(comp.wasCopied).toBeFalsy(); + jest.useRealTimers(); + }); + + it('should not change wasCopied if copy is unsuccessful', () => { + comp.ngOnInit(); + comp.onCopyFinished(false); + + // Verify that wasCopied remains false + expect(comp.wasCopied).toBeFalsy(); + }); + + function startTokenCreation() { + comp.ngOnInit(); + fixture.detectChanges(); + expect(comp.currentUser!.vcsAccessToken).toBeUndefined(); + + // click on new token button + const addTokenButton = fixture.debugElement.query(By.css('#add-new-token-button')); + addTokenButton.triggerEventHandler('onClick', null); + fixture.detectChanges(); + expect(comp.edit).toBeTruthy(); + } +}); diff --git a/src/test/javascript/spec/component/assessment-dashboard/assessment-dashboard.component.spec.ts b/src/test/javascript/spec/component/assessment-dashboard/assessment-dashboard.component.spec.ts index b9b5192b7e70..bc3cfc327be3 100644 --- a/src/test/javascript/spec/component/assessment-dashboard/assessment-dashboard.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-dashboard/assessment-dashboard.component.spec.ts @@ -12,10 +12,10 @@ import { ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.mode import { TutorParticipationStatus } from 'app/entities/participation/tutor-participation.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { SecondCorrectionEnableButtonComponent } from 'app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component'; import { AssessmentDashboardComponent } from 'app/course/dashboards/assessment-dashboard/assessment-dashboard.component'; diff --git a/src/test/javascript/spec/component/assessment-dashboard/exam-assessment-buttons.component.spec.ts b/src/test/javascript/spec/component/assessment-dashboard/exam-assessment-buttons.component.spec.ts index 8b424f245ce8..e3e18abc527a 100644 --- a/src/test/javascript/spec/component/assessment-dashboard/exam-assessment-buttons.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-dashboard/exam-assessment-buttons.component.spec.ts @@ -8,7 +8,7 @@ import { Course } from 'app/entities/course.model'; import { of, throwError } from 'rxjs'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { User } from 'app/core/user/user.model'; import dayjs from 'dayjs/esm'; import { By } from '@angular/platform-browser'; diff --git a/src/test/javascript/spec/component/assessment-dashboard/exercise-assessment-dashboard.component.spec.ts b/src/test/javascript/spec/component/assessment-dashboard/exercise-assessment-dashboard.component.spec.ts index f8bdd14321c4..92a1d051230e 100644 --- a/src/test/javascript/spec/component/assessment-dashboard/exercise-assessment-dashboard.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-dashboard/exercise-assessment-dashboard.component.spec.ts @@ -24,19 +24,19 @@ import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.mod import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { FileUploadSubmissionService } from 'app/exercises/file-upload/participate/file-upload-submission.service'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Complaint, ComplaintType } from 'app/entities/complaint.model'; import { Language } from 'app/entities/course.model'; import { Submission, SubmissionExerciseType } from 'app/entities/submission.model'; import { TutorParticipationService } from 'app/exercises/shared/dashboards/tutor/tutor-participation.service'; import { Participation } from 'app/entities/participation/participation.model'; import { Result } from 'app/entities/result.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { SecondCorrectionEnableButtonComponent } from 'app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component'; import { LanguageTableCellComponent } from 'app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component'; diff --git a/src/test/javascript/spec/component/assessment-shared/assessment-header.component.spec.ts b/src/test/javascript/spec/component/assessment-shared/assessment-header.component.spec.ts index 83d241b52277..6c151bfbce91 100644 --- a/src/test/javascript/spec/component/assessment-shared/assessment-header.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-shared/assessment-header.component.spec.ts @@ -14,7 +14,7 @@ import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.s import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { TranslateDirective, TranslateService } from '@ngx-translate/core'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { GradingSystemService } from 'app/grading-system/grading-system.service'; import { GradingScale } from 'app/entities/grading-scale.model'; diff --git a/src/test/javascript/spec/component/assessment-shared/assessment-instructions.component.spec.ts b/src/test/javascript/spec/component/assessment-shared/assessment-instructions.component.spec.ts index 9f823f82f15f..548e3b7b9c1a 100644 --- a/src/test/javascript/spec/component/assessment-shared/assessment-instructions.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-shared/assessment-instructions.component.spec.ts @@ -6,9 +6,9 @@ import { ExpandableSectionComponent } from 'app/assessment/assessment-instructio import { StructuredGradingInstructionsAssessmentLayoutComponent } from 'app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseType } from 'app/entities/exercise.model'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { ExtensionPointDirective } from 'app/shared/extension-point/extension-point.directive'; diff --git a/src/test/javascript/spec/component/assessment-shared/assessment-locks.component.spec.ts b/src/test/javascript/spec/component/assessment-shared/assessment-locks.component.spec.ts index a61789265a44..54ebbc921892 100644 --- a/src/test/javascript/spec/component/assessment-shared/assessment-locks.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-shared/assessment-locks.component.spec.ts @@ -8,8 +8,8 @@ import { MockHasAnyAuthorityDirective } from '../../helpers/mocks/directive/mock import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modeling-assessment.service'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; import { ProgrammingAssessmentManualResultService } from 'app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service'; diff --git a/src/test/javascript/spec/component/assessment-shared/assessment-warning.component.spec.ts b/src/test/javascript/spec/component/assessment-shared/assessment-warning.component.spec.ts index e7ba6af20d1c..eefbd24f2338 100644 --- a/src/test/javascript/spec/component/assessment-shared/assessment-warning.component.spec.ts +++ b/src/test/javascript/spec/component/assessment-shared/assessment-warning.component.spec.ts @@ -2,9 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import dayjs from 'dayjs/esm'; import { AssessmentWarningComponent } from 'app/assessment/assessment-warning/assessment-warning.component'; import { ArtemisTestModule } from '../../test.module'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; describe('AssessmentWarningComponent', () => { let component: AssessmentWarningComponent; diff --git a/src/test/javascript/spec/component/code-editor/code-editor-build-output.component.spec.ts b/src/test/javascript/spec/component/code-editor/code-editor-build-output.component.spec.ts index cca5d65fc88d..87206e5b1fff 100644 --- a/src/test/javascript/spec/component/code-editor/code-editor-build-output.component.spec.ts +++ b/src/test/javascript/spec/component/code-editor/code-editor-build-output.component.spec.ts @@ -7,17 +7,17 @@ import { ParticipationWebsocketService } from 'app/overview/participation-websoc import { triggerChanges } from '../../helpers/utils/general.utils'; import { CodeEditorBuildOutputComponent } from 'app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component'; import { Participation } from 'app/entities/participation/participation.model'; -import { BuildLogEntryArray } from 'app/entities/build-log.model'; +import { BuildLogEntryArray } from 'app/entities/programming/build-log.model'; import { CodeEditorBuildLogService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { MockResultService } from '../../helpers/mocks/service/mock-result.service'; import { MockCodeEditorBuildLogService } from '../../helpers/mocks/service/mock-code-editor-build-log.service'; import { MockParticipationWebsocketService } from '../../helpers/mocks/service/mock-participation-websocket.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Result } from 'app/entities/result.model'; -import { StaticCodeAnalysisIssue } from 'app/entities/static-code-analysis-issue.model'; +import { StaticCodeAnalysisIssue } from 'app/entities/programming/static-code-analysis-issue.model'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { MockPipe, MockProvider } from 'ng-mocks'; import { CodeEditorSubmissionService } from 'app/exercises/programming/shared/code-editor/service/code-editor-submission.service'; diff --git a/src/test/javascript/spec/component/complaints/complaint-response.service.spec.ts b/src/test/javascript/spec/component/complaints/complaint-response.service.spec.ts index 149e46dae205..5d93f7870fa0 100644 --- a/src/test/javascript/spec/component/complaints/complaint-response.service.spec.ts +++ b/src/test/javascript/spec/component/complaints/complaint-response.service.spec.ts @@ -9,7 +9,7 @@ import { AccountService } from 'app/core/auth/account.service'; import { MockProvider } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { User } from 'app/core/user/user.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ComplaintAction, ComplaintResponseUpdateDTO } from 'app/entities/complaint-response-dto.model'; describe('ComplaintResponseService', () => { diff --git a/src/test/javascript/spec/component/complaints/complaint-student-view.component.spec.ts b/src/test/javascript/spec/component/complaints/complaint-student-view.component.spec.ts index 58ba0cd9a035..528e9d6d5c6a 100644 --- a/src/test/javascript/spec/component/complaints/complaint-student-view.component.spec.ts +++ b/src/test/javascript/spec/component/complaints/complaint-student-view.component.spec.ts @@ -7,7 +7,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; import { Participation } from 'app/entities/participation/participation.model'; import { Result } from 'app/entities/result.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Submission } from 'app/entities/submission.model'; import { Complaint } from 'app/entities/complaint.model'; import { Observable, of } from 'rxjs'; diff --git a/src/test/javascript/spec/component/complaints/complaint.service.spec.ts b/src/test/javascript/spec/component/complaints/complaint.service.spec.ts index 95b704d76e81..959355b8d92a 100644 --- a/src/test/javascript/spec/component/complaints/complaint.service.spec.ts +++ b/src/test/javascript/spec/component/complaints/complaint.service.spec.ts @@ -1,7 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { ComplaintService } from 'app/complaints/complaint.service'; import { Complaint, ComplaintType } from 'app/entities/complaint.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; import { User } from 'app/core/user/user.model'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/test/javascript/spec/component/complaints/list-of-complaints.component.spec.ts b/src/test/javascript/spec/component/complaints/list-of-complaints.component.spec.ts index aeff086bc8e6..e3abe463b09d 100644 --- a/src/test/javascript/spec/component/complaints/list-of-complaints.component.spec.ts +++ b/src/test/javascript/spec/component/complaints/list-of-complaints.component.spec.ts @@ -13,8 +13,8 @@ import { Complaint, ComplaintType } from 'app/entities/complaint.model'; import { Course } from 'app/entities/course.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Result } from 'app/entities/result.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; diff --git a/src/test/javascript/spec/component/consistency-check/consistency-check.component.spec.ts b/src/test/javascript/spec/component/consistency-check/consistency-check.component.spec.ts index 78a8c5a7577c..5a845b83473d 100644 --- a/src/test/javascript/spec/component/consistency-check/consistency-check.component.spec.ts +++ b/src/test/javascript/spec/component/consistency-check/consistency-check.component.spec.ts @@ -1,6 +1,6 @@ import { ConsistencyCheckComponent } from 'app/shared/consistency-check/consistency-check.component'; import { ConsistencyCheckService } from 'app/shared/consistency-check/consistency-check.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { ConsistencyCheckError, ErrorType } from 'app/entities/consistency-check-result.model'; import { ArtemisTestModule } from '../../test.module'; diff --git a/src/test/javascript/spec/component/course/course-exercises.component.spec.ts b/src/test/javascript/spec/component/course/course-exercises.component.spec.ts index 017814b34c5e..3a2e57aa1588 100644 --- a/src/test/javascript/spec/component/course/course-exercises.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-exercises.component.spec.ts @@ -29,6 +29,7 @@ import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { By } from '@angular/platform-browser'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; describe('CourseExercisesComponent', () => { let fixture: ComponentFixture; @@ -44,7 +45,7 @@ describe('CourseExercisesComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [ArtemisTestModule, FormsModule, RouterTestingModule.withRoutes([]), MockModule(ReactiveFormsModule)], + imports: [ArtemisTestModule, FormsModule, RouterTestingModule.withRoutes([]), MockModule(ReactiveFormsModule), MockDirective(TranslateDirective)], declarations: [ CourseExercisesComponent, SidebarComponent, diff --git a/src/test/javascript/spec/component/course/course-lti-configuration.component.spec.ts b/src/test/javascript/spec/component/course/course-lti-configuration.component.spec.ts index c8d23f7f5ddc..2912262c8621 100644 --- a/src/test/javascript/spec/component/course/course-lti-configuration.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-lti-configuration.component.spec.ts @@ -11,7 +11,7 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; import { CourseLtiConfigurationComponent } from 'app/course/manage/course-lti-configuration/course-lti-configuration.component'; import { SortService } from 'app/shared/service/sort.service'; import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; diff --git a/src/test/javascript/spec/component/course/course-overview.component.spec.ts b/src/test/javascript/spec/component/course/course-overview.component.spec.ts index cb3496dcb9b7..b411f3760596 100644 --- a/src/test/javascript/spec/component/course/course-overview.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-overview.component.spec.ts @@ -31,7 +31,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { AfterViewInit, ChangeDetectorRef, Component, TemplateRef, ViewChild } from '@angular/core'; import { By } from '@angular/platform-browser'; import { TeamAssignmentPayload } from 'app/entities/team.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { CourseOverviewComponent } from 'app/overview/course-overview.component'; import { BarControlConfiguration, BarControlConfigurationProvider } from 'app/shared/tab-bar/tab-bar'; diff --git a/src/test/javascript/spec/component/course/course-overview.service.spec.ts b/src/test/javascript/spec/component/course/course-overview.service.spec.ts index d0527dde176a..7dbc94aabaea 100644 --- a/src/test/javascript/spec/component/course/course-overview.service.spec.ts +++ b/src/test/javascript/spec/component/course/course-overview.service.spec.ts @@ -11,8 +11,8 @@ import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ChannelDTO, ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; describe('CourseOverviewService', () => { diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index 2239ab6f91d1..289e91dbd948 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -29,7 +29,7 @@ import { OrganizationManagementService } from 'app/admin/organization-management import { Organization } from 'app/entities/organization.model'; import dayjs from 'dayjs/esm'; import { ImageCropperModule } from 'app/shared/image-cropper/image-cropper.module'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../../helpers/mocks/service/mock-account.service'; diff --git a/src/test/javascript/spec/component/course/course.component.spec.ts b/src/test/javascript/spec/component/course/course.component.spec.ts index dae7b6996b55..0922550c27e3 100644 --- a/src/test/javascript/spec/component/course/course.component.spec.ts +++ b/src/test/javascript/spec/component/course/course.component.spec.ts @@ -32,7 +32,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { Component } from '@angular/core'; import { of } from 'rxjs'; import dayjs from 'dayjs/esm'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; const endDate1 = dayjs().add(1, 'days'); diff --git a/src/test/javascript/spec/component/course/exercise-filter.model.spec.ts b/src/test/javascript/spec/component/course/exercise-filter.model.spec.ts index 8aadaa3d2189..b2720b28a415 100644 --- a/src/test/javascript/spec/component/course/exercise-filter.model.spec.ts +++ b/src/test/javascript/spec/component/course/exercise-filter.model.spec.ts @@ -1,15 +1,13 @@ import { ExerciseFilter } from 'app/entities/exercise-filter.model'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; describe('Exercise Filter Test', () => { - const category1 = new ExerciseCategory(); - category1.category = 'Easy'; - const category2 = new ExerciseCategory(); - category2.category = 'Hard'; + const category1 = new ExerciseCategory('Easy', undefined); + const category2 = new ExerciseCategory('Hard', undefined); const course: Course = { id: 123 } as Course; const exercise1 = new ProgrammingExercise(course, undefined); exercise1.id = 1; diff --git a/src/test/javascript/spec/component/exam-exercise-row-buttons/exam-exercise-row-buttons.component.spec.ts b/src/test/javascript/spec/component/exam-exercise-row-buttons/exam-exercise-row-buttons.component.spec.ts index 187443be2be4..cc2d7aaf230a 100644 --- a/src/test/javascript/spec/component/exam-exercise-row-buttons/exam-exercise-row-buttons.component.spec.ts +++ b/src/test/javascript/spec/component/exam-exercise-row-buttons/exam-exercise-row-buttons.component.spec.ts @@ -3,9 +3,9 @@ import { ArtemisTestModule } from '../../test.module'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; import { ExamExerciseRowButtonsComponent } from 'app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ExerciseType } from 'app/entities/exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { ModelingExerciseService } from 'app/exercises/modeling/manage/modeling-exercise.service'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; @@ -17,7 +17,7 @@ import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { of, throwError } from 'rxjs'; import { HttpErrorResponse } from '@angular/common/http'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockDirective, MockProvider } from 'ng-mocks'; import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; import { MockRouterLinkDirective } from '../../helpers/mocks/directive/mock-router-link.directive'; diff --git a/src/test/javascript/spec/component/exam/exam-update.component.spec.ts b/src/test/javascript/spec/component/exam/exam-update.component.spec.ts index 6de1e1db5c13..7dd87ad9bef8 100644 --- a/src/test/javascript/spec/component/exam/exam-update.component.spec.ts +++ b/src/test/javascript/spec/component/exam/exam-update.component.spec.ts @@ -15,7 +15,7 @@ import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Course, CourseInformationSharingConfiguration } from 'app/entities/course.model'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; @@ -41,7 +41,7 @@ import { DifficultyBadgeComponent } from 'app/exercises/shared/exercise-headers/ import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; import { TitleChannelNameComponent } from 'app/shared/form/title-channel-name/title-channel-name.component'; import { UMLDiagramType } from '@ls1intum/apollon'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; @Component({ diff --git a/src/test/javascript/spec/component/exam/feedback.utils.spec.ts b/src/test/javascript/spec/component/exam/feedback.utils.spec.ts index adf848356db6..5fc2c9311d97 100644 --- a/src/test/javascript/spec/component/exam/feedback.utils.spec.ts +++ b/src/test/javascript/spec/component/exam/feedback.utils.spec.ts @@ -2,7 +2,7 @@ import { prepareFeedbackComponentParameters } from 'app/exercises/shared/feedbac import { ResultTemplateStatus } from 'app/exercises/shared/result/result.utils'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import dayjs from 'dayjs/esm'; import { of } from 'rxjs'; import { MockProvider } from 'ng-mocks'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-exercise-import.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-exercise-import.component.spec.ts index 304a86ef1639..38b8d1df5b58 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-exercise-import.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-exercise-import.component.spec.ts @@ -3,12 +3,12 @@ import { ArtemisTestModule } from '../../../test.module'; import { MockModule, MockPipe } from 'ng-mocks'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { FormsModule } from '@angular/forms'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamExerciseImportComponent } from 'app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-exercise-row-buttons.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-exercise-row-buttons.component.spec.ts index fc769df6b8d6..430a59b2f55b 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-exercise-row-buttons.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-exercise-row-buttons.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Router, RouterModule } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { HttpResponse } from '@angular/common/http'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamExerciseRowButtonsComponent } from 'app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component'; import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-import.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-import.component.spec.ts index c743acdb5e98..b0476cd518fe 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-import.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-import.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-management-resolve.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-management-resolve.spec.ts index c3a2ed6b9848..9c7482c9fe6f 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-management-resolve.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-management-resolve.spec.ts @@ -6,7 +6,7 @@ import { MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; import { StudentExamWithGradeDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-management.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-management.component.spec.ts index ecd81a41c681..b5601206f621 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-management.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-management.component.spec.ts @@ -10,11 +10,11 @@ import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, Router, UrlSegment, convertToParamMap } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { ExamManagementComponent } from 'app/exam/manage/exam-management.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { SortService } from 'app/shared/service/sort.service'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; import { EventManager } from 'app/core/util/event-manager.service'; import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { MockDirective, MockPipe } from 'ng-mocks'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-management.service.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-management.service.spec.ts index a452d76c4a7b..747d87a8a3c5 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-management.service.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-management.service.spec.ts @@ -3,19 +3,19 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { Course } from 'app/entities/course.model'; import { ArtemisTestModule } from '../../../test.module'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; import { StudentDTO } from 'app/entities/student-dto.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExamScoreDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { AccountService } from 'app/core/auth/account.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { UMLDiagramType } from '@ls1intum/apollon'; describe('Exam Management Service Tests', () => { diff --git a/src/test/javascript/spec/component/exam/manage/exam-students-attendance-check.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-students-attendance-check.component.spec.ts index 1e6b737b7266..dfe43434b530 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-students-attendance-check.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-students-attendance-check.component.spec.ts @@ -4,8 +4,8 @@ import { RouterTestingModule } from '@angular/router/testing'; import { TranslateService } from '@ngx-translate/core'; import { User } from 'app/core/user/user.model'; import { Course } from 'app/entities/course.model'; -import { ExamUserAttendanceCheckDTO } from 'app/entities/exam-users-attendance-check-dto.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExamStudentsAttendanceCheckComponent } from 'app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/exam/manage/exam-students.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exam-students.component.spec.ts index 048b06c65333..58a448041153 100644 --- a/src/test/javascript/spec/component/exam/manage/exam-students.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exam-students.component.spec.ts @@ -7,7 +7,7 @@ import { NgxDatatableModule } from '@flaviosantoro92/ngx-datatable'; import { User } from 'app/core/user/user.model'; import { UserService } from 'app/core/user/user.service'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExamStudentsComponent } from 'app/exam/manage/students/exam-students.component'; import { StudentsUploadImagesButtonComponent } from 'app/exam/manage/students/upload-images/students-upload-images-button.component'; @@ -20,8 +20,8 @@ import { MockTranslateService } from '../../../helpers/mocks/service/mock-transl import { ArtemisTestModule } from '../../../test.module'; import { UsersImportButtonComponent } from 'app/shared/user-import/users-import-button.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; -import { ExamUserDTO } from 'app/entities/exam-user-dto.model'; -import { ExamUser } from 'app/entities/exam-user.model'; +import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; +import { ExamUser } from 'app/entities/exam/exam-user.model'; describe('ExamStudentsComponent', () => { const course = { id: 1 } as Course; diff --git a/src/test/javascript/spec/component/exam/manage/exams/exam-announcement-dialog/exam-live-announcement-create-button.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exams/exam-announcement-dialog/exam-live-announcement-create-button.component.spec.ts index 1c31b7cd263f..09c6836b7cbb 100644 --- a/src/test/javascript/spec/component/exam/manage/exams/exam-announcement-dialog/exam-live-announcement-create-button.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exams/exam-announcement-dialog/exam-live-announcement-create-button.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { faBullhorn } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { By } from '@angular/platform-browser'; diff --git a/src/test/javascript/spec/component/exam/manage/exams/exam-checklist.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exams/exam-checklist.component.spec.ts index 317ab262a4a9..814df06dd42b 100644 --- a/src/test/javascript/spec/component/exam/manage/exams/exam-checklist.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exams/exam-checklist.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamChecklistExerciseGroupTableComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component'; import { ExamChecklistComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.component'; import { ProgressBarComponent } from 'app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component'; diff --git a/src/test/javascript/spec/component/exam/manage/exams/exam-detail.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exams/exam-detail.component.spec.ts index 211c90093b8b..9d29bcdc9a23 100644 --- a/src/test/javascript/spec/component/exam/manage/exams/exam-detail.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exams/exam-detail.component.spec.ts @@ -8,7 +8,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { AccountService } from 'app/core/auth/account.service'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ChecklistCheckComponent } from 'app/shared/components/checklist-check.component'; import { ExamChecklistExerciseGroupTableComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component'; import { ExamChecklistComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.component'; diff --git a/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-group-update.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-group-update.component.spec.ts index 8ca421927fb9..900f896fa97a 100644 --- a/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-group-update.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-group-update.component.spec.ts @@ -6,7 +6,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { TranslateService } from '@ngx-translate/core'; import { EntityResponseType } from 'app/complaints/complaint.service'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExerciseGroupUpdateComponent } from 'app/exam/manage/exercise-groups/exercise-group-update.component'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; diff --git a/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-groups.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-groups.component.spec.ts index cc5dd2cb5225..040752cb4ffb 100644 --- a/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-groups.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exercise-groups/exercise-groups.component.spec.ts @@ -7,8 +7,8 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; import { Course } from 'app/entities/course.model'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; diff --git a/src/test/javascript/spec/component/exam/manage/exercise-groups/programming-exercise-group-cell.component.spec.ts b/src/test/javascript/spec/component/exam/manage/exercise-groups/programming-exercise-group-cell.component.spec.ts index fe38c8c71ae0..499338bffb52 100644 --- a/src/test/javascript/spec/component/exam/manage/exercise-groups/programming-exercise-group-cell.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/exercise-groups/programming-exercise-group-cell.component.spec.ts @@ -5,7 +5,7 @@ import { ProgrammingExerciseGroupCellComponent } from 'app/exam/manage/exercise- import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ExerciseType } from 'app/entities/exercise.model'; import { By } from '@angular/platform-browser'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TranslatePipeMock } from '../../../../helpers/mocks/service/mock-translate.service'; import { of } from 'rxjs'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; @@ -30,6 +30,7 @@ describe('Programming Exercise Group Cell Component', () => { }, allowOfflineIde: true, allowOnlineEditor: true, + allowOnlineIde: false, } as any as ProgrammingExercise; let mockedProfileService: ProfileService; @@ -93,11 +94,15 @@ describe('Programming Exercise Group Cell Component', () => { const div0 = fixture.debugElement.query(By.css('div > div > div:first-child')); expect(div0).not.toBeNull(); - expect(div0.nativeElement.textContent?.trim()).toBe('artemisApp.programmingExercise.offlineIde : true'); + expect(div0.nativeElement.textContent?.trim()).toBe(': true'); const div1 = fixture.debugElement.query(By.css('div > div > div:nth-child(2)')); expect(div1).not.toBeNull(); - expect(div1.nativeElement.textContent?.trim()).toBe('artemisApp.programmingExercise.onlineEditor : true'); + expect(div1.nativeElement.textContent?.trim()).toBe(': true'); + + const div2 = fixture.debugElement.query(By.css('div > div > div:nth-child(3)')); + expect(div2).not.toBeNull(); + expect(div2.nativeElement.textContent?.trim()).toBe(': false'); }); it('should download the repository', () => { diff --git a/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts b/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts index e4ecca13e935..cb10c66e1574 100644 --- a/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/programming-exam-diff.component.spec.ts @@ -4,7 +4,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { CommitsInfoComponent } from 'app/exercises/programming/shared/commits-info/commits-info.component'; import { MockComponent, MockPipe } from 'ng-mocks'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { of } from 'rxjs'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/exam-status.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/exam-status.component.spec.ts index 213d77fac9b6..453df0f6be55 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/exam-status.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/exam-status.component.spec.ts @@ -5,12 +5,12 @@ import { MockPipe } from 'ng-mocks'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { MockTranslateService } from '../../../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { MockExamChecklistService } from '../../../../helpers/mocks/service/mock-exam-checklist.service'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { of } from 'rxjs'; import { Course } from 'app/entities/course.model'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail-table-row.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail-table-row.component.spec.ts index d7cd5e7161e6..22cdaf2eb531 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail-table-row.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail-table-row.component.spec.ts @@ -9,11 +9,11 @@ import { ReactiveFormsModule } from '@angular/forms'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; import { Result } from 'app/entities/result.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail.component.spec.ts index ae4ca95910a7..531f3d5aef50 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-detail.component.spec.ts @@ -17,7 +17,7 @@ import { NgForm, NgModel, ReactiveFormsModule } from '@angular/forms'; import { Exercise } from 'app/entities/exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-summary.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-summary.component.spec.ts index fe4ee6e5d5eb..07ff99eac312 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-summary.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-summary.component.spec.ts @@ -4,7 +4,7 @@ import { MockComponent } from 'ng-mocks'; import { Course } from 'app/entities/course.model'; import { of } from 'rxjs'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExamSummaryComponent } from 'app/exam/manage/student-exams/student-exam-summary.component'; import { ExamResultSummaryComponent } from 'app/exam/participate/summary/exam-result-summary.component'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-timeline.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-timeline.component.spec.ts index 09cc4dee6962..e28f9b6390ca 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-timeline.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/student-exam-timeline.component.spec.ts @@ -4,7 +4,7 @@ import { MockComponent, MockPipe } from 'ng-mocks'; import { Course } from 'app/entities/course.model'; import { Observable, of } from 'rxjs'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExamTimelineComponent } from 'app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component'; import { ModelingExamSubmissionComponent } from 'app/exam/participate/exercises/modeling/modeling-exam-submission.component'; import { TextExamSubmissionComponent } from 'app/exam/participate/exercises/text/text-exam-submission.component'; @@ -16,14 +16,14 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ExamNavigationBarComponent } from 'app/exam/participate/exam-navigation-bar/exam-navigation-bar.component'; import { MockTranslateValuesDirective } from '../../../../helpers/mocks/directive/mock-translate-values.directive'; import { EntityArrayResponseType, SubmissionService } from 'app/exercises/shared/submission/submission.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import dayjs from 'dayjs/esm'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; import { SubmissionVersion } from 'app/entities/submission-version.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Submission } from 'app/entities/submission.model'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockLocalStorageService } from '../../../../helpers/mocks/service/mock-local-storage.service'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/student-exams.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/student-exams.component.spec.ts index 1534c947d717..c5fbbc5b1a02 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/student-exams.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/student-exams.component.spec.ts @@ -16,7 +16,7 @@ import { Course } from 'app/entities/course.model'; import { of, throwError } from 'rxjs'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { StudentExam } from 'app/entities/student-exam.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { User } from 'app/core/user/user.model'; import dayjs from 'dayjs/esm'; import { By } from '@angular/platform-browser'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-button.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-button.component.spec.ts index 3a2b4012bc1f..918148de4548 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-button.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-button.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent, MockModule, MockProvider } from 'ng-mocks'; import { AlertService } from 'app/core/util/alert.service'; import { TranslateModule } from '@ngx-translate/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { By } from '@angular/platform-browser'; import { NgbModal, NgbModalRef, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ButtonComponent } from 'app/shared/components/button.component'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-dialog.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-dialog.component.spec.ts index f0c4be9a98ce..c96fe070e773 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-dialog.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/students-upload-images-dialog.component.spec.ts @@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { StudentsUploadImagesDialogComponent } from 'app/exam/manage/students/upload-images/students-upload-images-dialog.component'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/user-import-button.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/user-import-button.component.spec.ts index ca19b1567ba3..e803ff5deb7f 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/user-import-button.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/user-import-button.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent, MockModule, MockProvider } from 'ng-mocks'; import { AlertService } from 'app/core/util/alert.service'; import { TranslateModule } from '@ngx-translate/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { By } from '@angular/platform-browser'; import { NgbModal, NgbModalRef, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ButtonComponent } from 'app/shared/components/button.component'; diff --git a/src/test/javascript/spec/component/exam/manage/student-exams/user-import-dialog.component.spec.ts b/src/test/javascript/spec/component/exam/manage/student-exams/user-import-dialog.component.spec.ts index 0541146c9d95..4f97eab7cc62 100644 --- a/src/test/javascript/spec/component/exam/manage/student-exams/user-import-dialog.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/student-exams/user-import-dialog.component.spec.ts @@ -5,7 +5,7 @@ import { By } from '@angular/platform-browser'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; @@ -19,7 +19,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { Router } from '@angular/router'; import * as fs from 'fs'; import * as path from 'path'; -import { ExamUserDTO } from 'app/entities/exam-user-dto.model'; +import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; describe('UsersImportDialogComponent', () => { let fixture: ComponentFixture; diff --git a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-behavior.component.spec.ts b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-behavior.component.spec.ts index ae26c8a72e3b..b7586940ad52 100644 --- a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-behavior.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-behavior.component.spec.ts @@ -14,7 +14,7 @@ import { ButtonComponent } from 'app/shared/components/button.component'; import { MockRouterLinkDirective } from '../../../../helpers/mocks/directive/mock-router-link.directive'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { Exercise } from 'app/entities/exercise.model'; -import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model'; import { MockRouter } from '../../../../helpers/mocks/mock-router'; import { FormsModule } from '@angular/forms'; import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; diff --git a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions-overview.component.spec.ts b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions-overview.component.spec.ts index 3676e637591f..f375ca4fb6cc 100644 --- a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions-overview.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions-overview.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; import { SuspiciousSessionsOverviewComponent } from 'app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component'; -import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model'; import { ArtemisTestModule } from '../../../../test.module'; import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; import { SuspiciousSessionsComponent } from 'app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component'; diff --git a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.component.spec.ts b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.component.spec.ts index 68d38513ba77..8024d4a251c2 100644 --- a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.component.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.component.spec.ts @@ -4,7 +4,7 @@ import { SuspiciousSessionsComponent } from 'app/exam/manage/suspicious-behavior import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockPipe } from 'ng-mocks'; import { StudentExam } from 'app/entities/student-exam.model'; -import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { ArtemisTestModule } from '../../../../test.module'; diff --git a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.service.spec.ts b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.service.spec.ts index bc1e1522f292..0760f147551b 100644 --- a/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.service.spec.ts +++ b/src/test/javascript/spec/component/exam/manage/suspicious-behavior/suspicious-sessions.service.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { SuspiciousSessionsService } from 'app/exam/manage/suspicious-behavior/suspicious-sessions.service'; -import { SuspiciousExamSessions, SuspiciousSessionReason, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam-session.model'; +import { SuspiciousExamSessions, SuspiciousSessionReason, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam/exam-session.model'; describe('SuspiciousSessionsService', () => { let service: SuspiciousSessionsService; diff --git a/src/test/javascript/spec/component/exam/participate/exam-bar.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-bar.component.spec.ts index 2c187f95b291..03eb5e090d94 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-bar.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-bar.component.spec.ts @@ -12,7 +12,7 @@ import { MockSyncStorage } from '../../../helpers/mocks/service/mock-sync-storag import { TranslateService } from '@ngx-translate/core'; import { ExamBarComponent } from 'app/exam/participate/exam-bar/exam-bar.component'; import { MockResizeObserver } from '../../../helpers/mocks/service/mock-resize-observer'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; describe('ExamBarComponent', () => { diff --git a/src/test/javascript/spec/component/exam/participate/exam-navigation-bar.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-navigation-bar.component.spec.ts index fb8f91a5ba57..40c65e3f008d 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-navigation-bar.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-navigation-bar.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin import dayjs from 'dayjs/esm'; import { MockComponent, MockDirective, MockModule } from 'ng-mocks'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ExamSession } from 'app/entities/exam-session.model'; +import { ExamSession } from 'app/entities/exam/exam-session.model'; import { of } from 'rxjs'; import { ExamNavigationBarComponent } from 'app/exam/participate/exam-navigation-bar/exam-navigation-bar.component'; import { CodeEditorRepositoryService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; diff --git a/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts index 9e2e2cf7b70d..45ce49fc2b4d 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { MockComponent, MockModule } from 'ng-mocks'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ExamSession } from 'app/entities/exam-session.model'; +import { ExamSession } from 'app/entities/exam/exam-session.model'; import { of } from 'rxjs'; import { ExamNavigationSidebarComponent } from 'app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component'; import { CodeEditorRepositoryService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; diff --git a/src/test/javascript/spec/component/exam/participate/exam-participation-cover.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-participation-cover.component.spec.ts index 9e7b34dedae5..4f5514b08626 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-participation-cover.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-participation-cover.component.spec.ts @@ -7,7 +7,7 @@ import { TranslateService } from '@ngx-translate/core'; import { AccountService } from 'app/core/auth/account.service'; import { User } from 'app/core/user/user.model'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExamParticipationCoverComponent } from 'app/exam/participate/exam-cover/exam-participation-cover.component'; diff --git a/src/test/javascript/spec/component/exam/participate/exam-participation.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-participation.component.spec.ts index 4742c845c8ad..de433ab78169 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-participation.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-participation.component.spec.ts @@ -9,20 +9,20 @@ import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { Course } from 'app/entities/course.model'; -import { ExamPage } from 'app/entities/exam-page.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamPage } from 'app/entities/exam/exam-page.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { InitializationState } from 'app/entities/participation/participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { Submission } from 'app/entities/submission.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ExamExerciseUpdateService } from 'app/exam/manage/exam-exercise-update.service'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { TestRunRibbonComponent } from 'app/exam/manage/test-runs/test-run-ribbon.component'; diff --git a/src/test/javascript/spec/component/exam/participate/exam-start-information/exam-start-information.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-start-information/exam-start-information.component.spec.ts index 5f3b9f04df78..6332ee962a6c 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-start-information/exam-start-information.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-start-information/exam-start-information.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExamStartInformationComponent } from 'app/exam/participate/exam-start-information/exam-start-information.component'; import { InformationBoxComponent } from 'app/shared/information-box/information-box.component'; diff --git a/src/test/javascript/spec/component/exam/participate/exam.utils.spec.ts b/src/test/javascript/spec/component/exam/participate/exam.utils.spec.ts index 1254b6abcb98..2f75558e228f 100644 --- a/src/test/javascript/spec/component/exam/participate/exam.utils.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam.utils.spec.ts @@ -2,7 +2,7 @@ import { isExamResultPublished } from 'app/exam/participate/exam.utils'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { MockArtemisServerDateService } from '../../../helpers/mocks/service/mock-server-date.service'; import { TestBed } from '@angular/core/testing'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; let artemisServerDateService: ArtemisServerDateService; diff --git a/src/test/javascript/spec/component/exam/participate/exercises/programming-exam-submission.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exercises/programming-exam-submission.component.spec.ts index bcd570d8c8a2..ad5c65c27275 100644 --- a/src/test/javascript/spec/component/exam/participate/exercises/programming-exam-submission.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exercises/programming-exam-submission.component.spec.ts @@ -4,8 +4,8 @@ import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingExamSubmissionComponent } from 'app/exam/participate/exercises/programming/programming-exam-submission.component'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; diff --git a/src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts index 6671fb12acf4..176692131b10 100644 --- a/src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts @@ -3,8 +3,8 @@ import { NgModel } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { TextExamSubmissionComponent } from 'app/exam/participate/exercises/text/text-exam-submission.component'; import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; import { TextEditorService } from 'app/exercises/text/participate/text-editor.service'; diff --git a/src/test/javascript/spec/component/exam/participate/general-information/exam-general-information.component.spec.ts b/src/test/javascript/spec/component/exam/participate/general-information/exam-general-information.component.spec.ts index 4be44e0709b1..9ead47365e2e 100644 --- a/src/test/javascript/spec/component/exam/participate/general-information/exam-general-information.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/general-information/exam-general-information.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExamGeneralInformationComponent } from 'app/exam/participate/general-information/exam-general-information.component'; import { StudentExamWorkingTimeComponent } from 'app/exam/shared/student-exam-working-time/student-exam-working-time.component'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/exam-result-summary.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/exam-result-summary.component.spec.ts index 815e36582b17..a36b5b62eb0e 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/exam-result-summary.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/exam-result-summary.component.spec.ts @@ -9,20 +9,20 @@ import { ThemeService } from 'app/core/theme/theme.service'; import { User } from 'app/core/user/user.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { GradeType } from 'app/entities/grading-scale.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; import { StudentExam } from 'app/entities/student-exam.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ExerciseResult, StudentExamWithGradeDTO, StudentResult } from 'app/exam/exam-scores/exam-score-dtos.model'; import { TestRunRibbonComponent } from 'app/exam/manage/test-runs/test-run-ribbon.component'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.spec.ts index 2fd0a02f46d8..b5a588f2f83f 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.spec.ts @@ -2,12 +2,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionType } from 'app/entities/submission.model'; import { TranslateDirective } from 'app/shared/language/translate.directive'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/exercises/programming-exam-summary.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/exercises/programming-exam-summary.component.spec.ts index deaa197ba1b6..6d8d7327aed3 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/exercises/programming-exam-summary.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/exercises/programming-exam-summary.component.spec.ts @@ -9,11 +9,11 @@ import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-st import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cache.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { User } from 'app/core/user/user.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/exercises/quiz-exam-summary.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/exercises/quiz-exam-summary.component.spec.ts index a6a54f521bfb..6dc458e8d548 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/exercises/quiz-exam-summary.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/exercises/quiz-exam-summary.component.spec.ts @@ -1,7 +1,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { AnswerOption } from 'app/entities/quiz/answer-option.model'; import { DragAndDropMapping } from 'app/entities/quiz/drag-and-drop-mapping.model'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/exercises/text-exam-summary.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/exercises/text-exam-summary.component.spec.ts index 7606478c4e1c..cf9a391476c0 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/exercises/text-exam-summary.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/exercises/text-exam-summary.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TextExamSummaryComponent } from 'app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Exercise } from 'app/entities/exercise.model'; import { TextEditorComponent } from 'app/exercises/text/participate/text-editor.component'; import { MockComponent } from 'ng-mocks'; diff --git a/src/test/javascript/spec/component/exam/participate/summary/result-overview/exam-result-overview.component.spec.ts b/src/test/javascript/spec/component/exam/participate/summary/result-overview/exam-result-overview.component.spec.ts index df597333fc63..836d997c7530 100644 --- a/src/test/javascript/spec/component/exam/participate/summary/result-overview/exam-result-overview.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/summary/result-overview/exam-result-overview.component.spec.ts @@ -4,14 +4,14 @@ import { RouterTestingModule } from '@angular/router/testing'; import { MockComponent, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { User } from 'app/core/user/user.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamResultOverviewComponent } from 'app/exam/participate/summary/result-overview/exam-result-overview.component'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Result } from 'app/entities/result.model'; diff --git a/src/test/javascript/spec/component/exam/shared/student-exam-working-time.component.spec.ts b/src/test/javascript/spec/component/exam/shared/student-exam-working-time.component.spec.ts index 1c71302e88cc..81da42c6eb6a 100644 --- a/src/test/javascript/spec/component/exam/shared/student-exam-working-time.component.spec.ts +++ b/src/test/javascript/spec/component/exam/shared/student-exam-working-time.component.spec.ts @@ -3,7 +3,7 @@ import { MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { StudentExamWorkingTimeComponent } from 'app/exam/shared/student-exam-working-time/student-exam-working-time.component'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; describe('StudentExamWorkingTimeComponent', () => { diff --git a/src/test/javascript/spec/component/exam/shared/testexam-working-time.component.spec.ts b/src/test/javascript/spec/component/exam/shared/testexam-working-time.component.spec.ts index 7a4817aa9936..b3ca685443bd 100644 --- a/src/test/javascript/spec/component/exam/shared/testexam-working-time.component.spec.ts +++ b/src/test/javascript/spec/component/exam/shared/testexam-working-time.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { TestexamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/testexam-working-time.component'; import { round } from 'app/shared/util/utils'; diff --git a/src/test/javascript/spec/component/exam/shared/working-time-control.component.spec.ts b/src/test/javascript/spec/component/exam/shared/working-time-control.component.spec.ts index 75382fcb9e4d..322846cc0bb2 100644 --- a/src/test/javascript/spec/component/exam/shared/working-time-control.component.spec.ts +++ b/src/test/javascript/spec/component/exam/shared/working-time-control.component.spec.ts @@ -2,7 +2,7 @@ import dayjs from 'dayjs/esm'; import { FormsModule } from '@angular/forms'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { WorkingTimeControlComponent } from 'app/exam/shared/working-time-control/working-time-control.component'; const createTestExam = (duration: number) => ({ workingTime: duration, startDate: dayjs.unix(0), endDate: dayjs.unix(duration) }) as Exam; diff --git a/src/test/javascript/spec/component/exam/test-run/create-test-run-modal.component.spec.ts b/src/test/javascript/spec/component/exam/test-run/create-test-run-modal.component.spec.ts index 23f0394ec3f3..4ca659c9076f 100644 --- a/src/test/javascript/spec/component/exam/test-run/create-test-run-modal.component.spec.ts +++ b/src/test/javascript/spec/component/exam/test-run/create-test-run-modal.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Course } from 'app/entities/course.model'; import { CreateTestRunModalComponent } from 'app/exam/manage/test-runs/create-test-run-modal.component'; import dayjs from 'dayjs/esm'; diff --git a/src/test/javascript/spec/component/exam/test-run/test-run-management.component.spec.ts b/src/test/javascript/spec/component/exam/test-run/test-run-management.component.spec.ts index 70e64fbff5d8..40d92651dcfa 100644 --- a/src/test/javascript/spec/component/exam/test-run/test-run-management.component.spec.ts +++ b/src/test/javascript/spec/component/exam/test-run/test-run-management.component.spec.ts @@ -9,7 +9,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { AccountService } from 'app/core/auth/account.service'; import { User } from 'app/core/user/user.model'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise } from 'app/entities/exercise.model'; import { StudentExam } from 'app/entities/student-exam.model'; diff --git a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts index 2af4e435afaf..4ef9a51082c3 100644 --- a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts +++ b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint-update.component.spec.ts @@ -13,8 +13,8 @@ import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { ActivatedRoute } from '@angular/router'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; diff --git a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts index 5d9ffdb54ca2..b7b531efa5a8 100644 --- a/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts +++ b/src/test/javascript/spec/component/exercise-hint/manage/exercise-hint.component.spec.ts @@ -9,7 +9,7 @@ import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; import { MockActivatedRoute } from '../../../helpers/mocks/activated-route/mock-activated-route'; import { EventManager } from 'app/core/util/event-manager.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; describe('ExerciseHint Management Component', () => { let comp: ExerciseHintComponent; diff --git a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts index 9bbadd2c6bb0..0dbdd2699087 100644 --- a/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts +++ b/src/test/javascript/spec/component/exercise-hint/participate/exercise-hint-expandable.component.spec.ts @@ -9,7 +9,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { ExerciseHintResponse, ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; import { ExerciseHintExpandableComponent } from 'app/exercises/shared/exercise-hint/participate/exercise-hint-expandable.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { StarRatingComponent } from 'app/exercises/shared/rating/star-rating/star-rating.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; diff --git a/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts b/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts index 745539ab66f3..d7852f272f21 100644 --- a/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts +++ b/src/test/javascript/spec/component/exercise-hint/shared/code-hint-container.component.spec.ts @@ -3,7 +3,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { CodeHintContainerComponent } from 'app/exercises/shared/exercise-hint/shared/code-hint-container.component'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; describe('ExerciseHint Management Component', () => { diff --git a/src/test/javascript/spec/component/exercises/problem-statement.component.spec.ts b/src/test/javascript/spec/component/exercises/problem-statement.component.spec.ts index dfe3302f9d69..8135e4878ae5 100644 --- a/src/test/javascript/spec/component/exercises/problem-statement.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/problem-statement.component.spec.ts @@ -6,8 +6,8 @@ import { ParticipationService } from 'app/exercises/shared/participation/partici import { HttpResponse } from '@angular/common/http'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProblemStatementComponent } from 'app/overview/exercise-details/problem-statement/problem-statement.component'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; diff --git a/src/test/javascript/spec/component/exercises/quiz/manage/quiz-pool.component.spec.ts b/src/test/javascript/spec/component/exercises/quiz/manage/quiz-pool.component.spec.ts index 598ed280ae94..c3c268ae9481 100644 --- a/src/test/javascript/spec/component/exercises/quiz/manage/quiz-pool.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/quiz/manage/quiz-pool.component.spec.ts @@ -15,7 +15,7 @@ import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-questi import { QuizGroup } from 'app/entities/quiz/quiz-group.model'; import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { QuizPoolMappingComponent } from 'app/exercises/quiz/manage/quiz-pool-mapping.component'; import { QuizQuestionListEditComponent } from 'app/exercises/quiz/manage/quiz-question-list-edit.component'; diff --git a/src/test/javascript/spec/component/exercises/quiz/manage/quiz-question-list-edit-existing.component.spec.ts b/src/test/javascript/spec/component/exercises/quiz/manage/quiz-question-list-edit-existing.component.spec.ts index e0c07d841d71..cac2387f7258 100644 --- a/src/test/javascript/spec/component/exercises/quiz/manage/quiz-question-list-edit-existing.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/quiz/manage/quiz-question-list-edit-existing.component.spec.ts @@ -10,7 +10,7 @@ import { QuizQuestionListEditExistingComponent, State } from 'app/exercises/quiz import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Course } from 'app/entities/course.model'; import { FormsModule } from '@angular/forms'; diff --git a/src/test/javascript/spec/component/exercises/shared/example-submission-import.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/example-submission-import.component.spec.ts index 8a82a0dd3925..4a7188fd906c 100644 --- a/src/test/javascript/spec/component/exercises/shared/example-submission-import.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/example-submission-import.component.spec.ts @@ -4,7 +4,7 @@ import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Submission, SubmissionType } from 'app/entities/submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ExampleSubmissionImportPagingService } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service'; import { ExampleSubmissionImportComponent } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import.component'; import { ExampleSubmissionService } from 'app/exercises/shared/example-submission/example-submission.service'; diff --git a/src/test/javascript/spec/component/exercises/shared/example-submissions.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/example-submissions.component.spec.ts index 0d292309f796..4b40d355f277 100644 --- a/src/test/javascript/spec/component/exercises/shared/example-submissions.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/example-submissions.component.spec.ts @@ -14,7 +14,7 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; import { ResultComponent } from 'app/exercises/shared/result/result.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { HttpResponse } from '@angular/common/http'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { AlertService } from 'app/core/util/alert.service'; describe('Example Submission Component', () => { diff --git a/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores-export-button.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores-export-button.component.spec.ts index 0faee4a0f4c9..4d6e2636cb3d 100644 --- a/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores-export-button.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores-export-button.component.spec.ts @@ -7,7 +7,7 @@ import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { Result } from 'app/entities/result.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; diff --git a/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores.component.spec.ts index c7bd261c1ed2..2bc2d727ee5e 100644 --- a/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/exercise-scores/exercise-scores.component.spec.ts @@ -11,7 +11,7 @@ import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Participation } from 'app/entities/participation/participation.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Result } from 'app/entities/result.model'; import { Submission } from 'app/entities/submission.model'; import { Team } from 'app/entities/team.model'; diff --git a/src/test/javascript/spec/component/exercises/shared/exercise-title-channel-name.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/exercise-title-channel-name.component.spec.ts index 23e6a4c7183c..37b91a62f115 100644 --- a/src/test/javascript/spec/component/exercises/shared/exercise-title-channel-name.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/exercise-title-channel-name.component.spec.ts @@ -2,7 +2,7 @@ import { SimpleChange } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NgForm } from '@angular/forms'; import { Course, CourseInformationSharingConfiguration } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; import { TitleChannelNameModule } from 'app/shared/form/title-channel-name/title-channel-name.module'; diff --git a/src/test/javascript/spec/component/exercises/shared/headers/difficulty-badge.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/headers/difficulty-badge.component.spec.ts index f1a7ce415e15..0b2b00eff87c 100644 --- a/src/test/javascript/spec/component/exercises/shared/headers/difficulty-badge.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/headers/difficulty-badge.component.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../../../../helpers/mocks/service/mock-translate.service'; import { DifficultyBadgeComponent } from 'app/exercises/shared/exercise-headers/difficulty-badge.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DifficultyLevel } from 'app/entities/exercise.model'; describe('DifficultyBadge', () => { diff --git a/src/test/javascript/spec/component/exercises/shared/headers/header-exercise-page-with-details.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/headers/header-exercise-page-with-details.component.spec.ts index 6d38f089db69..433069739443 100644 --- a/src/test/javascript/spec/component/exercises/shared/headers/header-exercise-page-with-details.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/headers/header-exercise-page-with-details.component.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { MockComponent, MockPipe } from 'ng-mocks'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { HeaderExercisePageWithDetailsComponent } from 'app/exercises/shared/exercise-headers/header-exercise-page-with-details.component'; import { NotReleasedTagComponent } from 'app/shared/components/not-released-tag.component'; import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @@ -11,10 +11,10 @@ import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-hea import { ExerciseTypePipe } from 'app/shared/pipes/exercise-type.pipe'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionType } from 'app/entities/submission.model'; import { Result } from 'app/entities/result.model'; import { LockRepositoryPolicy } from 'app/entities/submission-policy.model'; @@ -67,8 +67,7 @@ describe('HeaderExercisePageWithDetails', () => { expect(component.icon.iconName).toBe('keyboard'); // dueDate, categories, examMode should also be set if the necessary information is known - const category = new ExerciseCategory(); - category.category = 'testcategory'; + const category = new ExerciseCategory('testcategory', undefined); const categories = [category]; exercise.categories = categories; exam.endDate = dayjs().subtract(1, 'day'); diff --git a/src/test/javascript/spec/component/exercises/shared/headers/header-participation-page.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/headers/header-participation-page.component.spec.ts index 11e02319eee8..b8e5b4266769 100644 --- a/src/test/javascript/spec/component/exercises/shared/headers/header-participation-page.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/headers/header-participation-page.component.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { MockComponent, MockPipe } from 'ng-mocks'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { DifficultyBadgeComponent } from 'app/exercises/shared/exercise-headers/difficulty-badge.component'; @@ -8,7 +8,7 @@ import { ArtemisTestModule } from '../../../../test.module'; import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { HeaderParticipationPageComponent } from 'app/exercises/shared/exercise-headers/header-participation-page.component'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; diff --git a/src/test/javascript/spec/component/exercises/shared/result.spec.ts b/src/test/javascript/spec/component/exercises/shared/result.spec.ts index 3314be02465d..782145a2aa63 100644 --- a/src/test/javascript/spec/component/exercises/shared/result.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/result.spec.ts @@ -13,7 +13,7 @@ import { cloneDeep } from 'lodash-es'; import { Submission } from 'app/entities/submission.model'; import { ExerciseType } from 'app/entities/exercise.model'; import { faQuestionCircle, faTimesCircle } from '@fortawesome/free-regular-svg-icons'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/test/javascript/spec/component/exercises/shared/team-config-form-group.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/team-config-form-group.component.spec.ts index 91d124000a49..558987481795 100644 --- a/src/test/javascript/spec/component/exercises/shared/team-config-form-group.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/team-config-form-group.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Exercise, ExerciseMode } from 'app/entities/exercise.model'; import { TeamAssignmentConfig } from 'app/entities/team-assignment-config.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; import { Subject } from 'rxjs'; diff --git a/src/test/javascript/spec/component/exercises/shared/team-submission-sync.component.spec.ts b/src/test/javascript/spec/component/exercises/shared/team-submission-sync.component.spec.ts index 9fb883b25037..6bab9646a59b 100644 --- a/src/test/javascript/spec/component/exercises/shared/team-submission-sync.component.spec.ts +++ b/src/test/javascript/spec/component/exercises/shared/team-submission-sync.component.spec.ts @@ -14,7 +14,7 @@ import { MockHttpService } from '../../../helpers/mocks/service/mock-http.servic import { HttpClient } from '@angular/common/http'; import { Submission } from 'app/entities/submission.model'; import { Observable, Subject, of } from 'rxjs'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { SubmissionSyncPayload } from 'app/entities/submission-sync-payload.model'; import { User } from 'app/core/user/user.model'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/test/javascript/spec/component/file-upload-exercise/file-upload-exercise-update.component.spec.ts b/src/test/javascript/spec/component/file-upload-exercise/file-upload-exercise-update.component.spec.ts index d104f657bdf3..3579287a3913 100644 --- a/src/test/javascript/spec/component/file-upload-exercise/file-upload-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/file-upload-exercise/file-upload-exercise-update.component.spec.ts @@ -17,12 +17,13 @@ import { MockProvider } from 'ng-mocks'; import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import dayjs from 'dayjs/esm'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { fileUploadExercise } from '../../helpers/mocks/service/mock-file-upload-exercise.service'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; import { NgModel } from '@angular/forms'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; describe('FileUploadExerciseUpdateComponent', () => { let comp: FileUploadExerciseUpdateComponent; @@ -321,7 +322,7 @@ describe('FileUploadExerciseUpdateComponent', () => { it('should updateCategories properly by making category available for selection again when removing it', () => { comp.fileUploadExercise = fileUploadExercise; comp.exerciseCategories = []; - const newCategories = [{ category: 'Easy' }, { category: 'Hard' }]; + const newCategories = [new ExerciseCategory('Easy', undefined), new ExerciseCategory('Hard', undefined)]; comp.updateCategories(newCategories); diff --git a/src/test/javascript/spec/component/grading-system/detailed-grading-system.component.spec.ts b/src/test/javascript/spec/component/grading-system/detailed-grading-system.component.spec.ts index 4e3b44045052..7ecabd855b07 100644 --- a/src/test/javascript/spec/component/grading-system/detailed-grading-system.component.spec.ts +++ b/src/test/javascript/spec/component/grading-system/detailed-grading-system.component.spec.ts @@ -14,7 +14,7 @@ import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Course } from 'app/entities/course.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; diff --git a/src/test/javascript/spec/component/grading-system/interval-grading-system.component.spec.ts b/src/test/javascript/spec/component/grading-system/interval-grading-system.component.spec.ts index 70f795c2051c..531b3f1f2064 100644 --- a/src/test/javascript/spec/component/grading-system/interval-grading-system.component.spec.ts +++ b/src/test/javascript/spec/component/grading-system/interval-grading-system.component.spec.ts @@ -11,7 +11,7 @@ import { GradeStep } from 'app/entities/grade-step.model'; import { cloneDeep } from 'lodash-es'; import { of } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Course } from 'app/entities/course.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts index d34c0758200a..360a08238306 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-overview.component.spec.ts @@ -1,6 +1,6 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CodeHint, CodeHintGenerationStep } from 'app/entities/hestia/code-hint-model'; import { CodeHintGenerationOverviewComponent } from 'app/exercises/programming/hestia/generation-overview/code-hint-generation-overview/code-hint-generation-overview.component'; import { MockActivatedRoute } from '../../../helpers/mocks/activated-route/mock-activated-route'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts index 1581002cfad0..f0e9e9999233 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/code-hint-generation-step.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { CodeHintGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/code-hint-generation-step/code-hint-generation-step.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CodeHintService } from 'app/exercises/shared/exercise-hint/services/code-hint.service'; import { CodeHint } from 'app/entities/hestia/code-hint-model'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts index 2707ed4960a3..cda7ed0c1f60 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/coverage-generation-step.component.spec.ts @@ -2,7 +2,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { CoverageGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/coverage-generation-step/coverage-generation-step.component'; import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts index 72b487652668..6d9f4cca6346 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/diff-generation-step.component.spec.ts @@ -2,7 +2,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DiffGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/diff-generation-step/diff-generation-step.component'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts index 34c79f31899d..9720294dde47 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/manual-solution-entry-creation-modal.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ManualSolutionEntryCreationModalComponent } from 'app/exercises/programming/hestia/generation-overview/manual-solution-entry-creation-modal/manual-solution-entry-creation-modal.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { of } from 'rxjs'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts index 603c7838f5cd..7852a6aa8f5f 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-details-modal.component.spec.ts @@ -1,7 +1,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProgrammingExerciseSolutionEntryService } from 'app/exercises/shared/exercise-hint/services/programming-exercise-solution-entry.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { of } from 'rxjs'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; diff --git a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts index f231e94f008c..9e4ff7118bc9 100644 --- a/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/generation-overview/solution-entry-generation-step.component.spec.ts @@ -2,7 +2,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Observable, of } from 'rxjs'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SolutionEntryGenerationStepComponent } from 'app/exercises/programming/hestia/generation-overview/steps/solution-entry-generation-step/solution-entry-generation-step.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; @@ -11,7 +11,7 @@ import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programmin import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; import { EventEmitter } from '@angular/core'; -import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType } from 'app/entities/programming/programming-exercise-test-case.model'; import { SortingOrder } from 'app/shared/table/pageable-table'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; diff --git a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts index d837ef16643a..a35ac8f46b3b 100644 --- a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-file.component.spec.ts @@ -2,7 +2,7 @@ import { ArtemisTestModule } from '../../../test.module'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { TestwiseCoverageFileComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-file.component'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { TestwiseCoverageReportEntry } from 'app/entities/hestia/testwise-coverage-report-entry.model'; import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; import { MatExpansionModule } from '@angular/material/expansion'; diff --git a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts index 77f8c1db5cbd..1c2e97853b5b 100644 --- a/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts +++ b/src/test/javascript/spec/component/hestia/testwise-coverage-report/testwise-coverage-report.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TestwiseCoverageReportComponent } from 'app/exercises/programming/hestia/testwise-coverage-report/testwise-coverage-report.component'; import { CoverageReport } from 'app/entities/hestia/coverage-report.model'; import { CoverageFileReport } from 'app/entities/hestia/coverage-file-report.model'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { TestwiseCoverageReportEntry } from 'app/entities/hestia/testwise-coverage-report-entry.model'; describe('TestwiseCoverageReport Component', () => { diff --git a/src/test/javascript/spec/component/import/exercise-import-from-file.component.spec.ts b/src/test/javascript/spec/component/import/exercise-import-from-file.component.spec.ts index 1e6d212a1c9a..d855c5af0aa1 100644 --- a/src/test/javascript/spec/component/import/exercise-import-from-file.component.spec.ts +++ b/src/test/javascript/spec/component/import/exercise-import-from-file.component.spec.ts @@ -6,14 +6,14 @@ import { ArtemisTestModule } from '../../test.module'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { MockComponent, MockDirective } from 'ng-mocks'; import { ButtonComponent } from 'app/shared/components/button.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseType } from 'app/entities/exercise.model'; import { AlertService } from 'app/core/util/alert.service'; import { TranslateDirective } from 'app/shared/language/translate.directive'; import JSZip from 'jszip'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { UMLDiagramType } from '@ls1intum/apollon'; diff --git a/src/test/javascript/spec/component/import/exercise-import-wrapper.component.spec.ts b/src/test/javascript/spec/component/import/exercise-import-wrapper.component.spec.ts index fb60d4ab979c..eeb648c6126e 100644 --- a/src/test/javascript/spec/component/import/exercise-import-wrapper.component.spec.ts +++ b/src/test/javascript/spec/component/import/exercise-import-wrapper.component.spec.ts @@ -9,7 +9,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateDirective } from 'app/shared/language/translate.directive'; import { ArtemisTestModule } from '../../test.module'; import { ExerciseImportTabsComponent } from 'app/exercises/shared/import/exercise-import-tabs.component'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; describe('ExerciseImportWrapperComponent', () => { let component: ExerciseImportWrapperComponent; diff --git a/src/test/javascript/spec/component/import/exercise-import.component.spec.ts b/src/test/javascript/spec/component/import/exercise-import.component.spec.ts index cf1a0da9a91f..ae6201f3dea5 100644 --- a/src/test/javascript/spec/component/import/exercise-import.component.spec.ts +++ b/src/test/javascript/spec/component/import/exercise-import.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin import { FormsModule } from '@angular/forms'; import { NgbActiveModal, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ModelingExercisePagingService } from 'app/exercises/modeling/manage/modeling-exercise-paging.service'; import { CodeAnalysisPagingService } from 'app/exercises/programming/manage/services/code-analysis-paging.service'; import { ProgrammingExercisePagingService } from 'app/exercises/programming/manage/services/programming-exercise-paging.service'; diff --git a/src/test/javascript/spec/component/import/import.component.spec.ts b/src/test/javascript/spec/component/import/import.component.spec.ts index 637cfb5c95de..88919c3b0a58 100644 --- a/src/test/javascript/spec/component/import/import.component.spec.ts +++ b/src/test/javascript/spec/component/import/import.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { NgbActiveModal, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ButtonComponent } from 'app/shared/components/button.component'; import { ImportComponent } from 'app/shared/import/import.component'; import { BaseEntity } from 'app/shared/model/base-entity'; diff --git a/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts b/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts index a79259f2268a..ba02154c4bc5 100644 --- a/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts +++ b/src/test/javascript/spec/component/iris/settings/iris-enabled.component.spec.ts @@ -9,7 +9,7 @@ import { IrisSettings } from 'app/entities/iris/settings/iris-settings.model'; import { HttpResponse } from '@angular/common/http'; import { IrisEnabledComponent } from 'app/iris/settings/shared/iris-enabled.component'; import { TranslatePipeMock } from '../../../helpers/mocks/service/mock-translate.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model'; diff --git a/src/test/javascript/spec/component/learning-paths/components/learning-path-lecture-unit.component.spec.ts b/src/test/javascript/spec/component/learning-paths/components/learning-path-lecture-unit.component.spec.ts index e82d4195a3f6..a77d15c0709f 100644 --- a/src/test/javascript/spec/component/learning-paths/components/learning-path-lecture-unit.component.spec.ts +++ b/src/test/javascript/spec/component/learning-paths/components/learning-path-lecture-unit.component.spec.ts @@ -86,7 +86,7 @@ describe('LearningPathLectureUnitComponent', () => { }); it('should get lecture unit', async () => { - const getLectureUnitSpy = jest.spyOn(component, 'getLectureUnit'); + const getLectureUnitSpy = jest.spyOn(component, 'loadLectureUnit'); fixture.detectChanges(); await fixture.whenStable(); @@ -102,7 +102,7 @@ describe('LearningPathLectureUnitComponent', () => { }); it('should set loading state correctly', async () => { - const setIsLoadingSpy = jest.spyOn(component.isLectureUnitLoading, 'set'); + const setIsLoadingSpy = jest.spyOn(component.isLoading, 'set'); fixture.detectChanges(); await fixture.whenStable(); fixture.detectChanges(); diff --git a/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts index 7469f93f790d..bf1140cc06fb 100644 --- a/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts +++ b/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts @@ -8,7 +8,7 @@ import { NgbTooltipMocksModule } from '../../../../helpers/mocks/directive/ngbTo import { ExerciseNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { Exercise } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; describe('ExerciseNodeDetailsComponent', () => { let fixture: ComponentFixture; diff --git a/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts b/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts index 3c0cc20079f0..9ead2c3a9994 100644 --- a/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts +++ b/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts @@ -13,7 +13,7 @@ import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { Exercise } from 'app/entities/exercise.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { AttachmentUnit } from 'app/entities/lecture-unit/attachmentUnit.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { LearningPathLectureUnitViewComponent } from 'app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component'; import { CourseExerciseDetailsComponent } from 'app/overview/exercise-details/course-exercise-details.component'; import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry, StorageEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; diff --git a/src/test/javascript/spec/component/lecture-unit/exercise-unit/create-exercise-unit.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/exercise-unit/create-exercise-unit.component.spec.ts index 4841051e7ad9..c01d76662132 100644 --- a/src/test/javascript/spec/component/lecture-unit/exercise-unit/create-exercise-unit.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/exercise-unit/create-exercise-unit.component.spec.ts @@ -10,8 +10,8 @@ import { ExerciseUnitService } from 'app/lecture/lecture-unit/lecture-unit-manag import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; diff --git a/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.component.spec.ts index d44b64a65ff0..5d72248e268d 100644 --- a/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.component.spec.ts @@ -4,7 +4,7 @@ import { ExerciseUnit } from 'app/entities/lecture-unit/exerciseUnit.model'; import { Component, Input } from '@angular/core'; import { Exercise } from 'app/entities/exercise.model'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { By } from '@angular/platform-browser'; @Component({ selector: 'jhi-course-exercise-row', template: '' }) diff --git a/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.service.spec.ts b/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.service.spec.ts index 486196aa88dd..61d60ac7d907 100644 --- a/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.service.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/exercise-unit/exercise-unit.service.spec.ts @@ -7,7 +7,7 @@ import { take } from 'rxjs/operators'; import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { ExerciseUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/exerciseUnit.service'; import { ExerciseUnit } from 'app/entities/lecture-unit/exerciseUnit.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Course } from 'app/entities/course.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; diff --git a/src/test/javascript/spec/component/lecture-unit/lecture-unit.service.spec.ts b/src/test/javascript/spec/component/lecture-unit/lecture-unit.service.spec.ts index c068f467ac38..9195af36b64b 100644 --- a/src/test/javascript/spec/component/lecture-unit/lecture-unit.service.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/lecture-unit.service.spec.ts @@ -10,7 +10,7 @@ import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { VideoUnit } from 'app/entities/lecture-unit/videoUnit.model'; import { ExerciseUnit } from 'app/entities/lecture-unit/exerciseUnit.model'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Attachment, AttachmentType } from 'app/entities/attachment.model'; import { ArtemisTestModule } from '../../test.module'; diff --git a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-competencies.component.spec.ts b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-competencies.component.spec.ts index 2882280317c4..914fe55c18ab 100644 --- a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-competencies.component.spec.ts +++ b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-competencies.component.spec.ts @@ -16,7 +16,7 @@ import { Course } from 'app/entities/course.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { ExerciseUnit } from 'app/entities/lecture-unit/exerciseUnit.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts index 9b458fcfea34..41ab0dfcea81 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agent-details.component.spec.ts @@ -2,17 +2,17 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { BuildAgentsService } from 'app/localci/build-agents/build-agents.service'; import { of } from 'rxjs'; -import { BuildJob } from 'app/entities/build-job.model'; +import { BuildJob } from 'app/entities/programming/build-job.model'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../../test.module'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { DataTableComponent } from 'app/shared/data-table/data-table.component'; import { MockComponent, MockPipe } from 'ng-mocks'; import { NgxDatatableModule } from '@flaviosantoro92/ngx-datatable'; -import { BuildAgent } from 'app/entities/build-agent.model'; -import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; +import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { JobTimingInfo } from 'app/entities/job-timing-info.model'; -import { BuildConfig } from 'app/entities/build-config.model'; +import { BuildConfig } from 'app/entities/programming/build-config.model'; import { BuildAgentDetailsComponent } from 'app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component'; import { MockActivatedRoute } from '../../../helpers/mocks/activated-route/mock-activated-route'; import { ActivatedRoute } from '@angular/router'; diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts index 2ffc69f9b823..76a76f16c094 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agent-summary.component.spec.ts @@ -3,17 +3,17 @@ import { BuildAgentSummaryComponent } from 'app/localci/build-agents/build-agent import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { BuildAgentsService } from 'app/localci/build-agents/build-agents.service'; import { of } from 'rxjs'; -import { BuildJob } from 'app/entities/build-job.model'; +import { BuildJob } from 'app/entities/programming/build-job.model'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../../test.module'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { DataTableComponent } from 'app/shared/data-table/data-table.component'; import { MockComponent, MockPipe } from 'ng-mocks'; import { NgxDatatableModule } from '@flaviosantoro92/ngx-datatable'; -import { BuildAgent } from 'app/entities/build-agent.model'; -import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; +import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { JobTimingInfo } from 'app/entities/job-timing-info.model'; -import { BuildConfig } from 'app/entities/build-config.model'; +import { BuildConfig } from 'app/entities/programming/build-config.model'; describe('BuildAgentSummaryComponent', () => { let component: BuildAgentSummaryComponent; diff --git a/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts b/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts index 20ac1e1e4011..494f211ee2b3 100644 --- a/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts +++ b/src/test/javascript/spec/component/localci/build-agents/build-agents.service.spec.ts @@ -3,13 +3,13 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; -import { BuildJob } from 'app/entities/build-job.model'; +import { BuildJob } from 'app/entities/programming/build-job.model'; import dayjs from 'dayjs/esm'; import { BuildAgentsService } from 'app/localci/build-agents/build-agents.service'; -import { BuildAgent } from 'app/entities/build-agent.model'; -import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { BuildAgent } from 'app/entities/programming/build-agent.model'; +import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { JobTimingInfo } from 'app/entities/job-timing-info.model'; -import { BuildConfig } from 'app/entities/build-config.model'; +import { BuildConfig } from 'app/entities/programming/build-config.model'; describe('BuildAgentsService', () => { let service: BuildAgentsService; diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts index 966e1cdeb551..2d8f1c622405 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.component.spec.ts @@ -10,8 +10,8 @@ import { AccountService } from 'app/core/auth/account.service'; import { DataTableComponent } from 'app/shared/data-table/data-table.component'; import { NgxDatatableModule } from '@flaviosantoro92/ngx-datatable'; import { ArtemisTestModule } from '../../../test.module'; -import { BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/build-job.model'; -import { TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { BuildJobStatistics, FinishedBuildJob, SpanType } from 'app/entities/programming/build-job.model'; +import { TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { waitForAsync } from '@angular/core/testing'; import { HttpResponse } from '@angular/common/http'; import { SortingOrder } from 'app/shared/table/pageable-table'; diff --git a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts index aa1b2ad95943..e762ed0d878b 100644 --- a/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts +++ b/src/test/javascript/spec/component/localci/build-queue/build-queue.service.spec.ts @@ -8,11 +8,11 @@ import { MockSyncStorage } from '../../../helpers/mocks/service/mock-sync-storag import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { TranslateService } from '@ngx-translate/core'; -import { BuildJob, BuildJobStatistics, SpanType } from 'app/entities/build-job.model'; +import { BuildJob, BuildJobStatistics, SpanType } from 'app/entities/programming/build-job.model'; import dayjs from 'dayjs/esm'; -import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/repository-info.model'; +import { RepositoryInfo, TriggeredByPushTo } from 'app/entities/programming/repository-info.model'; import { JobTimingInfo } from 'app/entities/job-timing-info.model'; -import { BuildConfig } from 'app/entities/build-config.model'; +import { BuildConfig } from 'app/entities/programming/build-config.model'; import { FinishedBuildJobFilter } from 'app/localci/build-queue/build-queue.component'; describe('BuildQueueService', () => { diff --git a/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts b/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts index 3fb6e53ee8f7..fb964f7ea127 100644 --- a/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts +++ b/src/test/javascript/spec/component/localvc/commit-details-view.component.spec.ts @@ -7,7 +7,7 @@ import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import dayjs from 'dayjs/esm'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { Observable, of, throwError } from 'rxjs'; -import { CommitInfo } from 'app/entities/programming-submission.model'; +import { CommitInfo } from 'app/entities/programming/programming-submission.model'; import { MockComponent, MockPipe } from 'ng-mocks'; import { CommitDetailsViewComponent } from 'app/localvc/commit-details-view/commit-details-view.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; @@ -16,7 +16,7 @@ import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programmin import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { GitDiffReportComponent } from 'app/exercises/programming/hestia/git-diff-report/git-diff-report.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { HttpResponse } from '@angular/common/http'; describe('CommitDetailsViewComponent', () => { diff --git a/src/test/javascript/spec/component/localvc/commit-history.component.spec.ts b/src/test/javascript/spec/component/localvc/commit-history.component.spec.ts index 1ab3572e75f4..8c3a628980a6 100644 --- a/src/test/javascript/spec/component/localvc/commit-history.component.spec.ts +++ b/src/test/javascript/spec/component/localvc/commit-history.component.spec.ts @@ -8,12 +8,12 @@ import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import dayjs from 'dayjs/esm'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { of } from 'rxjs'; -import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { CommitsInfoComponent } from 'app/exercises/programming/shared/commits-info/commits-info.component'; import { MockComponent } from 'ng-mocks'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { MockProgrammingExerciseService } from '../../helpers/mocks/service/mock-programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { HttpResponse } from '@angular/common/http'; describe('CommitHistoryComponent', () => { diff --git a/src/test/javascript/spec/component/localvc/repository-view.component.spec.ts b/src/test/javascript/spec/component/localvc/repository-view.component.spec.ts index fcc2710140c4..5f4fae4014a9 100644 --- a/src/test/javascript/spec/component/localvc/repository-view.component.spec.ts +++ b/src/test/javascript/spec/component/localvc/repository-view.component.spec.ts @@ -13,7 +13,7 @@ import { AccountService } from 'app/core/auth/account.service'; import { DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { Observable, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; diff --git a/src/test/javascript/spec/component/modeling-assessment-editor/modeling-assessment-editor.component.spec.ts b/src/test/javascript/spec/component/modeling-assessment-editor/modeling-assessment-editor.component.spec.ts index 2bdc08914731..7bbd4b2e71a7 100644 --- a/src/test/javascript/spec/component/modeling-assessment-editor/modeling-assessment-editor.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-assessment-editor/modeling-assessment-editor.component.spec.ts @@ -12,7 +12,7 @@ import { AssessmentType } from 'app/entities/assessment-type.model'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; import { Complaint } from 'app/entities/complaint.model'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise } from 'app/entities/exercise.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; @@ -20,7 +20,7 @@ import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Result } from 'app/entities/result.model'; import { getLatestSubmissionResult } from 'app/entities/submission.model'; import { ModelingAssessmentEditorComponent } from 'app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component'; diff --git a/src/test/javascript/spec/component/modeling-exercise/modeling-exercise-update.component.spec.ts b/src/test/javascript/spec/component/modeling-exercise/modeling-exercise-update.component.spec.ts index b05efdeb91e5..ccbc08667bd0 100644 --- a/src/test/javascript/spec/component/modeling-exercise/modeling-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-exercise/modeling-exercise-update.component.spec.ts @@ -12,7 +12,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockActivatedRoute } from '../../helpers/mocks/activated-route/mock-activated-route'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { TranslateService } from '@ngx-translate/core'; import { MockComponent, MockProvider } from 'ng-mocks'; @@ -27,6 +27,7 @@ import { ExerciseUpdatePlagiarismComponent } from 'app/exercises/shared/plagiari import { NgModel } from '@angular/forms'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; import { UMLDiagramType } from '@ls1intum/apollon'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; describe('ModelingExerciseUpdateComponent', () => { let comp: ModelingExerciseUpdateComponent; @@ -34,7 +35,8 @@ describe('ModelingExerciseUpdateComponent', () => { let service: ModelingExerciseService; let courseService: CourseManagementService; let exerciseService: ExerciseService; - const categories = [{ category: 'testCat' }, { category: 'testCat2' }]; + const categories = [new ExerciseCategory('testCat', undefined), new ExerciseCategory('testCat2', undefined)]; + const categoriesStringified = categories.map((cat) => JSON.stringify(cat)); beforeEach(() => { @@ -262,7 +264,8 @@ describe('ModelingExerciseUpdateComponent', () => { const modelingExercise = new ModelingExercise(UMLDiagramType.ClassDiagram, undefined, undefined); modelingExercise.categories = categories; comp.modelingExercise = modelingExercise; - const newCategories = [{ category: 'newCat1' }, { category: 'newCat2' }]; + const newCategories = [new ExerciseCategory('newCat1', undefined), new ExerciseCategory('newCat2', undefined)]; + comp.updateCategories(newCategories); expect(comp.modelingExercise.categories).toEqual(newCategories); }); @@ -285,7 +288,7 @@ describe('ModelingExerciseUpdateComponent', () => { it('should updateCategories properly by making category available for selection again when removing it', () => { comp.modelingExercise = new ModelingExercise(UMLDiagramType.ClassDiagram, undefined, undefined); comp.exerciseCategories = []; - const newCategories = [{ category: 'Easy' }, { category: 'Hard' }]; + const newCategories = [new ExerciseCategory('Easy', undefined), new ExerciseCategory('Hard', undefined)]; comp.updateCategories(newCategories); diff --git a/src/test/javascript/spec/component/non-programming-exercise-detail-common-actions.component.spec.ts b/src/test/javascript/spec/component/non-programming-exercise-detail-common-actions.component.spec.ts index a01c3b4bdbd5..e81ad3cbd435 100644 --- a/src/test/javascript/spec/component/non-programming-exercise-detail-common-actions.component.spec.ts +++ b/src/test/javascript/spec/component/non-programming-exercise-detail-common-actions.component.spec.ts @@ -6,11 +6,11 @@ import { MockFileUploadExerciseService } from '../helpers/mocks/service/mock-fil import { SubmissionExportButtonComponent } from 'app/exercises/shared/submission-export/submission-export-button.component'; import { MockComponent, MockDirective, MockProvider } from 'ng-mocks'; import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Course } from 'app/entities/course.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; import { of } from 'rxjs'; diff --git a/src/test/javascript/spec/component/orion/orion-code-editor-instructor-and-editor-container.component.spec.ts b/src/test/javascript/spec/component/orion/orion-code-editor-instructor-and-editor-container.component.spec.ts index 5350df402a29..fc43be446641 100644 --- a/src/test/javascript/spec/component/orion/orion-code-editor-instructor-and-editor-container.component.spec.ts +++ b/src/test/javascript/spec/component/orion/orion-code-editor-instructor-and-editor-container.component.spec.ts @@ -19,7 +19,7 @@ import { MockRouter } from '../../helpers/mocks/mock-router'; import { Router } from '@angular/router'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; describe('CodeEditorInstructorAndEditorOrionContainerComponent', () => { let comp: CodeEditorInstructorAndEditorOrionContainerComponent; diff --git a/src/test/javascript/spec/component/orion/orion-exercise-assessment-dashboard.component.spec.ts b/src/test/javascript/spec/component/orion/orion-exercise-assessment-dashboard.component.spec.ts index d5688eb628cf..430f28bee09f 100644 --- a/src/test/javascript/spec/component/orion/orion-exercise-assessment-dashboard.component.spec.ts +++ b/src/test/javascript/spec/component/orion/orion-exercise-assessment-dashboard.component.spec.ts @@ -2,7 +2,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { OrionExerciseAssessmentDashboardComponent } from 'app/orion/assessment/orion-exercise-assessment-dashboard.component'; import { ExerciseType } from 'app/entities/exercise.model'; import { TutorParticipationStatus } from 'app/entities/participation/tutor-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { BehaviorSubject, of, throwError } from 'rxjs'; import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; diff --git a/src/test/javascript/spec/component/overview/course-card.component.spec.ts b/src/test/javascript/spec/component/overview/course-card.component.spec.ts index 9c22d764106f..fbe16f160c51 100644 --- a/src/test/javascript/spec/component/overview/course-card.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-card.component.spec.ts @@ -9,7 +9,7 @@ import { Exercise } from 'app/entities/exercise.model'; import { MockComponent, MockDirective, MockModule, MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { SubmissionExerciseType } from 'app/entities/submission.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; import { PieChartModule } from '@swimlane/ngx-charts'; import { TranslateDirective } from 'app/shared/language/translate.directive'; diff --git a/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts b/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts index 384e0875b8cd..658cc5b4f7ae 100644 --- a/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts @@ -20,7 +20,7 @@ import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { MockRouter } from '../../../helpers/mocks/mock-router'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Competency, CompetencyProgress } from 'app/entities/competency.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { HttpResponse } from '@angular/common/http'; import { MockHasAnyAuthorityDirective } from '../../../helpers/mocks/directive/mock-has-any-authority.directive'; diff --git a/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts b/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts index cabb62f2ae39..60590bae4bde 100644 --- a/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts @@ -2,23 +2,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { CourseExamsComponent } from 'app/overview/course-exams/course-exams.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ArtemisTestModule } from '../../../test.module'; import dayjs from 'dayjs/esm'; -import { MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { Observable, of } from 'rxjs'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { StudentExam } from 'app/entities/student-exam.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; -import { SidebarComponent } from '../../../../../../main/webapp/app/shared/sidebar/sidebar.component'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { RouterTestingModule } from '@angular/router/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MockRouter } from '../../../helpers/mocks/mock-router'; import { CourseOverviewService } from 'app/overview/course-overview.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; describe('CourseExamsComponent', () => { let component: CourseExamsComponent; @@ -100,7 +101,7 @@ describe('CourseExamsComponent', () => { router.navigate.mockImplementation(() => Promise.resolve(true)); TestBed.configureTestingModule({ - imports: [ArtemisTestModule, RouterTestingModule, MockModule(FormsModule), MockModule(ReactiveFormsModule)], + imports: [ArtemisTestModule, RouterTestingModule, MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(TranslateDirective)], declarations: [CourseExamsComponent, SidebarComponent, SearchFilterComponent, MockPipe(ArtemisTranslatePipe), MockPipe(SearchFilterPipe)], providers: [ { provide: Router, useValue: router }, diff --git a/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts b/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts index 7631c4d81136..b38eefa60f4e 100644 --- a/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts @@ -14,7 +14,7 @@ import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { Exercise, ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { TreeviewModule } from 'app/exercises/programming/shared/code-editor/treeview/treeview.module'; import { CourseCompetenciesComponent } from 'app/overview/course-competencies/course-competencies.component'; diff --git a/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts b/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts index 957b1b6738ce..5fc8684a61cd 100644 --- a/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts +++ b/src/test/javascript/spec/component/overview/exercise-details/course-exercise-details.component.spec.ts @@ -9,7 +9,7 @@ import { Participation, ParticipationType } from 'app/entities/participation/par import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Result } from 'app/entities/result.model'; import { TeamAssignmentPayload } from 'app/entities/team.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { ProgrammingSubmissionService } from 'app/exercises/programming/participate/programming-submission.service'; import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; @@ -49,11 +49,11 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { MockRouterLinkDirective } from '../../../helpers/mocks/directive/mock-router-link.directive'; import { LtiInitializerComponent } from 'app/overview/exercise-details/lti-initializer.component'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { MockCourseManagementService } from '../../../helpers/mocks/service/mock-course-management.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { DiscussionSectionComponent } from 'app/overview/discussion-section/discussion-section.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SubmissionPolicyService } from 'app/exercises/programming/manage/services/submission-policy.service'; import { LockRepositoryPolicy } from 'app/entities/submission-policy.model'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; diff --git a/src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts b/src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts index 9a0a2327c015..99068b63a154 100644 --- a/src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts +++ b/src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts @@ -9,10 +9,10 @@ import { Exercise, ExerciseMode, ExerciseType } from 'app/entities/exercise.mode import { InitializationState } from 'app/entities/participation/participation.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizBatch, QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { Team } from 'app/entities/team.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; import { ExerciseDetailsStudentActionsComponent } from 'app/overview/exercise-details/exercise-details-student-actions.component'; import { CodeButtonComponent } from 'app/shared/components/code-button/code-button.component'; @@ -55,6 +55,7 @@ describe('ExerciseDetailsStudentActionsComponent', () => { secondCorrectionEnabled: false, studentAssignedTeamIdComputed: false, }; + const teamExerciseWithoutTeamAssigned: Exercise = { ...exercise, mode: ExerciseMode.TEAM, @@ -607,18 +608,58 @@ describe('ExerciseDetailsStudentActionsComponent', () => { it.each([ [ - 'start theia button should be visible when profile is active and url is set', + 'start theia button should be visible when profile is active and theia is configured', { activeProfiles: [PROFILE_THEIA], theiaPortalURL: 'https://theia.test', }, + { + allowOnlineIde: true, + }, + { + theiaImage: 'this-is-a-theia-image', + }, true, ], + [ + 'start theia button should not be visible when profile is active but theia is ill-configured', + { + activeProfiles: [PROFILE_THEIA], + theiaPortalURL: 'https://theia.test', + }, + { + allowOnlineIde: true, + }, + { + theiaImage: undefined, + }, + false, + ], + [ + 'start theia button should not be visible when profile is active but onlineIde is not activated', + { + activeProfiles: [PROFILE_THEIA], + theiaPortalURL: 'https://theia.test', + }, + { + allowOnlineIde: false, + }, + { + theiaImage: 'this-is-an-old-image', + }, + false, + ], [ 'start theia button should not be visible when profile is active but url is not set', { activeProfiles: [PROFILE_THEIA], }, + { + allowOnlineIde: true, + }, + { + theiaImage: 'this-is-a-theia-image', + }, false, ], [ @@ -626,12 +667,20 @@ describe('ExerciseDetailsStudentActionsComponent', () => { { theiaPortalURL: 'https://theia.test', }, + { + allowOnlineIde: true, + }, + { + theiaImage: 'this-is-a-theia-image', + }, false, ], - ])('%s', (description, profileInfo, expectedVisibility) => { + ])('%s', (description, profileInfo, programmingExercise, buildConfig, expectedVisibility) => { getProfileInfoSub = jest.spyOn(profileService, 'getProfileInfo'); getProfileInfoSub.mockReturnValue(of(profileInfo as ProfileInfo)); - comp.exercise = exercise; + + // Expand the programmingExercise by given properties + comp.exercise = { ...exercise, ...programmingExercise, buildConfig: buildConfig } as ProgrammingExercise; fixture.detectChanges(); diff --git a/src/test/javascript/spec/component/participation-submission/participation-submission.component.spec.ts b/src/test/javascript/spec/component/participation-submission/participation-submission.component.spec.ts index 3e18c92b27c0..43b16fc6e6d0 100644 --- a/src/test/javascript/spec/component/participation-submission/participation-submission.component.spec.ts +++ b/src/test/javascript/spec/component/participation-submission/participation-submission.component.spec.ts @@ -20,15 +20,15 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; import { Submission, SubmissionExerciseType, SubmissionType } from 'app/entities/submission.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { RouterTestingModule } from '@angular/router/testing'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; diff --git a/src/test/javascript/spec/component/participation/participation.component.spec.ts b/src/test/javascript/spec/component/participation/participation.component.spec.ts index 569fc6ab73bb..7c96169e95ff 100644 --- a/src/test/javascript/spec/component/participation/participation.component.spec.ts +++ b/src/test/javascript/spec/component/participation/participation.component.spec.ts @@ -7,7 +7,7 @@ import { ParticipationService } from 'app/exercises/shared/participation/partici import { ParticipationComponent } from 'app/exercises/shared/participation/participation.component'; import { Course } from 'app/entities/course.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { of, throwError } from 'rxjs'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import dayjs from 'dayjs/esm'; @@ -31,7 +31,7 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; import { ArtemisTestModule } from '../../test.module'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { GradeStepsDTO } from 'app/entities/grade-step.model'; import { AlertService } from 'app/core/util/alert.service'; diff --git a/src/test/javascript/spec/component/plagiarism/exercise-update-plagiarism.component.spec.ts b/src/test/javascript/spec/component/plagiarism/exercise-update-plagiarism.component.spec.ts index feeb959ee591..9b85c7b332f4 100644 --- a/src/test/javascript/spec/component/plagiarism/exercise-update-plagiarism.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/exercise-update-plagiarism.component.spec.ts @@ -1,6 +1,6 @@ import { DEFAULT_PLAGIARISM_DETECTION_CONFIG, Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ExerciseUpdatePlagiarismComponent } from 'app/exercises/shared/plagiarism/exercise-update-plagiarism/exercise-update-plagiarism.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Subject } from 'rxjs'; describe('Exercise Update Plagiarism Component', () => { diff --git a/src/test/javascript/spec/component/plagiarism/plagiarism-case-instructor-detail-view.component.spec.ts b/src/test/javascript/spec/component/plagiarism/plagiarism-case-instructor-detail-view.component.spec.ts index fa2105078290..c66f5a72b3c1 100644 --- a/src/test/javascript/spec/component/plagiarism/plagiarism-case-instructor-detail-view.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/plagiarism-case-instructor-detail-view.component.spec.ts @@ -8,7 +8,7 @@ import { TranslateService } from '@ngx-translate/core'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { HttpResponse } from '@angular/common/http'; import { Observable, ReplaySubject, of } from 'rxjs'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import { MockLocalStorageService } from '../../helpers/mocks/service/mock-local-storage.service'; import { MetisService } from 'app/shared/metis/metis.service'; diff --git a/src/test/javascript/spec/component/plagiarism/plagiarism-case-student-detail-view.component.spec.ts b/src/test/javascript/spec/component/plagiarism/plagiarism-case-student-detail-view.component.spec.ts index 29248886bc42..5b4ecff30533 100644 --- a/src/test/javascript/spec/component/plagiarism/plagiarism-case-student-detail-view.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/plagiarism-case-student-detail-view.component.spec.ts @@ -12,7 +12,7 @@ import { MockLocalStorageService } from '../../helpers/mocks/service/mock-local- import { MetisService } from 'app/shared/metis/metis.service'; import { TranslateService } from '@ngx-translate/core'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import dayjs from 'dayjs/esm'; import { NotificationService } from 'app/shared/notification/notification.service'; diff --git a/src/test/javascript/spec/component/plagiarism/plagiarism-cases-instructor-view.component.spec.ts b/src/test/javascript/spec/component/plagiarism/plagiarism-cases-instructor-view.component.spec.ts index 6d04c479847c..4a6bda12288c 100644 --- a/src/test/javascript/spec/component/plagiarism/plagiarism-cases-instructor-view.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/plagiarism-cases-instructor-view.component.spec.ts @@ -8,7 +8,7 @@ import { Observable, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { TranslateService } from '@ngx-translate/core'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import * as DownloadUtil from 'app/shared/util/download.util'; import dayjs from 'dayjs/esm'; diff --git a/src/test/javascript/spec/component/plagiarism/plagiarism-inspector.component.spec.ts b/src/test/javascript/spec/component/plagiarism/plagiarism-inspector.component.spec.ts index ebb90a9f69a2..55b1e0eb0ea4 100644 --- a/src/test/javascript/spec/component/plagiarism/plagiarism-inspector.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/plagiarism-inspector.component.spec.ts @@ -11,8 +11,8 @@ import { ModelingPlagiarismResult } from 'app/exercises/shared/plagiarism/types/ import { PlagiarismStatus } from 'app/exercises/shared/plagiarism/types/PlagiarismStatus'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { TextPlagiarismResult } from 'app/exercises/shared/plagiarism/types/text/TextPlagiarismResult'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; diff --git a/src/test/javascript/spec/component/plagiarism/plagiarism-split-view.component.spec.ts b/src/test/javascript/spec/component/plagiarism/plagiarism-split-view.component.spec.ts index 7c95bd6dacb2..4b06a312d72c 100644 --- a/src/test/javascript/spec/component/plagiarism/plagiarism-split-view.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/plagiarism-split-view.component.spec.ts @@ -7,7 +7,7 @@ import { PlagiarismComparison } from 'app/exercises/shared/plagiarism/types/Plag import { PlagiarismSplitViewComponent } from 'app/exercises/shared/plagiarism/plagiarism-split-view/plagiarism-split-view.component'; import { ExerciseType } from 'app/entities/exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { PlagiarismSubmission } from 'app/exercises/shared/plagiarism/types/PlagiarismSubmission'; import { FromToElement, TextSubmissionElement } from 'app/exercises/shared/plagiarism/types/text/TextSubmissionElement'; import { PlagiarismMatch, SimpleMatch } from 'app/exercises/shared/plagiarism/types/PlagiarismMatch'; diff --git a/src/test/javascript/spec/component/plagiarism/text-submission-viewer.component.spec.ts b/src/test/javascript/spec/component/plagiarism/text-submission-viewer.component.spec.ts index 225fc1227220..2118d941e1df 100644 --- a/src/test/javascript/spec/component/plagiarism/text-submission-viewer.component.spec.ts +++ b/src/test/javascript/spec/component/plagiarism/text-submission-viewer.component.spec.ts @@ -7,9 +7,9 @@ import { TextSubmissionViewerComponent } from 'app/exercises/shared/plagiarism/p import { CodeEditorRepositoryFileService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { DomainChange, DomainType, FileType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { PlagiarismSubmission } from 'app/exercises/shared/plagiarism/types/PlagiarismSubmission'; import { TextSubmissionElement } from 'app/exercises/shared/plagiarism/types/text/TextSubmissionElement'; diff --git a/src/test/javascript/spec/component/programming-assessment/code-editor-tutor-assessment-container.component.spec.ts b/src/test/javascript/spec/component/programming-assessment/code-editor-tutor-assessment-container.component.spec.ts index 059cbe0252d6..92350e8377d9 100644 --- a/src/test/javascript/spec/component/programming-assessment/code-editor-tutor-assessment-container.component.spec.ts +++ b/src/test/javascript/spec/component/programming-assessment/code-editor-tutor-assessment-container.component.spec.ts @@ -14,10 +14,10 @@ import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; import { RepositoryFileService } from 'app/exercises/shared/result/repository.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ProgrammingAssessmentRepoExportButtonComponent } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { ProgrammingAssessmentManualResultService } from 'app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Complaint } from 'app/entities/complaint.model'; import { ComplaintService } from 'app/complaints/complaint.service'; import { MockRepositoryFileService } from '../../helpers/mocks/service/mock-repository-file.service'; diff --git a/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-button.component.spec.ts b/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-button.component.spec.ts index 28d7a4ff4867..5766fd56beda 100644 --- a/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-button.component.spec.ts @@ -1,7 +1,7 @@ import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../test.module'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockTranslateValuesDirective } from '../../helpers/mocks/directive/mock-translate-values.directive'; diff --git a/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-dialog.component.spec.ts b/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-dialog.component.spec.ts index c137b2e2db37..7f9e8d1d6ed4 100644 --- a/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-dialog.component.spec.ts +++ b/src/test/javascript/spec/component/programming-assessment/programming-assessment-repo-export-dialog.component.spec.ts @@ -6,7 +6,7 @@ import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingAssessmentRepoExportDialogComponent } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ProgrammingAssessmentRepoExportService } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/build-plan-editor.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/build-plan-editor.component.spec.ts index a62e6c9be607..b629be2732b0 100644 --- a/src/test/javascript/spec/component/programming-exercise/build-plan-editor.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/build-plan-editor.component.spec.ts @@ -6,7 +6,7 @@ import { ProgrammingExerciseService } from 'app/exercises/programming/manage/ser import { MockProgrammingExerciseService } from '../../helpers/mocks/service/mock-programming-exercise.service'; import { MockActivatedRoute } from '../../helpers/mocks/activated-route/mock-activated-route'; import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; -import { BuildPlan } from 'app/entities/build-plan.model'; +import { BuildPlan } from 'app/entities/programming/build-plan.model'; import { of, throwError } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { MockBuildPlanService } from '../../helpers/mocks/service/mock-build-plan.service'; @@ -15,7 +15,7 @@ import { MockComponent } from 'ng-mocks'; import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; import { NgbTooltipMocksModule } from '../../helpers/mocks/directive/ngbTooltipMocks.module'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AlertService } from 'app/core/util/alert.service'; import { MockAlertService } from '../../helpers/mocks/service/mock-alert.service'; import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/category-issues-chart.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/category-issues-chart.component.spec.ts index 3a8cce90d652..17fee925d299 100644 --- a/src/test/javascript/spec/component/programming-exercise/category-issues-chart.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/category-issues-chart.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { CategoryIssuesChartComponent } from 'app/exercises/programming/manage/grading/charts/category-issues-chart.component'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; describe('CategoryIssuesChartComponent', () => { let fixture: ComponentFixture; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-configure-grading.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-configure-grading.component.spec.ts index 603c70222109..714f39e19564 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-configure-grading.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-configure-grading.component.spec.ts @@ -7,10 +7,10 @@ import { NgxDatatableModule } from '@flaviosantoro92/ngx-datatable'; import { NgbModal, NgbModalRef, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; +import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; import { CategoryIssuesChartComponent } from 'app/exercises/programming/manage/grading/charts/category-issues-chart.component'; import { ScaCategoryDistributionChartComponent } from 'app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component'; import { TestCaseDistributionChartComponent } from 'app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-create-buttons.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-create-buttons.component.spec.ts index 17f226cd84d6..630029986a67 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-create-buttons.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-create-buttons.component.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing'; import { ExerciseType } from 'app/entities/exercise.model'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseComponent } from 'app/exercises/programming/manage/programming-exercise.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts index fdc1d42c64b1..8b59cc5e8c6b 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-aeolus-build-plan.component.spec.ts @@ -1,16 +1,10 @@ import { TestBed } from '@angular/core/testing'; +import { BuildAction, PlatformAction, ScriptAction } from 'app/entities/programming/build.action'; +import { DockerConfiguration } from 'app/entities/programming/docker.configuration'; +import { WindFile } from 'app/entities/programming/wind.file'; +import { WindMetadata } from 'app/entities/programming/wind.metadata'; import { ArtemisTestModule } from '../../test.module'; -import { - BuildAction, - DockerConfiguration, - PlatformAction, - ProgrammingExercise, - ProgrammingLanguage, - ProjectType, - ScriptAction, - WindFile, - WindMetadata, -} from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { ProgrammingExerciseCustomAeolusBuildPlanComponent } from 'app/exercises/programming/manage/update/update-components/custom-build-plans/programming-exercise-custom-aeolus-build-plan.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts index fe342f5eac56..b2ec21d46f82 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-custom-build-plan.component.spec.ts @@ -1,16 +1,10 @@ import { TestBed } from '@angular/core/testing'; +import { BuildAction, PlatformAction, ScriptAction } from 'app/entities/programming/build.action'; +import { DockerConfiguration } from 'app/entities/programming/docker.configuration'; +import { WindFile } from 'app/entities/programming/wind.file'; +import { WindMetadata } from 'app/entities/programming/wind.metadata'; import { ArtemisTestModule } from '../../test.module'; -import { - BuildAction, - DockerConfiguration, - PlatformAction, - ProgrammingExercise, - ProgrammingLanguage, - ProjectType, - ScriptAction, - WindFile, - WindMetadata, -} from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { ElementRef, Renderer2 } from '@angular/core'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts index 47ea2df80cb5..8563723b42b8 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { of, throwError } from 'rxjs'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseDetailComponent } from 'app/exercises/programming/manage/programming-exercise-detail.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockActivatedRoute } from '../../helpers/mocks/activated-route/mock-activated-route'; import { Course } from 'app/entities/course.model'; import { TranslateModule } from '@ngx-translate/core'; @@ -11,7 +11,7 @@ import { StatisticsService } from 'app/shared/statistics-graph/statistics.servic import { ExerciseManagementStatisticsDto } from 'app/exercises/shared/statistics/exercise-management-statistics-dto'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { MockProfileService } from '../../helpers/mocks/service/mock-profile.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; import { MockProgrammingExerciseService } from '../../helpers/mocks/service/mock-programming-exercise.service'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; @@ -24,7 +24,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockProgrammingExerciseGradingService } from '../../helpers/mocks/service/mock-programming-exercise-grading.service'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model'; import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model'; -import { BuildLogStatisticsDTO } from 'app/entities/build-log-statistics-dto'; +import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { HttpResponse } from '@angular/common/http'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-edit-selected.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-edit-selected.component.spec.ts index 550e8a533d0c..802cb0180956 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-edit-selected.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-edit-selected.component.spec.ts @@ -7,7 +7,7 @@ import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-editable-instruction.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-editable-instruction.component.spec.ts index 14ced4c71eea..c00bb591678c 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-editable-instruction.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-editable-instruction.component.spec.ts @@ -17,7 +17,7 @@ import { TemplateProgrammingExerciseParticipation } from 'app/entities/participa import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { IProgrammingExerciseGradingService, ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; import { Result } from 'app/entities/result.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseInstructionAnalysisComponent } from 'app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.component'; import { ProgrammingExerciseEditableInstructionComponent } from 'app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component'; import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instruction.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instruction.component.spec.ts index 4a4bee296170..3cfff55d7749 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instruction.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instruction.component.spec.ts @@ -31,7 +31,7 @@ import { ProgrammingExerciseParticipationService } from 'app/exercises/programmi import { ProgrammingExerciseInstructionTaskStatusComponent } from 'app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component'; import { Result } from 'app/entities/result.model'; import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockParticipationWebsocketService } from '../../helpers/mocks/service/mock-participation-websocket.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-status.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-status.component.spec.ts index 6f8ec78aa596..0d90f4c63d76 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-status.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-status.component.spec.ts @@ -8,8 +8,8 @@ import { Result } from 'app/entities/result.model'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { triggerChanges } from '../../helpers/utils/general.utils'; import { ProgrammingExerciseInstructorStatusComponent } from 'app/exercises/programming/manage/status/programming-exercise-instructor-status.component'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming-exercise-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-submission-state.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-submission-state.component.spec.ts index e1638e73e8e8..0a0bee0d069b 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-submission-state.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-submission-state.component.spec.ts @@ -12,7 +12,7 @@ import { ExerciseSubmissionState, ProgrammingSubmissionService, ProgrammingSubmi import { ProgrammingExerciseInstructorSubmissionStateComponent } from 'app/exercises/programming/shared/actions/programming-exercise-instructor-submission-state.component'; import { triggerChanges } from '../../helpers/utils/general.utils'; import { BuildRunState, ProgrammingBuildRunService } from 'app/exercises/programming/participate/programming-build-run.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; import { MockDirective, MockModule, MockPipe } from 'ng-mocks'; import { ProgrammingExerciseTriggerAllButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-trigger-build-button.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-trigger-build-button.component.spec.ts index f488e31968f6..bf9b03e41e6c 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-trigger-build-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-instructor-trigger-build-button.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { Course } from 'app/entities/course.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-lifecycle.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-lifecycle.component.spec.ts index b9bbd8e9ad28..32c41626803d 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-lifecycle.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-lifecycle.component.spec.ts @@ -4,7 +4,7 @@ import { ArtemisTestModule } from '../../test.module'; import { MockComponent, MockDirective } from 'ng-mocks'; import { ProgrammingExerciseLifecycleComponent } from 'app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseTestScheduleDatePickerComponent } from 'app/exercises/programming/shared/lifecycle/programming-exercise-test-schedule-date-picker.component'; import { NgModel } from '@angular/forms'; import { AssessmentType } from 'app/entities/assessment-type.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-re-evaluate-button.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-re-evaluate-button.component.spec.ts index 9283221d1266..16bd1cdf33b0 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-re-evaluate-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-re-evaluate-button.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { Course } from 'app/entities/course.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-repository-and-build-plan-details.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-repository-and-build-plan-details.component.spec.ts index ccc542ea3ec5..aa7f5e3e80bb 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-repository-and-build-plan-details.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-repository-and-build-plan-details.component.spec.ts @@ -1,11 +1,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { CheckoutDirectoriesDto } from 'app/entities/checkout-directories-dto'; -import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { CheckoutDirectoriesDto } from 'app/entities/programming/checkout-directories-dto'; +import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { MockComponent } from 'ng-mocks'; import { Subscription, of } from 'rxjs'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { SimpleChanges } from '@angular/core'; import { ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component'; import { ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-reset-dialog.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-reset-dialog.component.spec.ts index 6f1c53159b02..596b3dfad522 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-reset-dialog.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-reset-dialog.component.spec.ts @@ -6,7 +6,7 @@ import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseResetDialogComponent } from 'app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { ProgrammingExerciseResetOptions, ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { AlertService } from 'app/core/util/alert.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-test-case-passed-builds-charts.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-test-case-passed-builds-charts.component.spec.ts index 18603184899b..b237c4ad56c7 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-test-case-passed-builds-charts.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-test-case-passed-builds-charts.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { TestCasePassedBuildsChartComponent } from 'app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component'; -import { TestCaseStats } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; describe('TestCasePassedBuildsChartComponent', () => { let comp: TestCasePassedBuildsChartComponent; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-all-button.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-all-button.component.spec.ts index 019291c960f3..000cf3dbeb54 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-all-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-all-button.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { Course } from 'app/entities/course.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-build-button.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-build-button.component.spec.ts index 5a74d40dec87..c50aa8ffcea1 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-build-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-trigger-build-button.component.spec.ts @@ -19,7 +19,7 @@ import { ArtemisProgrammingExerciseActionsModule } from 'app/exercises/programmi import { triggerChanges } from '../../helpers/utils/general.utils'; import { InitializationState } from 'app/entities/participation/participation.model'; import { ProgrammingExerciseStudentTriggerBuildButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-student-trigger-build-button.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; describe('TriggerBuildButtonSpec', () => { diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts index df1e7b8924b0..093df3a0508f 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-update.component.spec.ts @@ -2,13 +2,14 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testin import { DebugElement } from '@angular/core'; import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { ActivatedRoute, UrlSegment } from '@angular/router'; +import { WindFile } from 'app/entities/programming/wind.file'; import { Subject, of, throwError } from 'rxjs'; import dayjs from 'dayjs/esm'; import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal.service'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseUpdateComponent } from 'app/exercises/programming/manage/update/programming-exercise-update.component'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise, ProgrammingLanguage, ProjectType, WindFile } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise, ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; @@ -62,11 +63,14 @@ import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { ExerciseUpdateNotificationComponent } from 'app/exercises/shared/exercise-update-notification/exercise-update-notification.component'; import { ExerciseUpdatePlagiarismComponent } from 'app/exercises/shared/plagiarism/exercise-update-plagiarism/exercise-update-plagiarism.component'; import * as Utils from 'app/exercises/shared/course-exercises/course-utils'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; import { AlertService, AlertType } from 'app/core/util/alert.service'; import { FormStatusBarComponent } from 'app/forms/form-status-bar/form-status-bar.component'; import { FormFooterComponent } from 'app/forms/form-footer/form-footer.component'; import { ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent } from 'app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component'; +import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; +import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { PROFILE_THEIA } from 'app/app.constants'; describe('ProgrammingExerciseUpdateComponent', () => { const courseId = 1; @@ -80,6 +84,9 @@ describe('ProgrammingExerciseUpdateComponent', () => { let exerciseGroupService: ExerciseGroupService; let programmingExerciseFeatureService: ProgrammingLanguageFeatureService; let alertService: AlertService; + let profileService: ProfileService; + + let getProfileInfoSub: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ @@ -147,6 +154,12 @@ describe('ProgrammingExerciseUpdateComponent', () => { exerciseGroupService = debugElement.injector.get(ExerciseGroupService); programmingExerciseFeatureService = debugElement.injector.get(ProgrammingLanguageFeatureService); alertService = debugElement.injector.get(AlertService); + profileService = debugElement.injector.get(ProfileService); + + getProfileInfoSub = jest.spyOn(profileService, 'getProfileInfo'); + const newProfileInfo = new ProfileInfo(); + newProfileInfo.activeProfiles = []; + getProfileInfoSub.mockReturnValue(of(newProfileInfo)); }); }); @@ -792,11 +805,56 @@ describe('ProgrammingExerciseUpdateComponent', () => { }); }); - it('find validation errors for invalid ide selection', () => { + it.each([ + [ + 'find validation errors for invalid ide selection', + { + activeProfiles: [PROFILE_THEIA], + }, + { + translateKey: 'artemisApp.programmingExercise.allowOnlineEditor.alert', + translateValues: {}, + }, + ], + [ + 'find validation errors for invalid ide selection without theia profile', + { + activeProfiles: [], + }, + { + translateKey: 'artemisApp.programmingExercise.allowOnlineEditor.alertNoTheia', + translateValues: {}, + }, + ], + ])('%s', (description, profileInfo, expectedException) => { + getProfileInfoSub = jest.spyOn(profileService, 'getProfileInfo'); + + const newProfileInfo = new ProfileInfo(); + newProfileInfo.activeProfiles = profileInfo.activeProfiles; + + getProfileInfoSub.mockReturnValue(of(newProfileInfo)); + + const route = TestBed.inject(ActivatedRoute); + route.params = of({ courseId }); + route.url = of([{ path: 'new' } as UrlSegment]); + route.data = of({ programmingExercise: comp.programmingExercise }); + + jest.spyOn(programmingExerciseFeatureService, 'getProgrammingLanguageFeature').mockReturnValue(getProgrammingLanguageFeature(ProgrammingLanguage.JAVA)); + comp.programmingExercise.allowOnlineEditor = false; comp.programmingExercise.allowOfflineIde = false; + comp.programmingExercise.allowOnlineIde = false; + + fixture.detectChanges(); + + expect(comp.getInvalidReasons()).toContainEqual(expectedException); + }); + + it('find validation errors for invalid online IDE image', () => { + comp.programmingExercise.allowOnlineIde = true; + comp.programmingExercise.buildConfig!.theiaImage = undefined; expect(comp.getInvalidReasons()).toContainEqual({ - translateKey: 'artemisApp.programmingExercise.allowOnlineEditor.alert', + translateKey: 'artemisApp.programmingExercise.theiaImage.alert', translateValues: {}, }); }); @@ -832,6 +890,7 @@ describe('ProgrammingExerciseUpdateComponent', () => { comp.programmingExercise.maxStaticCodeAnalysisPenalty = 60; comp.programmingExercise.allowOfflineIde = true; comp.programmingExercise.allowOnlineEditor = false; + comp.programmingExercise.allowOnlineIde = false; comp.programmingExercise.packageName = 'de.tum.in'; comp.programmingExercise.programmingLanguage = ProgrammingLanguage.JAVA; @@ -975,7 +1034,7 @@ describe('ProgrammingExerciseUpdateComponent', () => { fixture.detectChanges(); tick(); - const categories = [new ExerciseCategory()]; + const categories = [new ExerciseCategory(undefined, undefined)]; expect(comp.exerciseCategories).toBeUndefined(); comp.updateCategories(categories); expect(comp.exerciseCategories).toBe(categories); @@ -1017,6 +1076,7 @@ describe('ProgrammingExerciseUpdateComponent', () => { comp.programmingExercise.allowOfflineIde = false; comp.programmingExercise.allowOnlineEditor = false; + comp.programmingExercise.allowOnlineIde = false; comp.calculateFormStatusSections(); expect(comp.formStatusSections[1].valid).toBeFalse(); comp.programmingExercise.allowOnlineEditor = true; diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise.component.spec.ts index 7915f67f8f84..ec6b28b573ab 100644 --- a/src/test/javascript/spec/component/programming-exercise/programming-exercise.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise.component.spec.ts @@ -3,7 +3,7 @@ import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/ht import { of, throwError } from 'rxjs'; import { ArtemisTestModule } from '../../test.module'; import { ProgrammingExerciseComponent } from 'app/exercises/programming/manage/programming-exercise.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; diff --git a/src/test/javascript/spec/component/programming-exercise/sca-category-distribution-chart.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/sca-category-distribution-chart.component.spec.ts index fb52afff1ebc..863bb75ed2ca 100644 --- a/src/test/javascript/spec/component/programming-exercise/sca-category-distribution-chart.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/sca-category-distribution-chart.component.spec.ts @@ -6,9 +6,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { BarChartModule } from '@swimlane/ngx-charts'; import { TranslateService } from '@ngx-translate/core'; -import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/static-code-analysis-category.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { CategoryIssuesMap } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { CategoryIssuesMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; describe('SCA category distribution chart', () => { diff --git a/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-grading-tasks-table.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-grading-tasks-table.component.spec.ts index 760a02a0c910..dea5aea61f5d 100644 --- a/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-grading-tasks-table.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-grading-tasks-table.component.spec.ts @@ -3,8 +3,8 @@ import { ArtemisTestModule } from '../../../test.module'; import { ProgrammingExerciseGradingTasksTableComponent } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component'; import { ProgrammingExerciseTaskService } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task.service'; import { Observable, Subject, of } from 'rxjs'; -import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; @@ -12,7 +12,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockPipe } from 'ng-mocks'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; import { ButtonComponent } from 'app/shared/components/button.component'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; describe('ProgrammingExerciseGradingTasksTableComponent', () => { let fixture; diff --git a/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-task.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-task.component.spec.ts index fe23d6eea2d4..2f0c52e95903 100644 --- a/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-task.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/tasks/programming-exercise-task.component.spec.ts @@ -7,7 +7,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockPipe } from 'ng-mocks'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; import { ProgrammingExerciseTaskComponent } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component'; -import { Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { TestCasePassedBuildsChartComponent } from 'app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component'; import { Subject } from 'rxjs'; diff --git a/src/test/javascript/spec/component/programming-exercise/test-case-distribution-chart.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/test-case-distribution-chart.component.spec.ts index 32a4f2bd9ab9..f022af2d9cd1 100644 --- a/src/test/javascript/spec/component/programming-exercise/test-case-distribution-chart.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/test-case-distribution-chart.component.spec.ts @@ -5,10 +5,10 @@ import { TranslateService } from '@ngx-translate/core'; import { TestCaseDistributionChartComponent } from 'app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component'; import { MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { BarChartModule } from '@swimlane/ngx-charts'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { TestCaseStatsMap } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { TestCaseStatsMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; describe('Test case distribution chart', () => { diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts index d33537559cbd..0b99443e2f99 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-creation-config-mock.ts @@ -1,8 +1,8 @@ import { ProgrammingExerciseCreationConfig } from 'app/exercises/programming/manage/update/programming-exercise-creation-config'; import { Observable } from 'rxjs'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; /* eslint-disable @typescript-eslint/no-unused-vars */ export const programmingExerciseCreationConfigMock: ProgrammingExerciseCreationConfig = { @@ -69,5 +69,8 @@ export const programmingExerciseCreationConfigMock: ProgrammingExerciseCreationC validIdeSelection(): boolean | undefined { return true; }, + validOnlineIdeSelection(): boolean | undefined { + return true; + }, withDependencies: false, }; diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-difficulty.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-difficulty.component.spec.ts index 11d63911cc2c..70c70a17dd5b 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-difficulty.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-difficulty.component.spec.ts @@ -1,5 +1,6 @@ -import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent, MockPipe } from 'ng-mocks'; +import { DebugElement } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @@ -8,15 +9,22 @@ import { CheckboxControlValueAccessor, DefaultValueAccessor, NgModel, NumberValu import { DifficultyPickerComponent } from 'app/exercises/shared/difficulty-picker/difficulty-picker.component'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; import { programmingExerciseCreationConfigMock } from './programming-exercise-creation-config-mock'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; +import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { PROFILE_THEIA } from 'app/app.constants'; +import { ArtemisTestModule } from '../../../test.module'; describe('ProgrammingExerciseDifficultyComponent', () => { let fixture: ComponentFixture; let comp: ProgrammingExerciseDifficultyComponent; + let debugElement: DebugElement; + let profileService: ProfileService; + let getProfileInfoSub: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ - imports: [], + imports: [ArtemisTestModule], declarations: [ CheckboxControlValueAccessor, DefaultValueAccessor, @@ -42,6 +50,11 @@ describe('ProgrammingExerciseDifficultyComponent', () => { comp = fixture.componentInstance; comp.programmingExercise = new ProgrammingExercise(undefined, undefined); comp.programmingExerciseCreationConfig = programmingExerciseCreationConfigMock; + + debugElement = fixture.debugElement; + profileService = debugElement.injector.get(ProfileService); + getProfileInfoSub = jest.spyOn(profileService, 'getProfileInfo'); + getProfileInfoSub.mockReturnValue(of({ inProduction: false, sshCloneURLTemplate: 'ssh://git@testserver.com:1234/' } as ProfileInfo)); }); }); @@ -49,8 +62,16 @@ describe('ProgrammingExerciseDifficultyComponent', () => { jest.restoreAllMocks(); }); - it('should initialize', fakeAsync(() => { + it('should initialize', () => { fixture.detectChanges(); expect(comp).not.toBeNull(); - })); + }); + + it('should initialize theiaEnabled', () => { + getProfileInfoSub = jest.spyOn(profileService, 'getProfileInfo'); + getProfileInfoSub.mockReturnValue(of({ activeProfiles: [PROFILE_THEIA] } as ProfileInfo)); + + fixture.detectChanges(); + expect(comp.theiaEnabled).toBeTrue(); + }); }); diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-grading.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-grading.component.spec.ts index e59522cf01aa..8087a70b7cea 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-grading.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-grading.component.spec.ts @@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router'; import { Subject, of } from 'rxjs'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ProgrammingExerciseGradingComponent } from 'app/exercises/programming/manage/update/update-components/programming-exercise-grading.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-information.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-information.component.spec.ts index 30259859d720..963ef4c6ff37 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-information.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-information.component.spec.ts @@ -6,7 +6,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ProgrammingExerciseInformationComponent } from 'app/exercises/programming/manage/update/update-components/programming-exercise-information.component'; import { DefaultValueAccessor, NgModel } from '@angular/forms'; import { RemoveKeysPipe } from 'app/shared/pipes/remove-keys.pipe'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { CategorySelectorComponent } from 'app/shared/category-selector/category-selector.component'; import { AddAuxiliaryRepositoryButtonComponent } from 'app/exercises/programming/manage/update/add-auxiliary-repository-button.component'; diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-language.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-language.component.spec.ts index 399f1969f22d..55eefafd0162 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-language.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-language.component.spec.ts @@ -5,19 +5,28 @@ import { of } from 'rxjs'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { CheckboxControlValueAccessor, DefaultValueAccessor, NgModel, NumberValueAccessor, SelectControlValueAccessor } from '@angular/forms'; import { RemoveKeysPipe } from 'app/shared/pipes/remove-keys.pipe'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseLanguageComponent } from 'app/exercises/programming/manage/update/update-components/programming-exercise-language.component'; import { programmingExerciseCreationConfigMock } from './programming-exercise-creation-config-mock'; +import { ProgrammingExerciseTheiaComponent } from 'app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component'; +import { provideHttpClient } from '@angular/common/http'; +import { TheiaService } from 'app/exercises/programming/shared/service/theia.service'; describe('ProgrammingExerciseLanguageComponent', () => { let fixture: ComponentFixture; let comp: ProgrammingExerciseLanguageComponent; + let theiaServiceMock!: { getTheiaImages: jest.Mock }; + beforeEach(() => { + theiaServiceMock = { + getTheiaImages: jest.fn(), + }; TestBed.configureTestingModule({ imports: [], declarations: [ ProgrammingExerciseLanguageComponent, + ProgrammingExerciseTheiaComponent, CheckboxControlValueAccessor, DefaultValueAccessor, SelectControlValueAccessor, @@ -27,10 +36,15 @@ describe('ProgrammingExerciseLanguageComponent', () => { MockPipe(RemoveKeysPipe), ], providers: [ + provideHttpClient(), { provide: ActivatedRoute, useValue: { queryParams: of({}) }, }, + { + provide: TheiaService, + useValue: theiaServiceMock, + }, ], schemas: [], }) @@ -52,4 +66,19 @@ describe('ProgrammingExerciseLanguageComponent', () => { tick(); expect(comp).not.toBeNull(); })); + + it('should not load TheiaComponent when online IDE is not allowed', fakeAsync(() => { + comp.programmingExercise.allowOnlineIde = false; + fixture.detectChanges(); + tick(); + expect(comp.programmingExerciseTheiaComponent).toBeUndefined(); + })); + + it('should load TheiaComponent when online IDE is allowed', fakeAsync(() => { + theiaServiceMock.getTheiaImages.mockReturnValue(of({})); + comp.programmingExercise.allowOnlineIde = true; + fixture.detectChanges(); + tick(); + expect(comp.programmingExerciseTheiaComponent).not.toBeNull(); + })); }); diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-problem.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-problem.component.spec.ts index b56a555ea0ff..51ded01756bf 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-problem.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/programming-exercise-problem.component.spec.ts @@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { ProgrammingExerciseProblemComponent } from 'app/exercises/programming/manage/update/update-components/programming-exercise-problem.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { CompetencySelectionComponent } from 'app/shared/competency-selection/competency-selection.component'; import { NgModel } from '@angular/forms'; diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/remove-auxiliary-repository-button.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/remove-auxiliary-repository-button.component.spec.ts index 7cec929c092d..cc205e0a226e 100644 --- a/src/test/javascript/spec/component/programming-exercise/update-components/remove-auxiliary-repository-button.component.spec.ts +++ b/src/test/javascript/spec/component/programming-exercise/update-components/remove-auxiliary-repository-button.component.spec.ts @@ -4,9 +4,9 @@ import { RemoveAuxiliaryRepositoryButtonComponent } from 'app/exercises/programm import { ArtemisTestModule } from '../../../test.module'; import { ButtonComponent } from 'app/shared/components/button.component'; import { MockComponent } from 'ng-mocks'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; -import { AuxiliaryRepository } from 'app/entities/programming-exercise-auxiliary-repository-model'; +import { AuxiliaryRepository } from 'app/entities/programming/programming-exercise-auxiliary-repository-model'; describe('RemoveAuxiliaryRepositoryButton', () => { let comp: RemoveAuxiliaryRepositoryButtonComponent; diff --git a/src/test/javascript/spec/component/programming-exercise/update-components/theia/programming-exercise-theia.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/update-components/theia/programming-exercise-theia.component.spec.ts new file mode 100644 index 000000000000..ab3018105eaa --- /dev/null +++ b/src/test/javascript/spec/component/programming-exercise/update-components/theia/programming-exercise-theia.component.spec.ts @@ -0,0 +1,90 @@ +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { MockPipe } from 'ng-mocks'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { RemoveKeysPipe } from 'app/shared/pipes/remove-keys.pipe'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { programmingExerciseCreationConfigMock } from '../programming-exercise-creation-config-mock'; +import { ProgrammingExerciseTheiaComponent } from 'app/exercises/programming/manage/update/update-components/theia/programming-exercise-theia.component'; +import { TheiaService } from 'app/exercises/programming/shared/service/theia.service'; +import { ArtemisSharedLibsModule } from 'app/shared/shared-libs.module'; + +describe('ProgrammingExerciseTheiaComponent', () => { + let fixture: ComponentFixture; + let comp: ProgrammingExerciseTheiaComponent; + + let theiaServiceMock!: { getTheiaImages: jest.Mock }; + + beforeEach(() => { + theiaServiceMock = { + getTheiaImages: jest.fn(), + }; + TestBed.configureTestingModule({ + imports: [ProgrammingExerciseTheiaComponent, ArtemisSharedLibsModule], + declarations: [MockPipe(ArtemisTranslatePipe), MockPipe(RemoveKeysPipe)], + providers: [ + { + provide: ActivatedRoute, + useValue: { queryParams: of({}) }, + }, + { + provide: TheiaService, + useValue: theiaServiceMock, + }, + ], + schemas: [], + }).compileComponents(); + + fixture = TestBed.createComponent(ProgrammingExerciseTheiaComponent); + comp = fixture.componentInstance; + comp.programmingExerciseCreationConfig = programmingExerciseCreationConfigMock; + comp.programmingExercise = new ProgrammingExercise(undefined, undefined); + comp.programmingExercise.allowOnlineIde = true; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should initialize', fakeAsync(() => { + fixture.detectChanges(); + tick(); + expect(comp).not.toBeNull(); + })); + + it('should have no selectedImage when no image is available', fakeAsync(() => { + theiaServiceMock.getTheiaImages.mockReturnValue(of({})); + fixture.detectChanges(); + comp.loadTheiaImages(); + tick(); + expect(comp.programmingExercise.buildConfig?.theiaImage).toBeUndefined(); + })); + + it('should select first image when none was selected', fakeAsync(() => { + theiaServiceMock.getTheiaImages.mockReturnValue( + of({ + 'Java-17': 'test-url', + 'Java-Test': 'test-url-2', + }), + ); + fixture.detectChanges(); + comp.loadTheiaImages(); + tick(); + expect(comp.programmingExercise.buildConfig?.theiaImage).toMatch('test-url'); + })); + + it('should not overwrite selected image when others are loaded', fakeAsync(() => { + comp.programmingExercise.buildConfig!.theiaImage = 'test-url-2'; + theiaServiceMock.getTheiaImages.mockReturnValue( + of({ + 'Java-17': 'test-url', + 'Java-Test': 'test-url-2', + }), + ); + fixture.detectChanges(); + comp.loadTheiaImages(); + tick(); + expect(comp.programmingExercise.buildConfig?.theiaImage).toMatch('test-url-2'); + })); +}); diff --git a/src/test/javascript/spec/component/quiz-exercise/quiz-exercise-update.component.spec.ts b/src/test/javascript/spec/component/quiz-exercise/quiz-exercise-update.component.spec.ts index 60940086d9d3..a93c87bdf3f3 100644 --- a/src/test/javascript/spec/component/quiz-exercise/quiz-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/quiz-exercise/quiz-exercise-update.component.spec.ts @@ -35,13 +35,14 @@ import { MockRouter } from '../../helpers/mocks/mock-router'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { ArtemisTestModule } from '../../test.module'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { MockProvider } from 'ng-mocks'; import { Duration } from 'app/exercises/quiz/manage/quiz-exercise-interfaces'; import { QuizQuestionListEditComponent } from 'app/exercises/quiz/manage/quiz-question-list-edit.component'; import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal.service'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; -describe('QuizExercise Update Detail Component', () => { +describe('QuizExerciseUpdateComponent', () => { let comp: QuizExerciseUpdateComponent; let exerciseGroupService: ExerciseGroupService; let courseManagementService: CourseManagementService; @@ -447,7 +448,7 @@ describe('QuizExercise Update Detail Component', () => { it('should updateCategories properly by making category available for selection again when removing it', () => { comp.quizExercise = quizExercise; comp.exerciseCategories = []; - const newCategories = [{ category: 'Easy' }, { category: 'Hard' }]; + const newCategories = [new ExerciseCategory('Easy', undefined), new ExerciseCategory('Hard', undefined)]; comp.updateCategories(newCategories); @@ -583,8 +584,8 @@ describe('QuizExercise Update Detail Component', () => { it('should update categories to given categories', () => { resetQuizExercise(); comp.quizExercise = quizExercise; - const exerciseCategory1 = { exerciseId: 1, category: 'category1', color: 'color1' }; - const exerciseCategory2 = { exerciseId: 1, category: 'category1', color: 'color1' }; + const exerciseCategory1 = new ExerciseCategory('category1', 'color1'); + const exerciseCategory2 = new ExerciseCategory('category1', 'color1'); const expected = [exerciseCategory1, exerciseCategory2]; comp.updateCategories([exerciseCategory1, exerciseCategory2]); expect(comp.quizExercise.categories).toEqual(expected); diff --git a/src/test/javascript/spec/component/range-slider.component.spec.ts b/src/test/javascript/spec/component/range-slider.component.spec.ts new file mode 100644 index 000000000000..7e1b747047e0 --- /dev/null +++ b/src/test/javascript/spec/component/range-slider.component.spec.ts @@ -0,0 +1,69 @@ +import { RangeSliderComponent } from 'app/shared/range-slider/range-slider.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MockModule } from 'ng-mocks'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +describe('RangeSliderComponent', () => { + let component: RangeSliderComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MockModule(FormsModule), MockModule(ReactiveFormsModule)], + declarations: [RangeSliderComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RangeSliderComponent); + component = fixture.componentInstance; + + component.generalMinValue = 0; + component.generalMaxValue = 100; + component.selectedMinValue = 20; + component.selectedMaxValue = 80; + component.step = 5; + }); + + it('should emit the updated max value', () => { + const emitSpy = jest.spyOn(component.selectedMaxValueChange, 'emit'); + + component.selectedMaxValue = 90; + const event = new Event('input'); + Object.defineProperty(event, 'target', { value: { className: 'range-max', value: 90 } }); + + component.onSelectedMaxValueChanged(event); + expect(emitSpy).toHaveBeenCalledWith(90); + }); + + it('should emit the updated max value rounded up to next selectable value', () => { + const emitSpy = jest.spyOn(component.selectedMaxValueChange, 'emit'); + + component.selectedMaxValue = 11; + const event = new Event('input'); + Object.defineProperty(event, 'target', { value: { className: 'range-max', value: 11 } }); + + component.onSelectedMaxValueChanged(event); + expect(emitSpy).toHaveBeenCalledWith(25); + }); + + it('should emit the updated min value', () => { + const emitSpy = jest.spyOn(component.selectedMinValueChange, 'emit'); + + component.selectedMinValue = 30; + const event = new Event('input'); + Object.defineProperty(event, 'target', { value: { className: 'range-min', value: 30 } }); + + component.onSelectedMinValueChanged(event); + expect(emitSpy).toHaveBeenCalledWith(30); + }); + + it('should emit the updated min value rounded down to next selectable value', () => { + const emitSpy = jest.spyOn(component.selectedMinValueChange, 'emit'); + + component.selectedMinValue = 99; + const event = new Event('input'); + Object.defineProperty(event, 'target', { value: { className: 'range-min', value: 99 } }); + + component.onSelectedMinValueChanged(event); + expect(emitSpy).toHaveBeenCalledWith(75); + }); +}); diff --git a/src/test/javascript/spec/component/shared/code-button.component.spec.ts b/src/test/javascript/spec/component/shared/code-button.component.spec.ts index 499a4e36dc9b..967f51a4e333 100644 --- a/src/test/javascript/spec/component/shared/code-button.component.spec.ts +++ b/src/test/javascript/spec/component/shared/code-button.component.spec.ts @@ -147,7 +147,7 @@ describe('CodeButtonComponent', () => { component.ngOnInit(); tick(); - expect(component.setupSshKeysUrl).toBe(`${window.location.origin}/user-settings/sshSettings`); + expect(component.sshSettingsUrl).toBe(`${window.location.origin}/user-settings/ssh`); expect(component.sshTemplateUrl).toBe(info.sshCloneURLTemplate); expect(component.sshEnabled).toBe(!!info.sshCloneURLTemplate); expect(component.versionControlUrl).toBe(info.versionControlUrl); @@ -158,13 +158,14 @@ describe('CodeButtonComponent', () => { getVcsAccessTokenSpy = jest.spyOn(accountService, 'getVcsAccessToken').mockReturnValue(throwError(() => new HttpErrorResponse({ status: 404, statusText: 'Not found' }))); stubServices(); participation.id = 1; + component.useParticipationVcsAccessToken = true; component.participations = [participation]; component.ngOnChanges(); tick(); component.ngOnInit(); tick(); - expect(component.useVersionControlAccessToken).toBeTrue(); + expect(component.accessTokensEnabled).toBeTrue(); expect(component.user.vcsAccessToken).toEqual(vcsToken); expect(getVcsAccessTokenSpy).toHaveBeenCalled(); expect(createVcsAccessTokenSpy).toHaveBeenCalled(); @@ -173,13 +174,14 @@ describe('CodeButtonComponent', () => { it('should not create new vcsAccessToken when it exists', fakeAsync(() => { participation.id = 1; component.participations = [participation]; + component.useParticipationVcsAccessToken = true; stubServices(); component.ngOnChanges(); tick(); component.ngOnInit(); tick(); - expect(component.useVersionControlAccessToken).toBeTrue(); + expect(component.accessTokensEnabled).toBeTrue(); expect(component.user.vcsAccessToken).toEqual(vcsToken); expect(getVcsAccessTokenSpy).toHaveBeenCalled(); expect(createVcsAccessTokenSpy).not.toHaveBeenCalled(); @@ -207,12 +209,13 @@ describe('CodeButtonComponent', () => { it('should not use ssh when ssh is not enabled (even if useSsh is set)', () => { participation.repositoryUri = `https://gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise-team1.git`; component.participations = [participation]; - component.useSsh = true; + component.useParticipationVcsAccessToken = true; + component.useSsh = false; component.isTeamParticipation = false; - component.useVersionControlAccessToken = true; - component.useToken = true; + component.accessTokensEnabled = true; component.ngOnInit(); component.ngOnChanges(); + component.useToken = true; const url = component.getHttpOrSshRepositoryUri(); expect(url).toBe(`https://${component.user.login}:**********@gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise-team1.git`); @@ -258,12 +261,13 @@ describe('CodeButtonComponent', () => { it('should insert the correct token in the repository uri', () => { participation.repositoryUri = `https://${component.user.login}@gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise-team1.git`; component.participations = [participation]; + component.useParticipationVcsAccessToken = true; component.useSsh = false; - component.useToken = true; component.isTeamParticipation = false; - component.useVersionControlAccessToken = true; + component.accessTokensEnabled = true; component.ngOnInit(); component.ngOnChanges(); + component.useToken = true; // Placeholder is shown let url = component.getHttpOrSshRepositoryUri(); @@ -288,12 +292,13 @@ describe('CodeButtonComponent', () => { it('should add the user login and token to the URL', () => { participation.repositoryUri = `https://gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise-team1.git`; component.participations = [participation]; + component.useParticipationVcsAccessToken = true; component.useSsh = false; - component.useToken = true; component.isTeamParticipation = false; - component.useVersionControlAccessToken = true; + component.accessTokensEnabled = true; component.ngOnInit(); component.ngOnChanges(); + component.useToken = true; const url = component.getHttpOrSshRepositoryUri(); expect(url).toBe(`https://${component.user.login}:**********@gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise-team1.git`); @@ -334,6 +339,22 @@ describe('CodeButtonComponent', () => { expect(component.getHttpOrSshRepositoryUri()).toBe('https://user1@gitlab.ase.in.tum.de/scm/ITCPLEASE1/itcplease1-exercise.solution.git'); }); + it('should set wasCopied to true and back to false after 3 seconds on successful copy', () => { + component.ngOnInit(); + jest.useFakeTimers(); + component.onCopyFinished(true); + expect(component.wasCopied).toBeTrue(); + jest.advanceTimersByTime(3000); + expect(component.wasCopied).toBeFalse(); + jest.useRealTimers(); + }); + + it('should not change wasCopied if copy is unsuccessful', () => { + component.ngOnInit(); + component.onCopyFinished(false); + expect(component.wasCopied).toBeFalse(); + }); + it('should fetch and store ssh preference', fakeAsync(() => { stubServices(); @@ -347,33 +368,33 @@ describe('CodeButtonComponent', () => { expect(localStorageUseSshRetrieveStub).toHaveBeenNthCalledWith(1, 'useSsh'); expect(localStorageUseSshObserveStub).toHaveBeenNthCalledWith(1, 'useSsh'); - expect(component.useSsh).toBeFalsy(); + expect(component.useSsh).toBeFalse(); fixture.debugElement.query(By.css('.code-button')).nativeElement.click(); tick(); fixture.debugElement.query(By.css('#useSSHButton')).nativeElement.click(); tick(); expect(localStorageUseSshStoreStub).toHaveBeenNthCalledWith(1, 'useSsh', true); - expect(component.useSsh).toBeTruthy(); + expect(component.useSsh).toBeTrue(); fixture.debugElement.query(By.css('#useHTTPSButton')).nativeElement.click(); tick(); expect(localStorageUseSshStoreStub).toHaveBeenCalledWith('useSsh', false); - expect(component.useSsh).toBeFalsy(); + expect(component.useSsh).toBeFalse(); fixture.debugElement.query(By.css('#useHTTPSWithTokenButton')).nativeElement.click(); tick(); expect(localStorageUseSshStoreStub).toHaveBeenCalledWith('useSsh', false); - expect(component.useSsh).toBeFalsy(); - expect(component.useToken).toBeTruthy(); + expect(component.useSsh).toBeFalse(); + expect(component.useToken).toBeTrue(); localStorageUseSshObserveStubSubject.next(true); tick(); - expect(component.useSsh).toBeTruthy(); + expect(component.useSsh).toBeTrue(); localStorageUseSshObserveStubSubject.next(false); tick(); - expect(component.useSsh).toBeFalsy(); + expect(component.useSsh).toBeFalse(); })); it.each([ @@ -420,7 +441,7 @@ describe('CodeButtonComponent', () => { guidedTourSettings: [], login: 'edx_userLogin', internal: true, - vcsAccessToken: 'token', + vcsAccessToken: vcsToken, }), ); diff --git a/src/test/javascript/spec/component/shared/commits-info-group.component.spec.ts b/src/test/javascript/spec/component/shared/commits-info-group.component.spec.ts index 5765ddfd3f8b..ddcf06c215a2 100644 --- a/src/test/javascript/spec/component/shared/commits-info-group.component.spec.ts +++ b/src/test/javascript/spec/component/shared/commits-info-group.component.spec.ts @@ -4,7 +4,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockPipe } from 'ng-mocks'; import dayjs from 'dayjs/esm'; import { ArtemisTestModule } from '../../test.module'; -import type { CommitInfo } from 'app/entities/programming-submission.model'; +import type { CommitInfo } from 'app/entities/programming/programming-submission.model'; import { CommitsInfoRowComponent } from 'app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component'; import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { TruncatePipe } from 'app/shared/pipes/truncate.pipe'; diff --git a/src/test/javascript/spec/component/shared/commits-info.component.spec.ts b/src/test/javascript/spec/component/shared/commits-info.component.spec.ts index 0b5dc3c7fae6..3d7c860143ce 100644 --- a/src/test/javascript/spec/component/shared/commits-info.component.spec.ts +++ b/src/test/javascript/spec/component/shared/commits-info.component.spec.ts @@ -7,7 +7,7 @@ import { ArtemisTestModule } from '../../test.module'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockPipe } from 'ng-mocks'; import { of } from 'rxjs'; -import { CommitInfo } from 'app/entities/programming-submission.model'; +import { CommitInfo } from 'app/entities/programming/programming-submission.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; diff --git a/src/test/javascript/spec/component/shared/course-exam-archive-button.component.spec.ts b/src/test/javascript/spec/component/shared/course-exam-archive-button.component.spec.ts index c667429e667b..7093eb87a5d0 100644 --- a/src/test/javascript/spec/component/shared/course-exam-archive-button.component.spec.ts +++ b/src/test/javascript/spec/component/shared/course-exam-archive-button.component.spec.ts @@ -19,7 +19,7 @@ import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.di import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { CourseExamArchiveButtonComponent, CourseExamArchiveState } from 'app/shared/components/course-exam-archive-button/course-exam-archive-button.component'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { AccountService } from 'app/core/auth/account.service'; import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'; diff --git a/src/test/javascript/spec/component/shared/example-solution.component.spec.ts b/src/test/javascript/spec/component/shared/example-solution.component.spec.ts index 036b0ee9d044..95cc9ddbe47f 100644 --- a/src/test/javascript/spec/component/shared/example-solution.component.spec.ts +++ b/src/test/javascript/spec/component/shared/example-solution.component.spec.ts @@ -11,7 +11,7 @@ import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { ExampleSolutionComponent } from 'app/exercises/shared/example-solution/example-solution.component'; import { ExampleSolutionInfo, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; import { HeaderExercisePageWithDetailsComponent } from 'app/exercises/shared/exercise-headers/header-exercise-page-with-details.component'; import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; diff --git a/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.component.spec.ts b/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.component.spec.ts new file mode 100644 index 000000000000..3432102e0a9d --- /dev/null +++ b/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.component.spec.ts @@ -0,0 +1,259 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ExerciseFilterModalComponent } from 'app/shared/exercise-filter/exercise-filter-modal.component'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { MockComponent, MockModule, MockProvider } from 'ng-mocks'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component'; +import { RangeSliderComponent } from 'app/shared/range-slider/range-slider.component'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { RangeFilter } from 'app/types/exercise-filter'; +import { DifficultyLevel, Exercise, ExerciseType, getIcon } from 'app/entities/exercise.model'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { SidebarCardElement } from 'app/types/sidebar'; +import { Result } from 'app/entities/result.model'; +import { StudentParticipation } from 'app/entities/participation/student-participation.model'; + +const EXERCISE_1 = { categories: [new ExerciseCategory('category1', undefined), new ExerciseCategory('category2', '#1b97ca')], maxPoints: 5, type: ExerciseType.TEXT } as Exercise; +const EXERCISE_2 = { + categories: [new ExerciseCategory('category3', '#0d3cc2'), new ExerciseCategory('category4', '#6ae8ac')], + maxPoints: 5, + type: ExerciseType.PROGRAMMING, +} as Exercise; +const EXERCISE_3 = { + categories: [new ExerciseCategory('category1', undefined), new ExerciseCategory('category4', '#6ae8ac')], + maxPoints: 8, + type: ExerciseType.PROGRAMMING, +} as Exercise; + +const SIDEBAR_CARD_ELEMENT_1 = { + exercise: EXERCISE_1, + type: ExerciseType.TEXT, + difficulty: DifficultyLevel.HARD, + studentParticipation: { results: [{ score: 7.7 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_2 = { + exercise: EXERCISE_2, + type: ExerciseType.PROGRAMMING, + difficulty: DifficultyLevel.EASY, + studentParticipation: { results: [{ score: 5.0 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_3 = { + exercise: EXERCISE_3, + type: ExerciseType.PROGRAMMING, + difficulty: DifficultyLevel.EASY, + studentParticipation: { results: [{ score: 82.3 } as Result] } as StudentParticipation, +} as SidebarCardElement; + +const SCORE_FILTER: RangeFilter = { + isDisplayed: true, + filter: { + generalMin: 0, + generalMax: 100, + selectedMin: 0, + selectedMax: 100, + step: 5, + }, +}; + +const POINTS_FILTER: RangeFilter = { + isDisplayed: true, + filter: { + generalMin: 0, + generalMax: 20, + selectedMin: 0, + selectedMax: 20, + step: 1, + }, +}; + +describe('ExerciseFilterModalComponent', () => { + let component: ExerciseFilterModalComponent; + let fixture: ComponentFixture; + let activeModal: NgbActiveModal; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + MockModule(FormsModule), + MockModule(ReactiveFormsModule), + MockModule(FontAwesomeModule), + MockModule(ArtemisSharedCommonModule), + MockModule(ArtemisSharedComponentModule), + ], + declarations: [ExerciseFilterModalComponent, MockComponent(CustomExerciseCategoryBadgeComponent), MockComponent(RangeSliderComponent)], + providers: [MockProvider(NgbActiveModal)], + }).compileComponents(); + + fixture = TestBed.createComponent(ExerciseFilterModalComponent); + component = fixture.componentInstance; + activeModal = TestBed.inject(NgbActiveModal); + + component.exerciseFilters = { + exerciseTypesFilter: { + isDisplayed: true, + options: [ + { name: 'artemisApp.courseStatistics.programming', value: ExerciseType.PROGRAMMING, checked: false, icon: getIcon(ExerciseType.PROGRAMMING) }, + { name: 'artemisApp.courseStatistics.quiz', value: ExerciseType.QUIZ, checked: false, icon: getIcon(ExerciseType.QUIZ) }, + { name: 'artemisApp.courseStatistics.modeling', value: ExerciseType.MODELING, checked: false, icon: getIcon(ExerciseType.MODELING) }, + { name: 'artemisApp.courseStatistics.text', value: ExerciseType.TEXT, checked: false, icon: getIcon(ExerciseType.TEXT) }, + { name: 'artemisApp.courseStatistics.file-upload', value: ExerciseType.FILE_UPLOAD, checked: false, icon: getIcon(ExerciseType.FILE_UPLOAD) }, + ], + }, + difficultyFilter: { + isDisplayed: true, + options: [ + { name: 'artemisApp.exercise.easy', value: DifficultyLevel.EASY, checked: false }, + { name: 'artemisApp.exercise.medium', value: DifficultyLevel.MEDIUM, checked: false }, + { name: 'artemisApp.exercise.hard', value: DifficultyLevel.HARD, checked: false }, + ], + }, + categoryFilter: { + isDisplayed: true, + options: [ + { category: new ExerciseCategory('category1', undefined), searched: false }, + { category: new ExerciseCategory('category2', undefined), searched: false }, + ], + }, + achievedScore: SCORE_FILTER, + achievablePoints: POINTS_FILTER, + }; + + fixture.detectChanges(); + }); + + it('should initialize filters properly', () => { + expect(component.categoryFilter).toEqual(component.exerciseFilters?.categoryFilter); + expect(component.typeFilter).toEqual(component.exerciseFilters?.exerciseTypesFilter); + expect(component.difficultyFilter).toEqual(component.exerciseFilters?.difficultyFilter); + expect(component.achievedScore).toEqual(component.exerciseFilters?.achievedScore); + expect(component.achievablePoints).toEqual(component.exerciseFilters?.achievablePoints); + }); + + describe('should close modal', () => { + it('with button in upper right corner on click', () => { + const closeSpy = jest.spyOn(activeModal, 'close'); + const closeModalSpy = jest.spyOn(component, 'closeModal'); + + const closeButton = fixture.debugElement.query(By.css('.btn-close')); + expect(closeButton).not.toBeNull(); + + closeButton.nativeElement.click(); + expect(closeSpy).toHaveBeenCalledOnce(); + expect(closeModalSpy).toHaveBeenCalledOnce(); + }); + + it('with button in lower right corner on click', () => { + const closeSpy = jest.spyOn(activeModal, 'close'); + const closeModalSpy = jest.spyOn(component, 'closeModal'); + + const cancelButton = fixture.debugElement.query(By.css('button[jhiTranslate="entity.action.cancel"]')); + expect(cancelButton).not.toBeNull(); + + cancelButton.nativeElement.click(); + expect(closeSpy).toHaveBeenCalledOnce(); + expect(closeModalSpy).toHaveBeenCalledOnce(); + }); + }); + + describe('select category', () => { + it('should mark a category as selected when category is found', () => { + expect(component.categoryFilter?.options[0].searched).toBeFalse(); // if it is not false in the beginning we do not test anything here + const onSelectItemSpy = jest.spyOn(component, 'onSelectItem'); + + component.model = 'category1'; + // Simulate selecting an item + const event = { + item: component.selectableCategoryOptions[0], + preventDefault: jest.fn(), + }; + component.onSelectItem(event); + fixture.detectChanges(); + + expect(onSelectItemSpy).toHaveBeenCalledOnce(); + expect(component.categoryFilter?.options[0].searched).toBeTrue(); + expect(component.model).toBeUndefined(); // Clear the input field after selection + }); + + it('should not change category filter when no item is provided', () => { + expect(component.categoryFilter?.options[0].searched).toBeFalse(); // if it is not false in the beginning we do not test anything here + const onSelectItemSpy = jest.spyOn(component, 'onSelectItem'); + + component.model = 'categoryThatIsNotDefinedAndSearchedViaEnter'; + const event = { + item: undefined, + preventDefault: jest.fn(), + stopPropagation: jest.fn(), + }; + component.onSelectItem(event); + fixture.detectChanges(); + + expect(onSelectItemSpy).toHaveBeenCalledOnce(); + expect(component.categoryFilter?.options[0].searched).toBeFalse(); + expect(component.model).toBe('categoryThatIsNotDefinedAndSearchedViaEnter'); + }); + }); + + it('should reset all filters when button is clicked', () => { + component.categoryFilter!.options[0].searched = true; + component.categoryFilter!.options[1].searched = true; + component.typeFilter!.options[0].checked = true; + component.typeFilter!.options[1].checked = true; + component.difficultyFilter!.options[0].checked = true; + component.difficultyFilter!.options[1].checked = false; + component.achievablePoints!.filter.selectedMax = 10; + component.achievedScore!.filter.selectedMin = 10; + const resetFilterSpy = jest.spyOn(component, 'clearFilter'); + + const resetButton = fixture.debugElement.query(By.css('span[jhiTranslate="artemisApp.courseOverview.exerciseFilter.clearFilter"]')); + expect(resetButton).not.toBeNull(); + resetButton.nativeElement.click(); + + expect(resetFilterSpy).toHaveBeenCalledOnce(); + expect(component.categoryFilter!.options[0].searched).toBeFalse(); + expect(component.categoryFilter!.options[1].searched).toBeFalse(); + expect(component.typeFilter!.options[0].checked).toBeFalse(); + expect(component.typeFilter!.options[1].checked).toBeFalse(); + expect(component.difficultyFilter!.options[0].checked).toBeFalse(); + expect(component.difficultyFilter!.options[1].checked).toBeFalse(); + expect(component.achievablePoints!.filter.selectedMax).toBe(component.achievablePoints?.filter.generalMax); + expect(component.achievedScore!.filter.selectedMin).toBe(component.achievedScore?.filter.generalMin); + }); + + it('should apply filters, emit the correct sidebar data and close the modal', () => { + component.sidebarData = { + groupByCategory: true, + sidebarType: 'exercise', + groupedData: { + past: { entityData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3] }, + }, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3], + }; + component.categoryFilter!.options[0].searched = true; // must have 'category1' + component.typeFilter!.options[0].checked = true; // must be a programming exercise + component.difficultyFilter!.options[0].checked = true; // must be easy + component.achievablePoints!.filter.selectedMax = 10; + component.achievedScore!.filter.selectedMin = 10; + + const filterAppliedEmitSpy = jest.spyOn(component.filterApplied, 'emit'); + const applyFilterSpy = jest.spyOn(component, 'applyFilter'); + const closeModalSpy = jest.spyOn(component, 'closeModal'); + const applyButton = fixture.debugElement.query(By.css('button[jhiTranslate="artemisApp.courseOverview.exerciseFilter.applyFilter"]')); + expect(applyButton).not.toBeNull(); + applyButton.nativeElement.click(); + + expect(applyFilterSpy).toHaveBeenCalledOnce(); + expect(filterAppliedEmitSpy).toHaveBeenCalledOnce(); + expect(filterAppliedEmitSpy).toHaveBeenCalledWith({ + filteredSidebarData: component.sidebarData, + appliedExerciseFilters: component.exerciseFilters, + isFilterActive: true, + }); + /** only {@link EXERCISE_3} fullfills the filter options and should be emitted in the event */ + expect(component.sidebarData.ungroupedData?.length).toBe(1); + + expect(closeModalSpy).toHaveBeenCalledOnce(); + }); +}); diff --git a/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.helper.spec.ts b/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.helper.spec.ts new file mode 100644 index 000000000000..23ec9d99c4be --- /dev/null +++ b/src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.helper.spec.ts @@ -0,0 +1,333 @@ +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { DifficultyLevel, Exercise, ExerciseType } from 'app/entities/exercise.model'; +import { Result } from 'app/entities/result.model'; +import { StudentParticipation } from 'app/entities/participation/student-participation.model'; +import { SidebarCardElement } from 'app/types/sidebar'; +import { + satisfiesCategoryFilter, + satisfiesDifficultyFilter, + satisfiesFilters, + satisfiesPointsFilter, + satisfiesScoreFilter, +} from 'app/shared/exercise-filter/exercise-filter-modal.helper'; +import { FilterDetails, RangeFilter } from 'app/types/exercise-filter'; + +const EXERCISE_1 = { categories: [new ExerciseCategory('category1', '#691b0b'), new ExerciseCategory('category2', '#1b97ca')], maxPoints: 10, type: ExerciseType.TEXT } as Exercise; +const EXERCISE_2 = { categories: [new ExerciseCategory('category3', '#0d3cc2'), new ExerciseCategory('category4', '#6ae8ac')], maxPoints: 5 } as Exercise; +const EXERCISE_4 = { categories: [new ExerciseCategory('category1', '#691b0b'), new ExerciseCategory('category8', '#1b97ca')], maxPoints: 10 } as Exercise; +const EXERCISE_5 = { maxPoints: 20 } as Exercise; + +const SIDEBAR_CARD_ELEMENT_1 = { + exercise: EXERCISE_1, + type: ExerciseType.TEXT, + difficulty: DifficultyLevel.HARD, + studentParticipation: { results: [{ score: 7.7 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_2 = { + exercise: EXERCISE_2, + type: ExerciseType.PROGRAMMING, + difficulty: DifficultyLevel.EASY, + studentParticipation: { results: [{ score: 82.3 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_4 = { exercise: EXERCISE_4 } as SidebarCardElement; + +/** contains duplicated type and difficulty with {@link SIDEBAR_CARD_ELEMENT_2}*/ +const SIDEBAR_CARD_ELEMENT_5 = { exercise: EXERCISE_5, type: ExerciseType.PROGRAMMING, difficulty: DifficultyLevel.EASY } as SidebarCardElement; + +describe('satisfiesDifficultyFilter', () => { + it('should return true if difficulty filter is undefined', () => { + const difficultyFilter = undefined; + + const resultItemWithDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_1, difficultyFilter); + expect(resultItemWithDifficulty).toBeTrue(); + + const resultItemWithoutDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_4, difficultyFilter); + expect(resultItemWithoutDifficulty).toBeTrue(); + }); + + it('should return true if difficulty filter is []', () => { + const difficultyFilter: DifficultyLevel[] = []; + + const resultItemWithDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_1, difficultyFilter); + expect(resultItemWithDifficulty).toBeTrue(); + + const resultItemWithoutDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_4, difficultyFilter); + expect(resultItemWithoutDifficulty).toBeTrue(); + }); + + it('should return true if difficulty is in difficulty filter', () => { + const difficultyFilter = [DifficultyLevel.HARD]; + + const resultItemWithDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_1, difficultyFilter); + expect(resultItemWithDifficulty).toBeTrue(); + }); + + it('should return false if difficulty is NOT in difficulty filter', () => { + const difficultyFilter = [DifficultyLevel.HARD]; + + const resultItemWithDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_2, difficultyFilter); + expect(resultItemWithDifficulty).toBeFalse(); + + const resultItemWithoutDifficulty = satisfiesDifficultyFilter(SIDEBAR_CARD_ELEMENT_4, difficultyFilter); + expect(resultItemWithoutDifficulty).toBeFalse(); + }); +}); + +describe('satisfiesCategoryFilter', () => { + it('should return true if difficulty filter is []', () => { + const categoryFilter: ExerciseCategory[] = []; + + const resultItemWithCategory = satisfiesCategoryFilter(SIDEBAR_CARD_ELEMENT_1, categoryFilter); + expect(resultItemWithCategory).toBeTrue(); + + const resultItemWithoutCategory = satisfiesCategoryFilter(SIDEBAR_CARD_ELEMENT_5, categoryFilter); + expect(resultItemWithoutCategory).toBeTrue(); + }); + + it('should return true category is included in difficulty filter', () => { + const categoryFilter = [new ExerciseCategory('category1', '#691b0b')]; + + const resultItemWithMatchingCategory = satisfiesCategoryFilter(SIDEBAR_CARD_ELEMENT_1, categoryFilter); + expect(resultItemWithMatchingCategory).toBeTrue(); + }); + + it('should return false if difficulty is NOT in difficulty filter', () => { + const categoryFilter = [new ExerciseCategory('notExistingCategory', '#691b0b')]; + + const resultItemWithCategory = satisfiesCategoryFilter(SIDEBAR_CARD_ELEMENT_1, categoryFilter); + expect(resultItemWithCategory).toBeFalse(); + + const resultItemWithoutCategory = satisfiesCategoryFilter(SIDEBAR_CARD_ELEMENT_5, categoryFilter); + expect(resultItemWithoutCategory).toBeFalse(); + }); +}); + +describe('satisfiesScoreFilter', () => { + it('should return true if score filter is undefined', () => { + const scoreFilter = undefined; + + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_1, true, scoreFilter); + expect(resultItemWithScore).toBeTrue(); + + const resultItemWithoutScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_4, true, scoreFilter); + expect(resultItemWithoutScore).toBeTrue(); + }); + + it('should return true if score filter is not applied', () => { + const scoreFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 0, + selectedMax: 1, + generalMin: 0, + generalMax: 1, + step: 1, + }, + }; + + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_1, false, scoreFilter); + expect(resultItemWithScore).toBeTrue(); + + const resultItemWithoutScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_4, false, scoreFilter); + expect(resultItemWithoutScore).toBeTrue(); + }); + + it('should return true if score is in score filter', () => { + const scoreFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 5, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_1, true, scoreFilter); + expect(resultItemWithScore).toBeTrue(); + }); + + it('should return false if score is NOT in score filter', () => { + const scoreFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 20, + selectedMax: 30, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_1, true, scoreFilter); + expect(resultItemWithScore).toBeFalse(); + + const resultItemWithoutScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_4, true, scoreFilter); + expect(resultItemWithoutScore).toBeFalse(); + }); + + it('should return true if score of participation is not defined (not participated) and lower bound is 0', () => { + const scoreFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 0, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_4, true, scoreFilter); + expect(resultItemWithScore).toBeTrue(); + }); + + it('should return false if score of participation is not defined (not participated) and lower bound is NOT 0', () => { + const scoreFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 1, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + const resultItemWithScore = satisfiesScoreFilter(SIDEBAR_CARD_ELEMENT_4, true, scoreFilter); + expect(resultItemWithScore).toBeFalse(); + }); +}); + +describe('satisfiesPointsFilter', () => { + it('should return true if points filter is undefined', () => { + const pointsFilter = undefined; + + const resultItemWithPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_1, true, pointsFilter); + expect(resultItemWithPoints).toBeTrue(); + + const resultItemWithoutPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_4, true, pointsFilter); + expect(resultItemWithoutPoints).toBeTrue(); + }); + + it('should return true if points filter is not applied', () => { + const pointsFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 0, + selectedMax: 1, + generalMin: 0, + generalMax: 1, + step: 1, + }, + }; + + const resultItemWithPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_1, false, pointsFilter); + expect(resultItemWithPoints).toBeTrue(); + + const resultItemWithoutPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_4, false, pointsFilter); + expect(resultItemWithoutPoints).toBeTrue(); + }); + + it('should return true if points is in points filter', () => { + const pointsFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 9, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + + const resultItemWithPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_1, true, pointsFilter); + expect(resultItemWithPoints).toBeTrue(); + }); + + it('should return false if points is NOT in points filter', () => { + const pointsFilter: RangeFilter = { + isDisplayed: true, + filter: { + selectedMin: 11, + selectedMax: 12, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }; + + const resultItemWithPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_1, true, pointsFilter); + expect(resultItemWithPoints).toBeFalse(); + + const resultItemWithoutPoints = satisfiesPointsFilter(SIDEBAR_CARD_ELEMENT_4, true, pointsFilter); + expect(resultItemWithoutPoints).toBeFalse(); + }); +}); + +describe('satisfiesFilters', () => { + it('should return true if item satisfies filters', () => { + const filter: FilterDetails = { + selectedCategories: [new ExerciseCategory('category1', '#691b0b')], + searchedTypes: [ExerciseType.TEXT], + searchedDifficulties: [DifficultyLevel.HARD], + isScoreFilterApplied: false, + isPointsFilterApplied: false, + achievedScore: { + isDisplayed: true, + filter: { + selectedMin: 5, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }, + achievablePoints: { + isDisplayed: true, + filter: { + selectedMin: 9, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }, + }; + const resultItem = satisfiesFilters(SIDEBAR_CARD_ELEMENT_1, filter); + + expect(resultItem).toBeTrue(); + }); + + it('should return false if item does not satisfy the score filter', () => { + const filter: FilterDetails = { + selectedCategories: [new ExerciseCategory('category1', '#691b0b')], + searchedTypes: [ExerciseType.TEXT], + searchedDifficulties: [DifficultyLevel.HARD], + isScoreFilterApplied: false, + isPointsFilterApplied: false, + achievedScore: { + isDisplayed: true, + filter: { + selectedMin: 69, + selectedMax: 70, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }, + achievablePoints: { + isDisplayed: true, + filter: { + selectedMin: 9, + selectedMax: 10, + generalMin: 0, + generalMax: 80, + step: 1, + }, + }, + }; + const resultItem = satisfiesFilters(SIDEBAR_CARD_ELEMENT_1, filter); + + expect(resultItem).toBeTrue(); + }); +}); diff --git a/src/test/javascript/spec/component/shared/feedback/feedback-modal.component.spec.ts b/src/test/javascript/spec/component/shared/feedback/feedback-modal.component.spec.ts index 98f536f9dcea..f2123eb7f2b4 100644 --- a/src/test/javascript/spec/component/shared/feedback/feedback-modal.component.spec.ts +++ b/src/test/javascript/spec/component/shared/feedback/feedback-modal.component.spec.ts @@ -8,8 +8,8 @@ import { ExerciseType } from 'app/entities/exercise.model'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Result } from 'app/entities/result.model'; import { SubmissionType } from 'app/entities/submission.model'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; diff --git a/src/test/javascript/spec/component/shared/feedback/standalone-feedback.component.spec.ts b/src/test/javascript/spec/component/shared/feedback/standalone-feedback.component.spec.ts index aa2aa73b8ccb..443d3b40e1a2 100644 --- a/src/test/javascript/spec/component/shared/feedback/standalone-feedback.component.spec.ts +++ b/src/test/javascript/spec/component/shared/feedback/standalone-feedback.component.spec.ts @@ -7,7 +7,7 @@ import { StandaloneFeedbackComponent } from 'app/exercises/shared/feedback/stand import { MockComponent, MockProvider } from 'ng-mocks'; import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; describe('StandaloneFeedbackComponent', () => { diff --git a/src/test/javascript/spec/component/shared/reset-repo-button.component.spec.ts b/src/test/javascript/spec/component/shared/reset-repo-button.component.spec.ts index b1bc40312d47..b910e7893f56 100644 --- a/src/test/javascript/spec/component/shared/reset-repo-button.component.spec.ts +++ b/src/test/javascript/spec/component/shared/reset-repo-button.component.spec.ts @@ -10,7 +10,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Subject } from 'rxjs'; import { ResetRepoButtonComponent } from 'app/shared/components/reset-repo-button/reset-repo-button.component'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts index e1e67128974d..bcffb82bc9c1 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts @@ -63,10 +63,6 @@ describe('SidebarAccordionComponent', () => { jest.restoreAllMocks(); }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - it('should toggle collapse state for a group', () => { const groupKey = 'noDate'; component.toggleGroupCategoryCollapse(groupKey); @@ -89,10 +85,20 @@ describe('SidebarAccordionComponent', () => { component.searchValue = 'test'; component.ngOnChanges(); + expect(component.expandAll).toHaveBeenCalledOnce(); + }); + + it('should call expandAll when filter is active', () => { + jest.spyOn(component, 'expandAll'); + + component.isFilterActive = true; + component.ngOnChanges(); + fixture.detectChanges(); - expect(component.expandAll).toHaveBeenCalled(); + expect(component.expandAll).toHaveBeenCalledOnce(); }); + it('should correctly call setStoredCollapseState when searchValue is cleared', () => { const expectedStateAfterClear = component.collapseState; component.searchValue = 'initial value'; @@ -106,7 +112,7 @@ describe('SidebarAccordionComponent', () => { fixture.detectChanges(); - expect(component.setStoredCollapseState).toHaveBeenCalled(); + expect(component.setStoredCollapseState).toHaveBeenCalledOnce(); expect(component.collapseState).toEqual(expectedStateAfterClear); }); diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts index b809b3e3708d..55ad12ae7776 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts @@ -4,25 +4,39 @@ import { SidebarCardMediumComponent } from 'app/shared/sidebar/sidebar-card-medi import { SidebarCardItemComponent } from 'app/shared/sidebar/sidebar-card-item/sidebar-card-item.component'; import { SidebarCardDirective } from 'app/shared/sidebar/sidebar-card.directive'; import { ArtemisTestModule } from '../../../test.module'; -import { DebugElement } from '@angular/core'; import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; - import { By } from '@angular/platform-browser'; -import { MockModule, MockPipe } from 'ng-mocks'; +import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MockRouterLinkDirective } from '../../../helpers/mocks/directive/mock-router-link.directive'; import { RouterModule } from '@angular/router'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { SidebarCardElement, SidebarData } from 'app/types/sidebar'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ExerciseFilterModalComponent } from 'app/shared/exercise-filter/exercise-filter-modal.component'; +import { ExerciseFilterResults } from 'app/types/exercise-filter'; +import { EventEmitter } from '@angular/core'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { ExerciseType } from 'app/entities/exercise.model'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; describe('SidebarComponent', () => { let component: SidebarComponent; let fixture: ComponentFixture; - let debugElement: DebugElement; + let modalService: NgbModal; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(FormsModule), MockModule(ReactiveFormsModule), MockModule(RouterModule)], + imports: [ + ArtemisTestModule, + MockModule(FormsModule), + MockModule(ReactiveFormsModule), + MockModule(RouterModule), + MockDirective(TranslateDirective), + MockComponent(ExerciseFilterModalComponent), + ], declarations: [ SidebarComponent, SidebarCardMediumComponent, @@ -33,18 +47,19 @@ describe('SidebarComponent', () => { MockPipe(ArtemisTranslatePipe), MockRouterLinkDirective, ], + providers: [MockProvider(NgbModal)], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(SidebarComponent); component = fixture.componentInstance; - debugElement = fixture.debugElement; - fixture.detectChanges(); - }); + modalService = TestBed.inject(NgbModal); - it('should create', () => { - expect(component).toBeTruthy(); + component.sidebarData = { + sidebarType: 'default', + } as SidebarData; + fixture.detectChanges(); }); it('should filter sidebar items based on search criteria', () => { @@ -71,11 +86,16 @@ describe('SidebarComponent', () => { groupByCategory: true, ungroupedData: [], }; - fixture.detectChanges(); + component.sidebarDataBeforeFiltering = { + groupByCategory: true, + ungroupedData: [] as SidebarCardElement[], + }; + + const noDataMessageElement = fixture.debugElement.query(By.css('.scrollable-item-content')).nativeElement; - const noDataMessageElement = debugElement.query(By.css('[jhiTranslate$=noDataFound]')); expect(noDataMessageElement).toBeTruthy(); - expect(noDataMessageElement.nativeElement.getAttribute('jhiTranslate')).toBe('artemisApp.courseOverview.general.noDataFound'); + // unfortunately the translation key is cut off in debug mode that seems to be used for testing + expect(noDataMessageElement.getAttribute('ng-reflect-jhi-translate')).toBe('artemisApp.courseOverview.gene'); }); it('should give the correct size for exercises', () => { @@ -109,4 +129,118 @@ describe('SidebarComponent', () => { const size = component.getSize(); expect(size).toBe('M'); }); + + describe('openFilterExercisesLink', () => { + const FILTER_LINK_SELECTOR = '.text-primary a'; + + it('should display the filter link', () => { + component.showFilter = true; + fixture.detectChanges(); + + const filterLink = fixture.debugElement.query(By.css(FILTER_LINK_SELECTOR)); + + expect(filterLink).toBeTruthy(); + }); + + it('should NOT display the filter link when sidebarType is NOT exercise', () => { + const filterLink = fixture.debugElement.query(By.css(FILTER_LINK_SELECTOR)); + + expect(filterLink).toBeFalsy(); + }); + + it('should open modal on click with initialized filters', () => { + component.showFilter = true; + fixture.detectChanges(); + const filterAppliedMock = new EventEmitter(); + const mockReturnValue = { + result: Promise.resolve({}), + componentInstance: { + sidebarData: {}, + exerciseFilters: {}, + filterApplied: filterAppliedMock, + }, + } as NgbModalRef; + const openModalSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockReturnValue); + const openFilterExercisesDialogSpy = jest.spyOn(component, 'openFilterExercisesDialog'); + const initFilterOptionsSpy = jest.spyOn(component, 'initializeFilterOptions'); + + const filterLink = fixture.debugElement.query(By.css(FILTER_LINK_SELECTOR)).nativeElement; + filterLink.click(); + + expect(initFilterOptionsSpy).toHaveBeenCalledOnce(); + expect(openFilterExercisesDialogSpy).toHaveBeenCalledOnce(); + expect(openModalSpy).toHaveBeenCalledWith(ExerciseFilterModalComponent, { animation: true, backdrop: 'static', size: 'lg' }); + }); + }); + + describe('openFilterExercisesDialog', () => { + it('should subscribe to filterApplied from modal', () => { + const filterAppliedEmitter = new EventEmitter(); + const mockModalRef: Partial = { + componentInstance: { + filterApplied: filterAppliedEmitter, + }, + }; + const openSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + const subscribeSpy = jest.spyOn(filterAppliedEmitter, 'subscribe'); + + component.openFilterExercisesDialog(); + + expect(openSpy).toHaveBeenCalledOnce(); + expect(subscribeSpy).toHaveBeenCalledOnce(); + }); + + it('should update variables correctly when filterApplied is emitted', () => { + const filterAppliedEmitter = new EventEmitter(); + const mockModalRef: Partial = { + componentInstance: { + filterApplied: filterAppliedEmitter, + }, + }; + jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + + const mockFilterResults: ExerciseFilterResults = { + filteredSidebarData: { + sidebarType: 'exercise', + groupByCategory: true, + ungroupedData: [{ title: 'test sidebar card element' } as SidebarCardElement], + groupedData: { + testGroup: { + entityData: [{ title: 'test group element' } as SidebarCardElement], + }, + }, + }, + appliedExerciseFilters: { + categoryFilter: { + isDisplayed: true, + options: [ + { + category: new ExerciseCategory('test', undefined), + searched: true, + }, + ], + }, + exerciseTypesFilter: { + isDisplayed: true, + options: [ + { + name: 'testType', + value: ExerciseType.PROGRAMMING, + checked: true, + icon: 'testIcon' as unknown as IconProp, + }, + ], + }, + }, + isFilterActive: true, + }; + + component.openFilterExercisesDialog(); + filterAppliedEmitter.emit(mockFilterResults); + + expect(component.sidebarData).toEqual(mockFilterResults.filteredSidebarData); + expect(component.exerciseFilters).toEqual(mockFilterResults.appliedExerciseFilters); + expect(component.isFilterActive).toBeTrue(); + }); + }); }); diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar.helper.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar.helper.spec.ts new file mode 100644 index 000000000000..a29596e12685 --- /dev/null +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar.helper.spec.ts @@ -0,0 +1,329 @@ +import { + getAchievablePointsAndAchievedScoreFilterOptions, + getExerciseCategoryFilterOptions, + getExerciseDifficultyFilterOptions, + getExerciseTypeFilterOptions, +} from 'app/shared/sidebar/sidebar.helper'; +import { SidebarCardElement, SidebarData } from 'app/types/sidebar'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { DifficultyLevel, Exercise, ExerciseType, getIcon } from 'app/entities/exercise.model'; +import { StudentParticipation } from 'app/entities/participation/student-participation.model'; +import { Result } from 'app/entities/result.model'; + +const EXERCISE_1 = { categories: [new ExerciseCategory('category1', '#691b0b'), new ExerciseCategory('category2', '#1b97ca')], maxPoints: 10 } as Exercise; +const EXERCISE_2 = { categories: [new ExerciseCategory('category3', '#0d3cc2'), new ExerciseCategory('category4', '#6ae8ac')], maxPoints: 5 } as Exercise; +const EXERCISE_3 = { categories: [new ExerciseCategory('category5', '#691b0b')], maxPoints: 2 } as Exercise; + +/** contains 1 duplicate categories and maxPoints with {@link EXERCISE_1} */ +const EXERCISE_4 = { categories: [new ExerciseCategory('category1', '#691b0b'), new ExerciseCategory('category8', '#1b97ca')], maxPoints: 10 } as Exercise; +const EXERCISE_5 = { categories: [] as ExerciseCategory[], maxPoints: 20 } as Exercise; + +const SIDEBAR_CARD_ELEMENT_1 = { + exercise: EXERCISE_1, + type: ExerciseType.TEXT, + difficulty: DifficultyLevel.HARD, + studentParticipation: { results: [{ score: 7.7 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_2 = { + exercise: EXERCISE_2, + type: ExerciseType.PROGRAMMING, + difficulty: DifficultyLevel.EASY, + studentParticipation: { results: [{ score: 82.3 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_3 = { + exercise: EXERCISE_3, + type: ExerciseType.QUIZ, + difficulty: DifficultyLevel.MEDIUM, + studentParticipation: { results: [{ score: 44.5 } as Result] } as StudentParticipation, +} as SidebarCardElement; +const SIDEBAR_CARD_ELEMENT_4 = { exercise: EXERCISE_4 } as SidebarCardElement; + +/** contains duplicated type and difficulty with {@link SIDEBAR_CARD_ELEMENT_2}*/ +const SIDEBAR_CARD_ELEMENT_5 = { exercise: EXERCISE_5, type: ExerciseType.PROGRAMMING, difficulty: DifficultyLevel.EASY } as SidebarCardElement; + +describe('getExerciseCategoryFilterOptions', () => { + it('should return all exercise categories', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + groupedData: { + dueSoon: { + entityData: [], + }, + noDate: { + entityData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2], + }, + past: { + entityData: [SIDEBAR_CARD_ELEMENT_3], + }, + }, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3], + }; + + const exerciseCategories = getExerciseCategoryFilterOptions(sidebarData, undefined); + expect(exerciseCategories).toEqual({ + isDisplayed: true, + options: [ + { category: new ExerciseCategory('category1', '#691b0b'), searched: false }, + { category: new ExerciseCategory('category2', '#1b97ca'), searched: false }, + { category: new ExerciseCategory('category3', '#0d3cc2'), searched: false }, + { category: new ExerciseCategory('category4', '#6ae8ac'), searched: false }, + { category: new ExerciseCategory('category5', '#691b0b'), searched: false }, + ], + }); + }); + + it('should filter duplicates', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_4], + }; + + const exerciseCategories = getExerciseCategoryFilterOptions(sidebarData, undefined); + expect(exerciseCategories).toEqual({ + isDisplayed: true, + options: [ + { category: new ExerciseCategory('category1', '#691b0b'), searched: false }, + { category: new ExerciseCategory('category2', '#1b97ca'), searched: false }, + { category: new ExerciseCategory('category8', '#1b97ca'), searched: false }, + ], + }); + }); + + it('should sort categories alphanumerical', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_1], + }; + + const exerciseCategories = getExerciseCategoryFilterOptions(sidebarData, undefined); + expect(exerciseCategories).toEqual({ + isDisplayed: true, + options: [ + { category: new ExerciseCategory('category1', '#691b0b'), searched: false }, + { category: new ExerciseCategory('category2', '#1b97ca'), searched: false }, + { category: new ExerciseCategory('category3', '#0d3cc2'), searched: false }, + { category: new ExerciseCategory('category4', '#6ae8ac'), searched: false }, + ], + }); + }); + + it('should not be displayed if no categories are available', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_5], + }; + + const exerciseCategories = getExerciseCategoryFilterOptions(sidebarData, undefined); + expect(exerciseCategories).toEqual({ + isDisplayed: false, + options: [], + }); + }); + + it('should directly return if already initialized', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2], + }; + + const exerciseCategories = getExerciseCategoryFilterOptions(sidebarData, { + categoryFilter: { + isDisplayed: false, + options: [], + }, + }); + expect(exerciseCategories).toEqual({ + isDisplayed: false, + options: [], + }); + }); +}); + +describe('getExerciseTypeFilterOptions', () => { + it('should return present exercise types and sort them properly (same order as instructor creation)', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3, SIDEBAR_CARD_ELEMENT_4, SIDEBAR_CARD_ELEMENT_5], + }; + + const exerciseTypesFilter = getExerciseTypeFilterOptions(sidebarData, undefined); + expect(exerciseTypesFilter).toEqual({ + isDisplayed: true, + options: [ + { name: 'artemisApp.courseStatistics.programming', value: ExerciseType.PROGRAMMING, checked: false, icon: getIcon(ExerciseType.PROGRAMMING) }, + { name: 'artemisApp.courseStatistics.quiz', value: ExerciseType.QUIZ, checked: false, icon: getIcon(ExerciseType.QUIZ) }, + { name: 'artemisApp.courseStatistics.text', value: ExerciseType.TEXT, checked: false, icon: getIcon(ExerciseType.TEXT) }, + ], + }); + }); + + it('should not contain duplicates', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_5], + }; + + const exerciseTypesFilter = getExerciseTypeFilterOptions(sidebarData, undefined); + expect(exerciseTypesFilter).toEqual({ + isDisplayed: true, + options: [ + { name: 'artemisApp.courseStatistics.programming', value: ExerciseType.PROGRAMMING, checked: false, icon: getIcon(ExerciseType.PROGRAMMING) }, + { name: 'artemisApp.courseStatistics.text', value: ExerciseType.TEXT, checked: false, icon: getIcon(ExerciseType.TEXT) }, + ], + }); + }); + + it('should directly return if already initialized', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2], + }; + + const exerciseTypesFilter = getExerciseTypeFilterOptions(sidebarData, { + exerciseTypesFilter: { + isDisplayed: false, + options: [], + }, + }); + expect(exerciseTypesFilter).toEqual({ + isDisplayed: false, + options: [], + }); + }); +}); + +describe('getExerciseDifficultyFilterOptions', () => { + it('should return present exercise difficulties and sort them ascending', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3, SIDEBAR_CARD_ELEMENT_4, SIDEBAR_CARD_ELEMENT_5], + }; + + const difficultyFilter = getExerciseDifficultyFilterOptions(sidebarData, undefined); + expect(difficultyFilter).toEqual({ + isDisplayed: true, + options: [ + { name: 'artemisApp.exercise.easy', value: DifficultyLevel.EASY, checked: false }, + { name: 'artemisApp.exercise.medium', value: DifficultyLevel.MEDIUM, checked: false }, + { name: 'artemisApp.exercise.hard', value: DifficultyLevel.HARD, checked: false }, + ], + }); + }); + + it('should not contain duplicates', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_5], + }; + + const difficultyFilter = getExerciseDifficultyFilterOptions(sidebarData, undefined); + expect(difficultyFilter).toEqual({ + isDisplayed: true, + options: [ + { name: 'artemisApp.exercise.easy', value: DifficultyLevel.EASY, checked: false }, + { name: 'artemisApp.exercise.hard', value: DifficultyLevel.HARD, checked: false }, + ], + }); + }); + + it('should directly return if already initialized', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2], + }; + + const difficultyFilter = getExerciseDifficultyFilterOptions(sidebarData, { + difficultyFilter: { + isDisplayed: false, + options: [], + }, + }); + expect(difficultyFilter).toEqual({ + isDisplayed: false, + options: [], + }); + }); +}); + +describe('getAchievablePointsAndAchievedScoreFilterOptions', () => { + const expectedFilterForFirstThreePresentExercises = { + achievablePoints: { + isDisplayed: true, + filter: { + generalMin: 2, + generalMax: 10, + selectedMin: 2, + selectedMax: 10, + step: 1, + }, + }, + achievedScore: { + isDisplayed: true, + filter: { + generalMax: 85, + generalMin: 5, + selectedMax: 85, + selectedMin: 5, + step: 5, + }, + }, + }; + + it('should return present exercise point and score range', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1, SIDEBAR_CARD_ELEMENT_2, SIDEBAR_CARD_ELEMENT_3], + }; + + const scoreAndPointsFilterOptions = getAchievablePointsAndAchievedScoreFilterOptions(sidebarData, undefined); + expect(scoreAndPointsFilterOptions).toEqual(expectedFilterForFirstThreePresentExercises); + }); + + it('should set scores filter to not displayed if no scores are present', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_4, SIDEBAR_CARD_ELEMENT_5], + }; + + const scoreAndPointsFilterOptions = getAchievablePointsAndAchievedScoreFilterOptions(sidebarData, undefined); + + expect(scoreAndPointsFilterOptions).toEqual({ + achievablePoints: { + isDisplayed: true, + filter: { + generalMax: 20, + generalMin: 10, + selectedMax: 20, + selectedMin: 10, + step: 1, + }, + }, + achievedScore: { + isDisplayed: false, + filter: { + generalMax: -Infinity, + generalMin: Infinity, + selectedMax: -Infinity, + selectedMin: Infinity, + step: 1, + }, + }, + }); + }); + + it('should directly return if already initialized and filters are not applied', () => { + const sidebarData: SidebarData = { + groupByCategory: true, + ungroupedData: [SIDEBAR_CARD_ELEMENT_1], + }; + + const scoreAndPointsFilterOptions = getAchievablePointsAndAchievedScoreFilterOptions(sidebarData, { + achievablePoints: expectedFilterForFirstThreePresentExercises.achievablePoints, + achievedScore: expectedFilterForFirstThreePresentExercises.achievedScore, + }); + expect(scoreAndPointsFilterOptions).toEqual({ + achievablePoints: expectedFilterForFirstThreePresentExercises.achievablePoints, + achievedScore: expectedFilterForFirstThreePresentExercises.achievedScore, + }); + }); +}); diff --git a/src/test/javascript/spec/component/shared/start-practice-mode-button.component.spec.ts b/src/test/javascript/spec/component/shared/start-practice-mode-button.component.spec.ts index d7ac41fb943a..3df720eeefff 100644 --- a/src/test/javascript/spec/component/shared/start-practice-mode-button.component.spec.ts +++ b/src/test/javascript/spec/component/shared/start-practice-mode-button.component.spec.ts @@ -12,7 +12,7 @@ import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle import { StartPracticeModeButtonComponent } from 'app/shared/components/start-practice-mode-button/start-practice-mode-button.component'; import { ExerciseType } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { InitializationState } from 'app/entities/participation/participation.model'; import { Subject } from 'rxjs'; import dayjs from 'dayjs/esm'; diff --git a/src/test/javascript/spec/component/shared/submission-policy-update.component.spec.ts b/src/test/javascript/spec/component/shared/submission-policy-update.component.spec.ts index f416514a047b..05f3eb734dee 100644 --- a/src/test/javascript/spec/component/shared/submission-policy-update.component.spec.ts +++ b/src/test/javascript/spec/component/shared/submission-policy-update.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { LockRepositoryPolicy, SubmissionPenaltyPolicy, SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { SubmissionPolicyUpdateComponent } from 'app/exercises/shared/submission-policy/submission-policy-update.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; diff --git a/src/test/javascript/spec/component/shared/updating-result.component.spec.ts b/src/test/javascript/spec/component/shared/updating-result.component.spec.ts index afa519d2640b..9b8c8b9435e9 100644 --- a/src/test/javascript/spec/component/shared/updating-result.component.spec.ts +++ b/src/test/javascript/spec/component/shared/updating-result.component.spec.ts @@ -8,7 +8,7 @@ import { ProgrammingSubmissionService, ProgrammingSubmissionState } from 'app/ex import { MockProgrammingSubmissionService } from '../../helpers/mocks/service/mock-programming-submission.service'; import { triggerChanges } from '../../helpers/utils/general.utils'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; import { ResultComponent } from 'app/exercises/shared/result/result.component'; import { Result } from 'app/entities/result.model'; diff --git a/src/test/javascript/spec/component/statistics/statistics-average-score-graph.component.spec.ts b/src/test/javascript/spec/component/statistics/statistics-average-score-graph.component.spec.ts index e850210ee35e..2626dffc45e1 100644 --- a/src/test/javascript/spec/component/statistics/statistics-average-score-graph.component.spec.ts +++ b/src/test/javascript/spec/component/statistics/statistics-average-score-graph.component.spec.ts @@ -12,6 +12,7 @@ import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import { ChartExerciseTypeFilter } from 'app/shared/chart/chart-exercise-type-filter'; import { ChartCategoryFilter } from 'app/shared/chart/chart-category-filter'; import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; describe('StatisticsAverageScoreGraphComponent', () => { let fixture: ComponentFixture; @@ -28,35 +29,35 @@ describe('StatisticsAverageScoreGraphComponent', () => { exerciseName: 'FacadePattern', averageScore: 0, exerciseType: ExerciseType.TEXT, - categories: [{ color: '#347aeb', category: 'structural pattern' }], + categories: [new ExerciseCategory('structural pattern', '#347aeb')], }; const exercise2 = { exerciseId: 2, exerciseName: 'BridgePattern', averageScore: 20, exerciseType: ExerciseType.PROGRAMMING, - categories: [{ color: '#347aeb', category: 'structural pattern' }], + categories: [new ExerciseCategory('structural pattern', '#347aeb')], }; const exercise3 = { exerciseId: 3, exerciseName: 'VisitorPattern', averageScore: 25, exerciseType: ExerciseType.FILE_UPLOAD, - categories: [{ color: '#c034eb', category: 'behavioral pattern' }], + categories: [new ExerciseCategory('behavioral pattern', '#c034eb')], }; const exercise4 = { exerciseId: 4, exerciseName: 'AdapterPattern', averageScore: 35, exerciseType: ExerciseType.QUIZ, - categories: [{ color: '#347aeb', category: 'structural pattern' }], + categories: [new ExerciseCategory('structural pattern', '#347aeb')], }; const exercise5 = { exerciseId: 5, exerciseName: 'ProxyPattern', averageScore: 40, exerciseType: ExerciseType.MODELING, - categories: [{ color: '#347aeb', category: 'structural pattern' }], + categories: [new ExerciseCategory('structural pattern', '#347aeb')], }; const exercise6 = { exerciseId: 6, exerciseName: 'BuilderPattern', averageScore: 50, exerciseType: ExerciseType.QUIZ }; const exercise7 = { exerciseId: 7, exerciseName: 'BehaviouralPattern', averageScore: 55, exerciseType: ExerciseType.PROGRAMMING }; diff --git a/src/test/javascript/spec/component/team/team-exercise-search.component.spec.ts b/src/test/javascript/spec/component/team/team-exercise-search.component.spec.ts index 156a40652c68..1083ee4d31fc 100644 --- a/src/test/javascript/spec/component/team/team-exercise-search.component.spec.ts +++ b/src/test/javascript/spec/component/team/team-exercise-search.component.spec.ts @@ -4,7 +4,7 @@ import dayjs from 'dayjs/esm'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TeamExerciseSearchComponent } from 'app/exercises/shared/team/team-exercise-search/team-exercise-search.component'; import { MockCourseManagementService } from '../../helpers/mocks/service/mock-course-management.service'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; diff --git a/src/test/javascript/spec/component/text-editor/text-editor.component.spec.ts b/src/test/javascript/spec/component/text-editor/text-editor.component.spec.ts index 6c60b40ad95e..6bfb8cf13d4f 100644 --- a/src/test/javascript/spec/component/text-editor/text-editor.component.spec.ts +++ b/src/test/javascript/spec/component/text-editor/text-editor.component.spec.ts @@ -16,12 +16,12 @@ import { TextResultComponent } from 'app/exercises/text/participate/text-result/ import { SubmissionResultStatusComponent } from 'app/overview/submission-result-status.component'; import { TextEditorComponent } from 'app/exercises/text/participate/text-editor.component'; import { textEditorRoute } from 'app/exercises/text/participate/text-editor.route'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ButtonComponent } from 'app/shared/components/button.component'; import { Result } from 'app/entities/result.model'; import { ComplaintsFormComponent } from 'app/complaints/form/complaints-form.component'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { MockTextSubmissionService } from '../../helpers/mocks/service/mock-text-submission.service'; import { Language } from 'app/entities/course.model'; diff --git a/src/test/javascript/spec/component/text-exercise/example-text-submission.component.spec.ts b/src/test/javascript/spec/component/text-exercise/example-text-submission.component.spec.ts index 355bed4320fa..28bd19ff7ed4 100644 --- a/src/test/javascript/spec/component/text-exercise/example-text-submission.component.spec.ts +++ b/src/test/javascript/spec/component/text-exercise/example-text-submission.component.spec.ts @@ -8,9 +8,9 @@ import { AssessmentInstructionsComponent } from 'app/assessment/assessment-instr import { ExampleSubmission } from 'app/entities/example-submission.model'; import { Feedback, FeedbackCorrectionErrorType } from 'app/entities/feedback.model'; import { Result } from 'app/entities/result.model'; -import { TextBlock } from 'app/entities/text-block.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { TutorParticipationService } from 'app/exercises/shared/dashboards/tutor/tutor-participation.service'; import { ExampleSubmissionService } from 'app/exercises/shared/example-submission/example-submission.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; @@ -27,7 +27,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { of, throwError } from 'rxjs'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { ArtemisTestModule } from '../../test.module'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { UnreferencedFeedbackComponent } from 'app/exercises/shared/unreferenced-feedback/unreferenced-feedback.component'; import { AlertService } from 'app/core/util/alert.service'; import { DebugElement } from '@angular/core'; diff --git a/src/test/javascript/spec/component/text-exercise/text-exercise-detail.component.spec.ts b/src/test/javascript/spec/component/text-exercise/text-exercise-detail.component.spec.ts index 8289a221fc91..b95a4cd7242a 100644 --- a/src/test/javascript/spec/component/text-exercise/text-exercise-detail.component.spec.ts +++ b/src/test/javascript/spec/component/text-exercise/text-exercise-detail.component.spec.ts @@ -6,7 +6,7 @@ import { ArtemisTestModule } from '../../test.module'; import { TextExerciseDetailComponent } from 'app/exercises/text/manage/text-exercise/text-exercise-detail.component'; import { Course } from 'app/entities/course.model'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { MockActivatedRoute } from '../../helpers/mocks/activated-route/mock-activated-route'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/test/javascript/spec/component/text-exercise/text-exercise-row-buttons.component.spec.ts b/src/test/javascript/spec/component/text-exercise/text-exercise-row-buttons.component.spec.ts index 53a691f7182d..acbd27f05fb4 100644 --- a/src/test/javascript/spec/component/text-exercise/text-exercise-row-buttons.component.spec.ts +++ b/src/test/javascript/spec/component/text-exercise/text-exercise-row-buttons.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { ArtemisTestModule } from '../../test.module'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextExerciseRowButtonsComponent } from 'app/exercises/text/manage/text-exercise/text-exercise-row-buttons.component'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; import { EventManager } from 'app/core/util/event-manager.service'; diff --git a/src/test/javascript/spec/component/text-exercise/text-exercise-update.component.spec.ts b/src/test/javascript/spec/component/text-exercise/text-exercise-update.component.spec.ts index aa0f2c889832..78fea1728c50 100644 --- a/src/test/javascript/spec/component/text-exercise/text-exercise-update.component.spec.ts +++ b/src/test/javascript/spec/component/text-exercise/text-exercise-update.component.spec.ts @@ -5,7 +5,7 @@ import { ActivatedRoute, UrlSegment } from '@angular/router'; import { ArtemisTestModule } from '../../test.module'; import { TextExerciseUpdateComponent } from 'app/exercises/text/manage/text-exercise/text-exercise-update.component'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; @@ -13,7 +13,7 @@ import { MockActivatedRoute } from '../../helpers/mocks/activated-route/mock-act import { Course } from 'app/entities/course.model'; import dayjs from 'dayjs/esm'; import { Subject, of, throwError } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { TranslateService } from '@ngx-translate/core'; import { MockProvider } from 'ng-mocks'; import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal.service'; @@ -23,6 +23,7 @@ import { NgModel } from '@angular/forms'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; import { ExerciseUpdatePlagiarismComponent } from 'app/exercises/shared/plagiarism/exercise-update-plagiarism/exercise-update-plagiarism.component'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; +import { ExerciseCategory } from 'app/entities/exercise-category.model'; describe('TextExercise Management Update Component', () => { let comp: TextExerciseUpdateComponent; @@ -363,7 +364,7 @@ describe('TextExercise Management Update Component', () => { it('should updateCategories properly by making category available for selection again when removing it', () => { comp.textExercise = new TextExercise(undefined, undefined); comp.exerciseCategories = []; - const newCategories = [{ category: 'Easy' }, { category: 'Hard' }]; + const newCategories = [new ExerciseCategory('Easy', undefined), new ExerciseCategory('Hard', undefined)]; comp.updateCategories(newCategories); diff --git a/src/test/javascript/spec/component/text-exercise/text-exercise.component.spec.ts b/src/test/javascript/spec/component/text-exercise/text-exercise.component.spec.ts index 2f47ef5e53c6..771035e33ff7 100644 --- a/src/test/javascript/spec/component/text-exercise/text-exercise.component.spec.ts +++ b/src/test/javascript/spec/component/text-exercise/text-exercise.component.spec.ts @@ -8,7 +8,7 @@ import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ArtemisTestModule } from '../../test.module'; import { TextExerciseComponent } from 'app/exercises/text/manage/text-exercise/text-exercise.component'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { Course } from 'app/entities/course.model'; diff --git a/src/test/javascript/spec/component/text-result/text-result.component.spec.ts b/src/test/javascript/spec/component/text-result/text-result.component.spec.ts index 579efafdff29..8fd19cfd4d3b 100644 --- a/src/test/javascript/spec/component/text-result/text-result.component.spec.ts +++ b/src/test/javascript/spec/component/text-result/text-result.component.spec.ts @@ -9,9 +9,9 @@ import { TextResultComponent } from 'app/exercises/text/participate/text-result/ import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { Result } from 'app/entities/result.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Feedback } from 'app/entities/feedback.model'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { TextResultBlock } from 'app/exercises/text/participate/text-result/text-result-block'; import { GradingInstruction } from 'app/exercises/shared/structured-grading-criterion/grading-instruction.model'; import { faCheck, faCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/test/javascript/spec/component/text-submission-assessment/manual-textblock-selection.component.spec.ts b/src/test/javascript/spec/component/text-submission-assessment/manual-textblock-selection.component.spec.ts index bf1801eb0362..5887bcf83e4e 100644 --- a/src/test/javascript/spec/component/text-submission-assessment/manual-textblock-selection.component.spec.ts +++ b/src/test/javascript/spec/component/text-submission-assessment/manual-textblock-selection.component.spec.ts @@ -4,11 +4,11 @@ import { ManualTextblockSelectionComponent } from 'app/exercises/text/assess/man import { TextblockAssessmentCardComponent } from 'app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component'; import { MockComponent, MockProvider } from 'ng-mocks'; import { By } from '@angular/platform-browser'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { ManualTextSelectionComponent, wordSelection } from 'app/exercises/text/shared/manual-text-selection/manual-text-selection.component'; import { SubmissionExerciseType, SubmissionType } from 'app/entities/submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlock } from 'app/entities/text-block.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlock } from 'app/entities/text/text-block.model'; import { TextSelectDirective } from 'app/exercises/text/shared/text-select.directive'; describe('ManualTextblockSelectionComponent', () => { diff --git a/src/test/javascript/spec/component/text-submission-assessment/text-assessment-area.component.spec.ts b/src/test/javascript/spec/component/text-submission-assessment/text-assessment-area.component.spec.ts index 1ddc5bf27049..27816633072e 100644 --- a/src/test/javascript/spec/component/text-submission-assessment/text-assessment-area.component.spec.ts +++ b/src/test/javascript/spec/component/text-submission-assessment/text-assessment-area.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TextAssessmentAreaComponent } from 'app/exercises/text/assess/text-assessment-area/text-assessment-area.component'; import { ArtemisTestModule } from '../../test.module'; import { TextblockAssessmentCardComponent } from 'app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { By } from '@angular/platform-browser'; import { MockComponent, MockDirective } from 'ng-mocks'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; diff --git a/src/test/javascript/spec/component/text-submission-assessment/text-submission-assessment.component.spec.ts b/src/test/javascript/spec/component/text-submission-assessment/text-submission-assessment.component.spec.ts index e1027d236097..1a550e93428c 100644 --- a/src/test/javascript/spec/component/text-submission-assessment/text-submission-assessment.component.spec.ts +++ b/src/test/javascript/spec/component/text-submission-assessment/text-submission-assessment.component.spec.ts @@ -10,10 +10,10 @@ import { TextblockAssessmentCardComponent } from 'app/exercises/text/assess/text import { TextblockFeedbackEditorComponent } from 'app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component'; import { ExerciseType } from 'app/entities/exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ParticipationType } from 'app/entities/participation/participation.model'; import { SubmissionExerciseType, SubmissionType, getLatestSubmissionResult } from 'app/entities/submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; @@ -22,7 +22,7 @@ import { ConfirmIconComponent } from 'app/shared/confirm-icon/confirm-icon.compo import { Course } from 'app/entities/course.model'; import { ManualTextblockSelectionComponent } from 'app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; -import { TextBlock, TextBlockType } from 'app/entities/text-block.model'; +import { TextBlock, TextBlockType } from 'app/entities/text/text-block.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; import { AlertService } from 'app/core/util/alert.service'; @@ -45,7 +45,7 @@ import { AssessmentAfterComplaint } from 'app/complaints/complaints-for-tutor/co import { TextAssessmentBaseComponent } from 'app/exercises/text/assess/text-assessment-base.component'; import { AthenaService } from 'app/assessment/athena.service'; import { MockAthenaService } from '../../helpers/mocks/service/mock-athena-service'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { TranslateDirective } from 'app/shared/language/translate.directive'; describe('TextSubmissionAssessmentComponent', () => { diff --git a/src/test/javascript/spec/component/text-submission-assessment/textblock-assessment-card.component.spec.ts b/src/test/javascript/spec/component/text-submission-assessment/textblock-assessment-card.component.spec.ts index a11f7b7f6692..4cce34baf424 100644 --- a/src/test/javascript/spec/component/text-submission-assessment/textblock-assessment-card.component.spec.ts +++ b/src/test/javascript/spec/component/text-submission-assessment/textblock-assessment-card.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; import { TextblockAssessmentCardComponent } from 'app/exercises/text/assess/textblock-assessment-card/textblock-assessment-card.component'; import { TextblockFeedbackEditorComponent } from 'app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { By } from '@angular/platform-browser'; import { MockComponent, MockDirective, MockProvider } from 'ng-mocks'; import { FaLayersComponent } from '@fortawesome/angular-fontawesome'; @@ -10,8 +10,8 @@ import { GradingInstruction } from 'app/exercises/shared/structured-grading-crit import { AssessmentCorrectionRoundBadgeComponent } from 'app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component'; import { TranslatePipeMock } from '../../helpers/mocks/service/mock-translate.service'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { StructuredGradingCriterionService } from 'app/exercises/shared/structured-grading-criterion/structured-grading-criterion.service'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; diff --git a/src/test/javascript/spec/component/text-submission-assessment/textblock-feedback-editor.component.spec.ts b/src/test/javascript/spec/component/text-submission-assessment/textblock-feedback-editor.component.spec.ts index 6c08b06ec59a..2405cc4cfd97 100644 --- a/src/test/javascript/spec/component/text-submission-assessment/textblock-feedback-editor.component.spec.ts +++ b/src/test/javascript/spec/component/text-submission-assessment/textblock-feedback-editor.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; import { TextblockFeedbackEditorComponent } from 'app/exercises/text/assess/textblock-feedback-editor/textblock-feedback-editor.component'; import { Feedback, FeedbackCorrectionErrorType, FeedbackType } from 'app/entities/feedback.model'; -import { TextBlock, TextBlockType } from 'app/entities/text-block.model'; +import { TextBlock, TextBlockType } from 'app/entities/text/text-block.model'; import { ConfirmIconComponent } from 'app/shared/confirm-icon/confirm-icon.component'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; @@ -16,7 +16,7 @@ import { MockNgbModalService } from '../../helpers/mocks/service/mock-ngb-modal. import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockTranslateService, TranslateTestingModule } from '../../helpers/mocks/service/mock-translate.service'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { NgModel } from '@angular/forms'; import { TranslateDirective } from 'app/shared/language/translate.directive'; import { TextblockFeedbackDropdownComponent } from 'app/exercises/text/assess/textblock-feedback-editor/dropdown/textblock-feedback-dropdown.component'; diff --git a/src/test/javascript/spec/component/text/manual-text-selection.component.spec.ts b/src/test/javascript/spec/component/text/manual-text-selection.component.spec.ts index db879a19b482..478418b4d8a2 100644 --- a/src/test/javascript/spec/component/text/manual-text-selection.component.spec.ts +++ b/src/test/javascript/spec/component/text/manual-text-selection.component.spec.ts @@ -1,15 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; import { ManualTextSelectionComponent } from 'app/exercises/text/shared/manual-text-selection/manual-text-selection.component'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlock, TextBlockType } from 'app/entities/text-block.model'; +import { TextBlock, TextBlockType } from 'app/entities/text/text-block.model'; import { MockProvider } from 'ng-mocks'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { ActivatedRoute } from '@angular/router'; import { SubmissionExerciseType, SubmissionType } from 'app/entities/submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { TextBlockRefGroup } from 'app/exercises/text/assess/manual-textblock-selection/manual-textblock-selection.component'; describe('ManualTextSelectionComponent', () => { diff --git a/src/test/javascript/spec/component/tutor-leaderboard/tutor-leaderboard.component.spec.ts b/src/test/javascript/spec/component/tutor-leaderboard/tutor-leaderboard.component.spec.ts index 95b4a23b2f3a..d8ca84cd934d 100644 --- a/src/test/javascript/spec/component/tutor-leaderboard/tutor-leaderboard.component.spec.ts +++ b/src/test/javascript/spec/component/tutor-leaderboard/tutor-leaderboard.component.spec.ts @@ -11,7 +11,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { Course } from 'app/entities/course.model'; import { Exercise } from 'app/entities/exercise.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; describe('TutorLeaderboardComponent', () => { diff --git a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts index 7a722cf69505..cf60468091e9 100644 --- a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { MockRouter } from '../../../helpers/mocks/mock-router'; -import { MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { AlertService } from 'app/core/util/alert.service'; import { ActivatedRoute, Router, RouterModule, convertToParamMap } from '@angular/router'; @@ -18,6 +18,7 @@ import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; describe('CourseTutorialGroupsComponent', () => { let fixture: ComponentFixture; @@ -32,7 +33,7 @@ describe('CourseTutorialGroupsComponent', () => { router.navigate.mockImplementation(() => Promise.resolve(true)); TestBed.configureTestingModule({ - imports: [ArtemisTestModule, RouterModule, MockModule(FormsModule), MockModule(ReactiveFormsModule)], + imports: [ArtemisTestModule, RouterModule, MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(TranslateDirective)], declarations: [CourseTutorialGroupsComponent, MockPipe(ArtemisTranslatePipe), SidebarComponent, SearchFilterComponent, MockPipe(SearchFilterPipe)], providers: [ MockProvider(TutorialGroupsService), diff --git a/src/test/javascript/spec/component/utils/exercise.utils.spec.ts b/src/test/javascript/spec/component/utils/exercise.utils.spec.ts index e993f54bce3f..bc08c615408c 100644 --- a/src/test/javascript/spec/component/utils/exercise.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/exercise.utils.spec.ts @@ -1,5 +1,5 @@ import dayjs from 'dayjs/esm'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { areManualResultsAllowed, diff --git a/src/test/javascript/spec/component/utils/programming-exercise.utils.spec.ts b/src/test/javascript/spec/component/utils/programming-exercise.utils.spec.ts index b0ec0a41a247..6556bc8c2ac9 100644 --- a/src/test/javascript/spec/component/utils/programming-exercise.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/programming-exercise.utils.spec.ts @@ -1,6 +1,6 @@ import { SubmissionExerciseType, SubmissionType } from 'app/entities/submission.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Participation, ParticipationType } from 'app/entities/participation/participation.model'; import { createBuildPlanUrl, diff --git a/src/test/javascript/spec/entities/exercise-category.model.spec.ts b/src/test/javascript/spec/entities/exercise-category.model.spec.ts new file mode 100644 index 000000000000..2beba4b62f40 --- /dev/null +++ b/src/test/javascript/spec/entities/exercise-category.model.spec.ts @@ -0,0 +1,49 @@ +import { ExerciseCategory } from 'app/entities/exercise-category.model'; + +describe('ExerciseCategory', () => { + describe('equals', () => { + it('should return true if the two exercise categories are equal', () => { + const exerciseCategory1 = new ExerciseCategory('Category 1', 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 1', 'red'); + + expect(exerciseCategory1.equals(exerciseCategory2)).toBeTruthy(); + }); + + it('should return false if the two exercise categories are not equal', () => { + const exerciseCategory1 = new ExerciseCategory('Category 1', 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 2', 'blue'); + + expect(exerciseCategory1.equals(exerciseCategory2)).toBeFalsy(); + }); + }); + + describe('compare', () => { + it("should return 0 if the two exercise categories' display text is the same", () => { + const exerciseCategory1 = new ExerciseCategory('Category 1', 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 1', 'blue'); + + expect(exerciseCategory1.compare(exerciseCategory2)).toBe(0); + }); + + it("should return -1 if the first exercise category's display text is smaller than the second exercise category's display text", () => { + const exerciseCategory1 = new ExerciseCategory('Category 1', 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 2', 'blue'); + + expect(exerciseCategory1.compare(exerciseCategory2)).toBe(-1); + }); + + it("should return 1 if the first exercise category's display text is larger than the second exercise category's display text", () => { + const exerciseCategory1 = new ExerciseCategory('Category 2', 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 1', 'blue'); + + expect(exerciseCategory1.compare(exerciseCategory2)).toBe(1); + }); + + it('should return -1 if the first exercise category is undefined', () => { + const exerciseCategory1 = new ExerciseCategory(undefined, 'red'); + const exerciseCategory2 = new ExerciseCategory('Category 1', 'blue'); + + expect(exerciseCategory1.compare(exerciseCategory2)).toBe(-1); + }); + }); +}); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-build-plan.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-build-plan.service.ts index 6f921f925cc8..eb4e1c7f8a91 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-build-plan.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-build-plan.service.ts @@ -1,5 +1,5 @@ import { of } from 'rxjs'; -import { BuildPlan } from 'app/entities/build-plan.model'; +import { BuildPlan } from 'app/entities/programming/build-plan.model'; export class MockBuildPlanService { getBuildPlan = (exerciseId: number) => of({}); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-code-editor-build-log.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-code-editor-build-log.service.ts index 6c4fdb3e1a95..75e162c1440c 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-code-editor-build-log.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-code-editor-build-log.service.ts @@ -1,5 +1,5 @@ import { of } from 'rxjs'; -import { BuildLogEntry } from 'app/entities/build-log.model'; +import { BuildLogEntry } from 'app/entities/programming/build-log.model'; import { IBuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; export class MockCodeEditorBuildLogService implements IBuildLogService { diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-course-exercise.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-course-exercise.service.ts index a84c3d6f75b5..f35a7271cc77 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-course-exercise.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-course-exercise.service.ts @@ -1,6 +1,6 @@ import { of } from 'rxjs'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class MockCourseExerciseService { startExercise = () => of({} as StudentParticipation); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts index ebcdaacd8f50..888c7b981944 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts @@ -2,7 +2,7 @@ import { HttpResponse } from '@angular/common/http'; import { User } from 'app/core/user/user.model'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { Course, CourseGroup } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { EntityArrayResponseType } from 'app/course/manage/course-management.service'; export class MockCourseManagementService { diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-exam-checklist.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-exam-checklist.service.ts index ce82ec8995bf..aaca1b66625f 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-exam-checklist.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-exam-checklist.service.ts @@ -1,5 +1,5 @@ -import { Exam } from 'app/entities/exam.model'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { Exam } from 'app/entities/exam/exam.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { of } from 'rxjs'; export class MockExamChecklistService { diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-exam-management.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-exam-management.service.ts index b63e816d8969..face7704e776 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-exam-management.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-exam-management.service.ts @@ -1,6 +1,6 @@ import { Observable, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; export class MockExamManagementService { findAllCurrentAndUpcomingExams() { diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-ide-build-and-test.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-ide-build-and-test.service.ts index a50b88c16229..e61332e5c35c 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-ide-build-and-test.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-ide-build-and-test.service.ts @@ -1,5 +1,5 @@ import { of } from 'rxjs'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; export class MockIdeBuildAndTestService { listenOnBuildOutputAndForwardChanges = (exercise: ProgrammingExercise) => of(); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-grading.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-grading.service.ts index cd93e0dd444c..b48ecc16b192 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-grading.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-grading.service.ts @@ -1,12 +1,12 @@ import { BehaviorSubject, Observable, of } from 'rxjs'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { IProgrammingExerciseGradingService, ProgrammingExerciseTestCaseUpdate, StaticCodeAnalysisCategoryUpdate, } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; -import { StaticCodeAnalysisCategory } from 'app/entities/static-code-analysis-category.model'; -import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { StaticCodeAnalysisCategory } from 'app/entities/programming/static-code-analysis-category.model'; +import { ProgrammingExerciseGradingStatistics } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; export class MockProgrammingExerciseGradingService implements IProgrammingExerciseGradingService { private testCaseSubject = new BehaviorSubject(undefined); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-participation.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-participation.service.ts index 6b0fb6ea0d78..8525ad43dc80 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-participation.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise-participation.service.ts @@ -2,7 +2,7 @@ import { of } from 'rxjs'; import { IProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { Result } from 'app/entities/result.model'; -import { CommitInfo } from 'app/entities/programming-submission.model'; +import { CommitInfo } from 'app/entities/programming/programming-submission.model'; export class MockProgrammingExerciseParticipationService implements IProgrammingExerciseParticipationService { getLatestResultWithFeedback = (participationId: number, withSubmission: boolean) => of({} as Result); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts index d897194809fd..b164cc8e700c 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts @@ -1,7 +1,7 @@ import { of } from 'rxjs'; import { ProgrammingExerciseInstructorRepositoryType } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { Participation } from 'app/entities/participation/participation.model'; -import { ProgrammingLanguage } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; export class MockProgrammingExerciseService { updateProblemStatement = (exerciseId: number, problemStatement: string) => of(); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-team.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-team.service.ts index fa1e00fdfe28..5f830bdf013d 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-team.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-team.service.ts @@ -8,7 +8,7 @@ import { StudentWithTeam } from 'app/entities/team.model'; import { Course } from 'app/entities/course.model'; import { TeamSearchUser } from 'app/entities/team-search-user.model'; import { User } from 'app/core/user/user.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TeamAssignmentConfig } from 'app/entities/team-assignment-config.model'; import { TeamService } from 'app/exercises/shared/team/team.service'; import dayjs from 'dayjs/esm'; diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-text-submission.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-text-submission.service.ts index 99dcd86c9c2d..dcea849439c1 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-text-submission.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-text-submission.service.ts @@ -1,5 +1,5 @@ import { Observable, of } from 'rxjs'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { HttpResponse } from '@angular/common/http'; type EntityResponseType = HttpResponse; diff --git a/src/test/javascript/spec/helpers/sample/iris-sample-data.ts b/src/test/javascript/spec/helpers/sample/iris-sample-data.ts index f82327cfaae8..4e348a67d615 100644 --- a/src/test/javascript/spec/helpers/sample/iris-sample-data.ts +++ b/src/test/javascript/spec/helpers/sample/iris-sample-data.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs/esm'; import { ExerciseType } from 'app/entities/exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { IrisAssistantMessage, IrisSender, IrisUserMessage } from 'app/entities/iris/iris-message.model'; import { IrisMessageContentType, IrisTextMessageContent } from 'app/entities/iris/iris-content-type.model'; import { IrisSession } from 'app/entities/iris/iris-session.model'; diff --git a/src/test/javascript/spec/helpers/sample/metis-sample-data.ts b/src/test/javascript/spec/helpers/sample/metis-sample-data.ts index a6d3e1db5eb0..ce2136c46028 100644 --- a/src/test/javascript/spec/helpers/sample/metis-sample-data.ts +++ b/src/test/javascript/spec/helpers/sample/metis-sample-data.ts @@ -13,7 +13,7 @@ import { Conversation, ConversationType } from 'app/entities/metis/conversation/ import { AttachmentUnit } from 'app/entities/lecture-unit/attachmentUnit.model'; import { Slide } from 'app/entities/lecture-unit/slide.model'; import { Channel, ChannelDTO, ChannelSubType } from 'app/entities/metis/conversation/channel.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model'; diff --git a/src/test/javascript/spec/integration/code-editor/code-editor-container.integration.spec.ts b/src/test/javascript/spec/integration/code-editor/code-editor-container.integration.spec.ts index 637ea5b8baad..d8f9e0bb8126 100644 --- a/src/test/javascript/spec/integration/code-editor/code-editor-container.integration.spec.ts +++ b/src/test/javascript/spec/integration/code-editor/code-editor-container.integration.spec.ts @@ -29,7 +29,7 @@ import { GuidedTourMapping } from 'app/guided-tour/guided-tour-setting.model'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { MockWebsocketService } from '../../helpers/mocks/service/mock-websocket.service'; import { Participation } from 'app/entities/participation/participation.model'; -import { BuildLogEntryArray } from 'app/entities/build-log.model'; +import { BuildLogEntryArray } from 'app/entities/programming/build-log.model'; import { CodeEditorConflictStateService } from 'app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; @@ -41,7 +41,7 @@ import { } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { Feedback } from 'app/entities/feedback.model'; import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { MockActivatedRouteWithSubjects } from '../../helpers/mocks/activated-route/mock-activated-route-with-subjects'; import { MockParticipationWebsocketService } from '../../helpers/mocks/service/mock-participation-websocket.service'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; @@ -51,7 +51,7 @@ import { MockCodeEditorRepositoryFileService } from '../../helpers/mocks/service import { MockCodeEditorBuildLogService } from '../../helpers/mocks/service/mock-code-editor-build-log.service'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; import { omit } from 'lodash-es'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { CodeEditorGridComponent } from 'app/exercises/programming/shared/code-editor/layout/code-editor-grid.component'; import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { CodeEditorActionsComponent } from 'app/exercises/programming/shared/code-editor/actions/code-editor-actions.component'; diff --git a/src/test/javascript/spec/integration/code-editor/code-editor-instructor.integration.spec.ts b/src/test/javascript/spec/integration/code-editor/code-editor-instructor.integration.spec.ts index a793a859f204..294c3d3a9a4e 100644 --- a/src/test/javascript/spec/integration/code-editor/code-editor-instructor.integration.spec.ts +++ b/src/test/javascript/spec/integration/code-editor/code-editor-instructor.integration.spec.ts @@ -27,7 +27,7 @@ import { DomainService } from 'app/exercises/programming/shared/code-editor/serv import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; import { Result } from 'app/entities/result.model'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { MockActivatedRouteWithSubjects } from '../../helpers/mocks/activated-route/mock-activated-route-with-subjects'; diff --git a/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts b/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts index 11036e23a71d..dabf4a7e1d34 100644 --- a/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts +++ b/src/test/javascript/spec/integration/code-editor/code-editor-student.integration.spec.ts @@ -28,7 +28,7 @@ import { } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { Feedback } from 'app/entities/feedback.model'; import { CodeEditorStudentContainerComponent } from 'app/exercises/programming/participate/code-editor-student-container.component'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { MockActivatedRouteWithSubjects } from '../../helpers/mocks/activated-route/mock-activated-route-with-subjects'; import { MockParticipationWebsocketService } from '../../helpers/mocks/service/mock-participation-websocket.service'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; diff --git a/src/test/javascript/spec/service/account.service.spec.ts b/src/test/javascript/spec/service/account.service.spec.ts index caf5c105276b..03bcd9a04a48 100644 --- a/src/test/javascript/spec/service/account.service.spec.ts +++ b/src/test/javascript/spec/service/account.service.spec.ts @@ -14,7 +14,6 @@ import { Course } from 'app/entities/course.model'; import { Exercise } from 'app/entities/exercise.model'; import { Participation } from 'app/entities/participation/participation.model'; import { Team } from 'app/entities/team.model'; -import { MockProfileService } from '../helpers/mocks/service/mock-profile.service'; describe('AccountService', () => { let accountService: AccountService; @@ -53,7 +52,6 @@ describe('AccountService', () => { httpService, new MockWebsocketService(), new MockFeatureToggleService(), - new MockProfileService(), ); getStub = jest.spyOn(httpService, 'get'); postStub = jest.spyOn(httpService, 'post'); @@ -538,17 +536,7 @@ describe('AccountService', () => { fetchStub = jest.spyOn(accountService, 'fetch'); }); - it('should retrieve user if vcs token is missing', () => { - accountService['versionControlAccessTokenRequired'] = true; - user.vcsAccessToken = undefined; - accountService.userIdentity = user; - - accountService.identity(); - expect(fetchStub).toHaveBeenCalledOnce(); - }); - it('should not retrieve user if vcs token is missing but not required', () => { - accountService['versionControlAccessTokenRequired'] = false; user.vcsAccessToken = undefined; accountService.userIdentity = user; @@ -557,7 +545,6 @@ describe('AccountService', () => { }); it('should not retrieve user if vcs token is present', () => { - accountService['versionControlAccessTokenRequired'] = true; user.vcsAccessToken = 'iAmAToken'; accountService.userIdentity = user; diff --git a/src/test/javascript/spec/service/athena.service.spec.ts b/src/test/javascript/spec/service/athena.service.spec.ts index 98de7b17fe9c..099b679ee249 100644 --- a/src/test/javascript/spec/service/athena.service.spec.ts +++ b/src/test/javascript/spec/service/athena.service.spec.ts @@ -8,8 +8,8 @@ import { of } from 'rxjs'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; import { Exercise } from 'app/entities/exercise.model'; import { ModelingFeedbackSuggestion, ProgrammingFeedbackSuggestion, TextFeedbackSuggestion } from 'app/entities/feedback-suggestion.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; diff --git a/src/test/javascript/spec/service/build-log.service.spec.ts b/src/test/javascript/spec/service/build-log.service.spec.ts index 9a8639c3159d..0582e9ff369b 100644 --- a/src/test/javascript/spec/service/build-log.service.spec.ts +++ b/src/test/javascript/spec/service/build-log.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; -import { BuildLogEntry, BuildLogType } from 'app/entities/build-log.model'; +import { BuildLogEntry, BuildLogType } from 'app/entities/programming/build-log.model'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; describe('Build Log Service', () => { diff --git a/src/test/javascript/spec/service/chart-category-filter.spec.ts b/src/test/javascript/spec/service/chart-category-filter.spec.ts index 533442313bc8..aa4a25ed8bbb 100644 --- a/src/test/javascript/spec/service/chart-category-filter.spec.ts +++ b/src/test/javascript/spec/service/chart-category-filter.spec.ts @@ -5,7 +5,7 @@ import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import dayjs from 'dayjs/esm'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { InitializationState } from 'app/entities/participation/participation.model'; diff --git a/src/test/javascript/spec/service/course-exercise.service.spec.ts b/src/test/javascript/spec/service/course-exercise.service.spec.ts index e31154c07dd5..d123760c6d25 100644 --- a/src/test/javascript/spec/service/course-exercise.service.spec.ts +++ b/src/test/javascript/spec/service/course-exercise.service.spec.ts @@ -7,8 +7,8 @@ import { Exercise } from 'app/entities/exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import dayjs from 'dayjs/esm'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { take } from 'rxjs/operators'; diff --git a/src/test/javascript/spec/service/exam-checklist.service.spec.ts b/src/test/javascript/spec/service/exam-checklist.service.spec.ts index 89286e0d0d9c..be298e2651d3 100644 --- a/src/test/javascript/spec/service/exam-checklist.service.spec.ts +++ b/src/test/javascript/spec/service/exam-checklist.service.spec.ts @@ -1,8 +1,8 @@ import { HttpResponse } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { Course } from 'app/entities/course.model'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; -import { Exam } from 'app/entities/exam.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { MockProvider } from 'ng-mocks'; import { of, take } from 'rxjs'; import { ArtemisTestModule } from '../test.module'; diff --git a/src/test/javascript/spec/service/exam-import-paging.service.spec.ts b/src/test/javascript/spec/service/exam-import-paging.service.spec.ts index 34299aa2e1ba..091acf0bc0c0 100644 --- a/src/test/javascript/spec/service/exam-import-paging.service.spec.ts +++ b/src/test/javascript/spec/service/exam-import-paging.service.spec.ts @@ -6,7 +6,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { take } from 'rxjs/operators'; import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamImportPagingService } from 'app/exam/manage/exams/exam-import/exam-import-paging.service'; describe('Exam Import Paging Service', () => { diff --git a/src/test/javascript/spec/service/exam-participation.service.spec.ts b/src/test/javascript/spec/service/exam-participation.service.spec.ts index 8a06fa6654d6..56d0aa53bef8 100644 --- a/src/test/javascript/spec/service/exam-participation.service.spec.ts +++ b/src/test/javascript/spec/service/exam-participation.service.spec.ts @@ -3,7 +3,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { take } from 'rxjs/operators'; import dayjs from 'dayjs/esm'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; @@ -11,10 +11,10 @@ import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.serv import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { ArtemisTestModule } from '../test.module'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Course } from 'app/entities/course.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import { getLatestSubmissionResult } from 'app/entities/submission.model'; import { StudentExamWithGradeDTO, StudentResult } from 'app/exam/exam-scores/exam-score-dtos.model'; diff --git a/src/test/javascript/spec/service/example-submission-import-paging.service.spec.ts b/src/test/javascript/spec/service/example-submission-import-paging.service.spec.ts index 27bb4130c2ab..d1eee71bc343 100644 --- a/src/test/javascript/spec/service/example-submission-import-paging.service.spec.ts +++ b/src/test/javascript/spec/service/example-submission-import-paging.service.spec.ts @@ -8,7 +8,7 @@ import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.serv import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { ExampleSubmissionImportPagingService } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service'; import { Exercise } from 'app/entities/exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; describe('Example Submission Import Paging Service', () => { let service: ExampleSubmissionImportPagingService; diff --git a/src/test/javascript/spec/service/example-submission.service.spec.ts b/src/test/javascript/spec/service/example-submission.service.spec.ts index 506871654bf2..60addda81875 100644 --- a/src/test/javascript/spec/service/example-submission.service.spec.ts +++ b/src/test/javascript/spec/service/example-submission.service.spec.ts @@ -6,7 +6,7 @@ import { ArtemisTestModule } from '../test.module'; import { ExampleSubmissionService } from 'app/exercises/shared/example-submission/example-submission.service'; import { ExampleSubmission } from 'app/entities/example-submission.model'; import { Exercise } from 'app/entities/exercise.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import { Feedback } from 'app/entities/feedback.model'; import { Submission, getLatestSubmissionResult } from 'app/entities/submission.model'; diff --git a/src/test/javascript/spec/service/exercise-hint.service.spec.ts b/src/test/javascript/spec/service/exercise-hint.service.spec.ts index cdff311a6fcc..74a6c5e84bb9 100644 --- a/src/test/javascript/spec/service/exercise-hint.service.spec.ts +++ b/src/test/javascript/spec/service/exercise-hint.service.spec.ts @@ -3,7 +3,7 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { HttpResponse } from '@angular/common/http'; import { take } from 'rxjs/operators'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ExerciseHint } from 'app/entities/hestia/exercise-hint.model'; import { MockExerciseService } from '../helpers/mocks/service/mock-exercise.service'; import { ExerciseHintService } from 'app/exercises/shared/exercise-hint/shared/exercise-hint.service'; diff --git a/src/test/javascript/spec/service/exercise-update-warning.service.spec.ts b/src/test/javascript/spec/service/exercise-update-warning.service.spec.ts index 55ad42c88d49..75be64c26763 100644 --- a/src/test/javascript/spec/service/exercise-update-warning.service.spec.ts +++ b/src/test/javascript/spec/service/exercise-update-warning.service.spec.ts @@ -5,7 +5,7 @@ import { GradingCriterion } from 'app/exercises/shared/structured-grading-criter import { Exercise } from 'app/entities/exercise.model'; import { ExerciseUpdateWarningComponent } from 'app/exercises/shared/exercise-update-warning/exercise-update-warning.component'; import { Component } from '@angular/core'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import dayjs from 'dayjs/esm'; describe('Exercise Update Warning Service', () => { diff --git a/src/test/javascript/spec/service/exercise.service.spec.ts b/src/test/javascript/spec/service/exercise.service.spec.ts index 8e58498bef7a..415078a437e1 100644 --- a/src/test/javascript/spec/service/exercise.service.spec.ts +++ b/src/test/javascript/spec/service/exercise.service.spec.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Exercise, ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; import { InitializationState } from 'app/entities/participation/participation.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import type { EntityResponseType, ExerciseDetailsType } from 'app/exercises/shared/exercise/exercise.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import dayjs from 'dayjs/esm'; @@ -15,7 +15,7 @@ import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.serv import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { MockProvider } from 'ng-mocks'; import { SafeHtml } from '@angular/platform-browser'; diff --git a/src/test/javascript/spec/service/external-submission.service.spec.ts b/src/test/javascript/spec/service/external-submission.service.spec.ts index b3d34c736e50..b7b7c6701e8b 100644 --- a/src/test/javascript/spec/service/external-submission.service.spec.ts +++ b/src/test/javascript/spec/service/external-submission.service.spec.ts @@ -7,7 +7,7 @@ import { Result } from 'app/entities/result.model'; import { User } from 'app/core/user/user.model'; import { EntityResponseType, ResultService } from 'app/exercises/shared/result/result.service'; import dayjs from 'dayjs/esm'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; describe('External Submission Service', () => { let httpMock: HttpTestingController; diff --git a/src/test/javascript/spec/service/feedback/programming-feedback-item.service.spec.ts b/src/test/javascript/spec/service/feedback/programming-feedback-item.service.spec.ts index 9a1d199c32be..89c6a7755334 100644 --- a/src/test/javascript/spec/service/feedback/programming-feedback-item.service.spec.ts +++ b/src/test/javascript/spec/service/feedback/programming-feedback-item.service.spec.ts @@ -1,4 +1,4 @@ -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { FeedbackGroup } from 'app/exercises/shared/feedback/group/feedback-group'; import { ProgrammingFeedbackItemService } from 'app/exercises/shared/feedback/item/programming-feedback-item.service'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER, SUBMISSION_POLICY_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; diff --git a/src/test/javascript/spec/service/modeling-exercise.service.spec.ts b/src/test/javascript/spec/service/modeling-exercise.service.spec.ts index e3227f1b718f..5fdfe0efe181 100644 --- a/src/test/javascript/spec/service/modeling-exercise.service.spec.ts +++ b/src/test/javascript/spec/service/modeling-exercise.service.spec.ts @@ -22,8 +22,8 @@ describe('ModelingExercise Service', () => { let httpMock: HttpTestingController; let elemDefault: ModelingExercise; let plagiarismResult: ModelingPlagiarismResult; - const category = { color: 'red', category: 'testCategory' } as ExerciseCategory; - const categories = [JSON.stringify(category) as ExerciseCategory]; + const category = new ExerciseCategory('testCategory', 'red'); + const categories = [JSON.stringify(category) as unknown as ExerciseCategory] as ExerciseCategory[]; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], diff --git a/src/test/javascript/spec/service/orion/orion-build-and-test.service.spec.ts b/src/test/javascript/spec/service/orion/orion-build-and-test.service.spec.ts index eb6b7f555eff..53394a7bbd27 100644 --- a/src/test/javascript/spec/service/orion/orion-build-and-test.service.spec.ts +++ b/src/test/javascript/spec/service/orion/orion-build-and-test.service.spec.ts @@ -5,13 +5,13 @@ import { MockProgrammingSubmissionService } from '../../helpers/mocks/service/mo import { Result } from 'app/entities/result.model'; import { BehaviorSubject, of } from 'rxjs'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; import { MockParticipationWebsocketService } from '../../helpers/mocks/service/mock-participation-websocket.service'; import { MockCodeEditorBuildLogService } from '../../helpers/mocks/service/mock-code-editor-build-log.service'; import { OrionBuildAndTestService } from 'app/shared/orion/orion-build-and-test.service'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ArtemisTestModule } from '../../test.module'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; diff --git a/src/test/javascript/spec/service/orion/orion-connector.service.spec.ts b/src/test/javascript/spec/service/orion/orion-connector.service.spec.ts index 6fb2c239ff92..5a5f05943019 100644 --- a/src/test/javascript/spec/service/orion/orion-connector.service.spec.ts +++ b/src/test/javascript/spec/service/orion/orion-connector.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { ArtemisTestModule } from '../../test.module'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; diff --git a/src/test/javascript/spec/service/participation.service.spec.ts b/src/test/javascript/spec/service/participation.service.spec.ts index aef90fe10869..0d405cbfb018 100644 --- a/src/test/javascript/spec/service/participation.service.spec.ts +++ b/src/test/javascript/spec/service/participation.service.spec.ts @@ -14,7 +14,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockRouter } from '../helpers/mocks/mock-router'; import { Router } from '@angular/router'; import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; describe('Participation Service', () => { let service: ParticipationService; diff --git a/src/test/javascript/spec/service/plagiarism-cases.service.spec.ts b/src/test/javascript/spec/service/plagiarism-cases.service.spec.ts index d72b46099546..134bdc518fee 100644 --- a/src/test/javascript/spec/service/plagiarism-cases.service.spec.ts +++ b/src/test/javascript/spec/service/plagiarism-cases.service.spec.ts @@ -3,7 +3,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; import { take } from 'rxjs/operators'; import { ExerciseType } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { PlagiarismStatus } from 'app/exercises/shared/plagiarism/types/PlagiarismStatus'; import { PlagiarismComparison } from 'app/exercises/shared/plagiarism/types/PlagiarismComparison'; import { TextSubmissionElement } from 'app/exercises/shared/plagiarism/types/text/TextSubmissionElement'; diff --git a/src/test/javascript/spec/service/profile.service.spec.ts b/src/test/javascript/spec/service/profile.service.spec.ts index 00296fdba322..cc833e61a7f6 100644 --- a/src/test/javascript/spec/service/profile.service.spec.ts +++ b/src/test/javascript/spec/service/profile.service.spec.ts @@ -7,7 +7,7 @@ import { MockRouter } from '../helpers/mocks/mock-router'; import { Router } from '@angular/router'; import { ProfileInfo } from 'app/shared/layouts/profiles/profile-info.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; -import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; +import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { BrowserFingerprintService } from 'app/shared/fingerprint/browser-fingerprint.service'; describe('ProfileService', () => { diff --git a/src/test/javascript/spec/service/programming-exercise-grading.service.spec.ts b/src/test/javascript/spec/service/programming-exercise-grading.service.spec.ts index 5d27d8f339af..6396a81e6dff 100644 --- a/src/test/javascript/spec/service/programming-exercise-grading.service.spec.ts +++ b/src/test/javascript/spec/service/programming-exercise-grading.service.spec.ts @@ -5,7 +5,7 @@ import { MockWebsocketService } from '../helpers/mocks/service/mock-websocket.se import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; import { MockHttpService } from '../helpers/mocks/service/mock-http.service'; -import { ProgrammingExerciseTestCase } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { Result } from 'app/entities/result.model'; import { HttpClient } from '@angular/common/http'; diff --git a/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts b/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts index 9571cadd5392..b45536f41584 100644 --- a/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts +++ b/src/test/javascript/spec/service/programming-exercise-task.service.spec.ts @@ -4,14 +4,14 @@ import { ArtemisTestModule } from '../test.module'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingExerciseGradingService } from 'app/exercises/programming/manage/services/programming-exercise-grading.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; -import { ProgrammingExerciseGradingStatistics, TestCaseStats } from 'app/entities/programming-exercise-test-case-statistics.model'; +import { ProgrammingExerciseGradingStatistics, TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { MockProvider } from 'ng-mocks'; import { MockProgrammingExerciseGradingService } from '../helpers/mocks/service/mock-programming-exercise-grading.service'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; import { ProgrammingExerciseServerSideTask } from 'app/entities/hestia/programming-exercise-task.model'; -import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase, ProgrammingExerciseTestCaseType, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { firstValueFrom, of } from 'rxjs'; describe('ProgrammingExerciseTask Service', () => { diff --git a/src/test/javascript/spec/service/programming-exercise.service.spec.ts b/src/test/javascript/spec/service/programming-exercise.service.spec.ts index 66ca8a1e0d44..70d9a94a1eb7 100644 --- a/src/test/javascript/spec/service/programming-exercise.service.spec.ts +++ b/src/test/javascript/spec/service/programming-exercise.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { take } from 'rxjs/operators'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; @@ -10,7 +10,7 @@ import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.serv import { ArtemisTestModule } from '../test.module'; import dayjs from 'dayjs/esm'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Result } from 'app/entities/result.model'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../helpers/mocks/service/mock-account.service'; @@ -179,6 +179,7 @@ describe('ProgrammingExercise Service', () => { solutionRepositoryUri: 'BBBBBB', templateBuildPlanId: 'BBBBBB', allowOnlineEditor: true, + allowOnlineIde: true, releaseDate: undefined, dueDate: undefined, assessmentDueDate: undefined, @@ -222,6 +223,7 @@ describe('ProgrammingExercise Service', () => { solutionRepositoryUri: 'BBBBBB', templateBuildPlanId: 'BBBBBB', allowOnlineEditor: true, + allowOnlineIde: true, releaseDate: undefined, dueDate: undefined, assessmentDueDate: undefined, diff --git a/src/test/javascript/spec/service/programming-submission.service.spec.ts b/src/test/javascript/spec/service/programming-submission.service.spec.ts index 74e4bf5c7326..6259bcaf65d9 100644 --- a/src/test/javascript/spec/service/programming-submission.service.spec.ts +++ b/src/test/javascript/spec/service/programming-submission.service.spec.ts @@ -10,7 +10,7 @@ import { } from 'app/exercises/programming/participate/programming-submission.service'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import { Result } from 'app/entities/result.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { Submission } from 'app/entities/submission.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { MockParticipationWebsocketService } from '../helpers/mocks/service/mock-participation-websocket.service'; diff --git a/src/test/javascript/spec/service/result.service.spec.ts b/src/test/javascript/spec/service/result.service.spec.ts index 00e6ff3ea277..8afb25f538cd 100644 --- a/src/test/javascript/spec/service/result.service.spec.ts +++ b/src/test/javascript/spec/service/result.service.spec.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; import dayjs from 'dayjs/esm'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { EntityResponseType, ResultService } from 'app/exercises/shared/result/result.service'; import { ResultWithPointsPerGradingCriterion } from 'app/entities/result-with-points-per-grading-criterion.model'; import { Result } from 'app/entities/result.model'; @@ -19,7 +19,7 @@ import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../helpers/mocks/service/mock-account.service'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; import { AssessmentType } from 'app/entities/assessment-type.model'; -import { ProgrammingSubmission } from 'app/entities/programming-submission.model'; +import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { FeedbackType, NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER, diff --git a/src/test/javascript/spec/service/submission-policy.service.spec.ts b/src/test/javascript/spec/service/submission-policy.service.spec.ts index 01ee9af19dba..60c5f139df52 100644 --- a/src/test/javascript/spec/service/submission-policy.service.spec.ts +++ b/src/test/javascript/spec/service/submission-policy.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { SubmissionPolicyService } from 'app/exercises/programming/manage/services/submission-policy.service'; import { LockRepositoryPolicy, SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { take } from 'rxjs/operators'; describe('Submission Policy Service', () => { diff --git a/src/test/javascript/spec/service/submission-version.service.spec.ts b/src/test/javascript/spec/service/submission-version.service.spec.ts index a9f9570f2207..380659aabf58 100644 --- a/src/test/javascript/spec/service/submission-version.service.spec.ts +++ b/src/test/javascript/spec/service/submission-version.service.spec.ts @@ -1,7 +1,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ArtemisTestModule } from '../test.module'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import dayjs from 'dayjs/esm'; import { SubmissionVersionService } from 'app/exercises/shared/submission-version/submission-version.service'; diff --git a/src/test/javascript/spec/service/submission.service.spec.ts b/src/test/javascript/spec/service/submission.service.spec.ts index 3ceaff266c5e..276aa8be2498 100644 --- a/src/test/javascript/spec/service/submission.service.spec.ts +++ b/src/test/javascript/spec/service/submission.service.spec.ts @@ -7,7 +7,7 @@ import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.service'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Result } from 'app/entities/result.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { HttpResponse } from '@angular/common/http'; diff --git a/src/test/javascript/spec/service/text-assessment-analytics.service.spec.ts b/src/test/javascript/spec/service/text-assessment-analytics.service.spec.ts index c7d1a8d53878..111bc06bc06c 100644 --- a/src/test/javascript/spec/service/text-assessment-analytics.service.spec.ts +++ b/src/test/javascript/spec/service/text-assessment-analytics.service.spec.ts @@ -1,9 +1,9 @@ import { TestBed, fakeAsync } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { TextAssessmentEventType } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { FeedbackType } from 'app/entities/feedback.model'; -import { TextBlockType } from 'app/entities/text-block.model'; +import { TextBlockType } from 'app/entities/text/text-block.model'; import { TranslateService } from '@ngx-translate/core'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { MockSyncStorage } from '../helpers/mocks/service/mock-sync-storage.service'; diff --git a/src/test/javascript/spec/service/text-assessment.service.spec.ts b/src/test/javascript/spec/service/text-assessment.service.spec.ts index 4cdfb552a3f9..63ae92c49ffb 100644 --- a/src/test/javascript/spec/service/text-assessment.service.spec.ts +++ b/src/test/javascript/spec/service/text-assessment.service.spec.ts @@ -1,18 +1,18 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { take } from 'rxjs/operators'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; import { Result } from 'app/entities/result.model'; -import { TextAssessmentEvent } from 'app/entities/text-assesment-event.model'; +import { TextAssessmentEvent } from 'app/entities/text/text-assesment-event.model'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../helpers/mocks/service/mock-account.service'; import { ExerciseType } from 'app/entities/exercise.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; -import { TextBlockRef } from 'app/entities/text-block-ref.model'; +import { TextBlockRef } from 'app/entities/text/text-block-ref.model'; import { NewStudentParticipationResolver, StudentParticipationResolver } from 'app/exercises/text/assess/text-submission-assessment.route'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { of } from 'rxjs'; diff --git a/src/test/javascript/spec/service/text-exercise.service.spec.ts b/src/test/javascript/spec/service/text-exercise.service.spec.ts index 675dbd3b8aa0..5557d381a1e9 100644 --- a/src/test/javascript/spec/service/text-exercise.service.spec.ts +++ b/src/test/javascript/spec/service/text-exercise.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { take } from 'rxjs/operators'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { Course } from 'app/entities/course.model'; import { MockTranslateService } from '../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/test/javascript/spec/service/text-submission.service.spec.ts b/src/test/javascript/spec/service/text-submission.service.spec.ts index 3fe80d04c135..d21d44eda72c 100644 --- a/src/test/javascript/spec/service/text-submission.service.spec.ts +++ b/src/test/javascript/spec/service/text-submission.service.spec.ts @@ -2,7 +2,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { take } from 'rxjs/operators'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { AccountService } from 'app/core/auth/account.service'; import { MockAccountService } from '../helpers/mocks/service/mock-account.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; diff --git a/src/test/javascript/spec/util/shared/utils.spec.ts b/src/test/javascript/spec/util/shared/utils.spec.ts index 296e7ba1fabc..b20604e27615 100644 --- a/src/test/javascript/spec/util/shared/utils.spec.ts +++ b/src/test/javascript/spec/util/shared/utils.spec.ts @@ -4,6 +4,7 @@ import { isExamExercise, round, roundScorePercentSpecifiedByCourseSettings, + roundToNextMultiple, roundValueSpecifiedByCourseSettings, stringifyIgnoringFields, } from 'app/shared/util/utils'; @@ -119,3 +120,25 @@ describe('isExamExercise', () => { expect(isExamExerciseResult).toBeFalse(); }); }); + +describe('roundUpToNextMultiple', () => { + it('should round up to multiple of 5 if value is closer to lower multiple', () => { + expect(roundToNextMultiple(21, 5, true)).toBe(25); + }); + + it('should round up to multiple of 5 if value is right underneath next multiple', () => { + expect(roundToNextMultiple(24.8, 5, true)).toBe(25); + }); + + it('should round down to multiple of 5 if value is over next multiple', () => { + expect(roundToNextMultiple(24.8, 5, false)).toBe(20); + }); + + it('should return value is it is a multiple', () => { + expect(roundToNextMultiple(25, 5, true)).toBe(25); + }); + + it('should round up to multiple of 1', () => { + expect(roundToNextMultiple(8.2, 1, true)).toBe(9); + }); +}); diff --git a/src/test/cypress/certs/Dockerfile b/src/test/playwright/certs/Dockerfile similarity index 100% rename from src/test/cypress/certs/Dockerfile rename to src/test/playwright/certs/Dockerfile diff --git a/src/test/cypress/certs/README.md b/src/test/playwright/certs/README.md similarity index 74% rename from src/test/cypress/certs/README.md rename to src/test/playwright/certs/README.md index b95146cc4a42..55e8f826bae7 100644 --- a/src/test/cypress/certs/README.md +++ b/src/test/playwright/certs/README.md @@ -1,7 +1,7 @@ -# Cypress: Generate Client Certificates +# Playwright: Generate Client Certificates -[Cypress allows to use self-signed client certificates](https://docs.cypress.io/guides/references/client-certificates -) if you also provide a certificate authority. +Playwright currently lacks support for client certificates. To bypass certificate issues, we are ignoring HTTPS errors. +Once Playwright supports client certificates, follow these steps to generate the necessary client certificates. In order to generate these certificates we use the tool [mkcert](https://github.com/FiloSottile/mkcert), instead of openssl for instance, as it's easy to use. @@ -10,7 +10,7 @@ instead of openssl for instance, as it's easy to use. The following steps show how to generate new client certificates and the CA files: ```bash -cd ./src/test/cypress/certs +cd ./src/test/playwright/certs docker run --rm -v ${PWD}:/certs $(docker build -q . ) /certs/generate-certs.sh artemis-nginx artemis.example localhost 127.0.0.1 ::1 ``` @@ -18,7 +18,7 @@ The CA private key `rootCA-key.pem` doesn't need to be in version control as it' but we can also just recreate the whole CA and client certificates. ## Using the client certificates locally -If you want to access the artemis-nginx locally from your browser instead of the cypress container's browser, +If you want to access the artemis-nginx locally from your browser instead of the playwright container's browser, you can also install the CA locally on your computer [by installing mkcert locally and the following steps](https://github.com/FiloSottile/mkcert#installing-the-ca-on-other-systems): diff --git a/src/test/playwright/certs/artemis-nginx+4-key.pem b/src/test/playwright/certs/artemis-nginx+4-key.pem index eb2d95da1e9c..e2c0bfc8ab63 100644 --- a/src/test/playwright/certs/artemis-nginx+4-key.pem +++ b/src/test/playwright/certs/artemis-nginx+4-key.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCuCJSHStSQd02f -j+IlQFes7pVUcYv2r0qm5qicGwPcKQf1/nmsy6k4WhE9HQV9VO9LQ4doSNp9NuYX -P/JQqdYLZYYQvxHS+fR7ofIPjirsrbQYAkG5F6imM8H7MkkueG3HGqaKD54PBmC4 -BgBJDFWiF8jSNSYNKOE2L5SaYG/g3LLIkWBlhBQHgrprkio4pv5Y44+nf+hGWkSj -bkRo2+PmIsNmQrpDB2o0O7uoyswa71HE967n9K17SWZ7Hi4kP6BGUWn65P5JB10a -6kz0y8183Uzz99bx8hzxLPg6VNiJZQ+dH4M1Jn6kysKiyV4x24JsM9s6t+Vhln9E -KX5ktosdAgMBAAECggEBAJs3ddkwqWLrtOSR/H2C5G+NHsyAtPdgIfG3mTwZcBjk -03/X5gdyYUusMOHTx3ifzwjOgq9FAvFYjGDCHMlKoGfrtWWsNCZ53k6CApVTE/+h -cRVUte9yJW2Ojf0PPWvf5vEEWPKbuTnnU03ttEVyZdG66tZoprZn9m1QhHYnesEO -PMPvYMd3Oyko8MD/Rr1A/KS/rmc0yfUvgLsqF6PLxq3NKxyVD/8Tp4u9aXbPMnd2 -vugVxjjvt5ubscF1Owi8EjqjVkXlw94JzLcy70XfBzsS2EvUtX/hmHgBEsViXUOQ -KGVyeFTvuReq0RvLQi1LA8vs2q6UC0ZYX75wGDfWWnUCgYEAyP6FY6xdP+N83qEM -TzAf2a33bBCcD5zbrfsvYwHwdzcAz9HBdf3TN1ZcbgfIzIWvuo+hFdjZd32E2+b7 -tSGpcs21iZ3dn1aWxngNs/h94h6cNak/02iCbOsmMX9rHfKZd1ODnQyA8q0s9PQY -uWWWMUfqPse7mSYbgU0aYOVFraMCgYEA3ak3N2mTgTVsUqhNyZCJlmtafp0tsT6b -/7GKSqkl741wokM6un3wx1eo6Q95mngxOlY2xxq9OChnNSEa9ZQnzdUDtQ0YE4QD -09awTIMHNCeSqpV2n3Yv2fT3C5Ya5/WEtYGpVAtqgxwWPij8+VMOa8MVzy+/v6Hg -N1Tpww+Y8D8CgYEAhbEGeK4FuKFQRaVJ0sJn7RrSIIdLxvbHCIqzkl+P2zwyxgj3 -bcxP2dcP1ABJiADESouO0kFTJS/QV5TkiC7DzyEVR1xCNeIamBjyxGrdELLbpLXX -Rn+VgW1IElR2o4zil4RtXuEaRFD8PlK+v1La/ByhqvCfz9aRJQhsK1dVaZECgYEA -jRYR0TFf89P/OLVrnapkCNwX45ND7Bc/0AY/UbpMLSfH02AbV2yl/xvqpT12Vz29 -h7Ysc5qvabk9x/FkaX99vmOhUnIdKv7SONnjqS+VPDsb/XvY3zKozoA/Zp6KTa5W -Y/k9wALsLruH5NTOABw/h5PKo+9uixkLz+w6Ri/9Vp0CgYEAqfkZJe7vCOIwtIwj -Mq5knkJgR+Vq30i4jRoFU0yxIcWA1hODVBnK39+mtA++/3+r5DY5fGRTc9mMyXU/ -y2N2nfSnvPMAUaRmisB7NhmvinEgymlrX+WE+7S9/+nOQADxzWSc6Hxg/ub6mTYV -k2/hv9uG1gbm2+OBP/EBOr48jz0= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBExGUKX71DriG +If8pZM7Wb3jA/W65o3tAz2SxM422yWqG/fGOFosDoKST7akQf3kVucFtZGsYAi5r +tm31Ngmj9YIfKFctAPU3TI52idsSbk0vG7NatwyC2YT12FayWeNwp/2S1jMW8cfI +rlGfkLAguzVOwO7XDoPtkn11gdnrMbxdww/QTjlFV3NPX5oz5Po+Amnc9HiYZSO5 +chTwq1nU4lEAj5270Yw32rynIrjEhSrwkhSAF+a4NFaHdD8exkT7EGe/pm0ExZZp +8TdBknIwZEiKWjz5L/9KhwyFKRWTZYWr73Ns9yr1FltkHcXpqfBfDhGjdqihZe9I +s5PqovLDAgMBAAECggEAP3Om2Uxx3tOBrQNLOi1ZFHv58WLYmsWxYfFGpD3PGv1C +ty6Jzip2cTDAJaV7ZGKgRpWPjgXsdC1zWA7SQD4MGQg8RUUasT2ZWR0dzLMtffcm +Ao9V+EAkKnSWD72TcHW+G236FeGzVJ0NDVTMmmYQnBEqScF6PM8WaGM2PAH6viI8 ++IrgL3ZWAXL2YvQmyQavG0P+MTLkctrymrnx4kId0FW7dBDgeBu1JOsLpz3Ft4DB +JoD3gv+aYkYYPzNF6ueLRMp5qVhVu1KZB4QDHZxCAjFUTgn1kCLHmulTVBGrc0v+ +HXhYW6HVrspFqv2em61BZInDu+aruZpADCFj9qxRgQKBgQD8amBMiU8FVBo9c6kD +FP/+NY4Gh9NPy1zk8uQ4InZ9abNpZ+TX1h9vMvMnyXe49XRy3BYPrT4CVXOe3hXd +aMolRfRD89W/KUNBlITUw2DEbr9FxQrYMUmjaWj5pKdxOiUxOndD2DmhGAwpi3AS +iFDLg1j6eAmPfCISXwIGPyT1owKBgQDD0PdB6AuqbJqcvoPRCfUr7x/ciA9VqVuS +gRYFW6ZGrIOPHp43EFEVzw8Va86lUVR2GRIvnRDWH8H+s1W3JSYaYOmqqA/sZ86i +lyDmvaACQqOUHrCBu+QUcsflye9a7e13xlRvVm6NchEPMhfe9mx1NbfMGhFd5AkB +/hi49TSgYQKBgATkMtWI26UoyhFiymWq5IMSmpv5ydiEAlrVciK8WDt57uYeA0tD +dPx8qkWdG9rTttv689p96vO0oDb16sqNm005m9AtS3kW3ZzJugE9gASOeNprRRiH +a/vksQoVbME+9TWNAn6oFAXRJ3suno9dYONwuiqwZM8JAsgEW0+vrZGtAoGBAItV +YOsTm2FsH+QSqCcRZ9QYNdqtMgQpGxC3K50qZrl5Pa+UAM57nMbg7AG5EXSO14Ol +OUyJ/ZXvSRHcN8y28VLpzLrzFUmJX/5PfUjSe03AbzcRU1UtBhEKz3SR6PARxtxo +UmJa5RVeqgbfBR9Qx0HrtFnV3q+peENjs+HAEO9hAoGBALAgM8uPmt/ZUAzGPf0T +kfLV4HolEk7lmSLvEVB0w8yru9q/YcHuD3VtOjiq5h+N38Yn3t2N6PhJ9tt28OWi +jUmDTmf+n95C1jGYG1Y6SgAPJwdCNHh+j6TPJuv9nwX66DCy8ClY0TpezAeXdH3e +YfFCsfkK9CPTQt85EqunOvSa -----END PRIVATE KEY----- diff --git a/src/test/playwright/certs/artemis-nginx+4.pem b/src/test/playwright/certs/artemis-nginx+4.pem index 6492786845eb..0c71f022d760 100644 --- a/src/test/playwright/certs/artemis-nginx+4.pem +++ b/src/test/playwright/certs/artemis-nginx+4.pem @@ -1,25 +1,25 @@ -----BEGIN CERTIFICATE----- -MIIERjCCAq6gAwIBAgIQSQ2vfdquHAQcrzbEKx46mzANBgkqhkiG9w0BAQsFADBf -MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RA -MWY0ZmQzNzYzNmMyMSEwHwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIw -HhcNMjIxMjA1MDk0NTEzWhcNMjUwMzA1MDk0NTEzWjBFMScwJQYDVQQKEx5ta2Nl -cnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxGjAYBgNVBAsMEXJvb3RAMWY0ZmQz -NzYzNmMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgiUh0rUkHdN -n4/iJUBXrO6VVHGL9q9KpuaonBsD3CkH9f55rMupOFoRPR0FfVTvS0OHaEjafTbm -Fz/yUKnWC2WGEL8R0vn0e6HyD44q7K20GAJBuReopjPB+zJJLnhtxxqmig+eDwZg -uAYASQxVohfI0jUmDSjhNi+UmmBv4NyyyJFgZYQUB4K6a5IqOKb+WOOPp3/oRlpE -o25EaNvj5iLDZkK6QwdqNDu7qMrMGu9RxPeu5/Ste0lmex4uJD+gRlFp+uT+SQdd -GupM9MvNfN1M8/fW8fIc8Sz4OlTYiWUPnR+DNSZ+pMrCosleMduCbDPbOrflYZZ/ -RCl+ZLaLHQIDAQABo4GXMIGUMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr -BgEFBQcDATAfBgNVHSMEGDAWgBSpuKALkiwfLnQmm7+JNG2bxGAIgzBMBgNVHREE -RTBDgg1hcnRlbWlzLW5naW54gg9hcnRlbWlzLmV4YW1wbGWCCWxvY2FsaG9zdIcE -fwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAYEApG8ZADQe -SsH/nqH9WpR3ZkYg0rm8pw+YquBNUdDFG2/4IQtaaxrgsvNPrEEMXfCO4vvnC0cH -6Tgay8LzFZxU9D1F06VZ9S1C7KNnYSsjgwhW7wxem1JXgauoutA8D0uHLr/2bVnz -rTShQT7gRp9SRunqDylaSkgpXlfZQRlEANrYT8Jh6LIHRjkxLh/etw7VdFA6Tywh -iQGBE/EbQcGpmqHBoMytblku0D8H+pcFHZ03AZq0FTMbByM9GekQ8HJV88epqvqJ -7pWyQPX9lr7yC6n121dPoA0ylP8D7jIBCmlFeF+QWCiRAgdeb1w+JONHMgI97IR+ -9HBm6gGE+Da/TRq82w02tUN/F7NHdzqwKGx/GKLrEsdNlfP6D9iiVtfBGBoAUm+C -2t3jbQEgqYHA+mzadS75RGJsRnVdY24IHvNjEnESW6KCaSfQyMmp3trRH6JeOttU -2JeqRPjmOzNvzIcB76w1/hB2ljhimyfoxB8Gbrts+GFPRZE+AXg1mvCn +MIIERzCCAq+gAwIBAgIRAMlqfnXCQz3X2CUiF24MFaAwDQYJKoZIhvcNAQELBQAw +XzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMRowGAYDVQQLDBFyb290 +QGQzNjZiZGIwMGRkODEhMB8GA1UEAwwYbWtjZXJ0IHJvb3RAZDM2NmJkYjAwZGQ4 +MB4XDTI0MDYyMzE2MzE0MloXDTI2MDkyMzE2MzE0MlowRTEnMCUGA1UEChMebWtj +ZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMRowGAYDVQQLDBFyb290QGQzNjZi +ZGIwMGRkODCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMETEZQpfvUO +uIYh/ylkztZveMD9brmje0DPZLEzjbbJaob98Y4WiwOgpJPtqRB/eRW5wW1kaxgC +Lmu2bfU2CaP1gh8oVy0A9TdMjnaJ2xJuTS8bs1q3DILZhPXYVrJZ43Cn/ZLWMxbx +x8iuUZ+QsCC7NU7A7tcOg+2SfXWB2esxvF3DD9BOOUVXc09fmjPk+j4Cadz0eJhl +I7lyFPCrWdTiUQCPnbvRjDfavKciuMSFKvCSFIAX5rg0Vod0Px7GRPsQZ7+mbQTF +lmnxN0GScjBkSIpaPPkv/0qHDIUpFZNlhavvc2z3KvUWW2Qdxemp8F8OEaN2qKFl +70izk+qi8sMCAwEAAaOBlzCBlDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYI +KwYBBQUHAwEwHwYDVR0jBBgwFoAUM5jJgNEPGr9YJJAukqOwnK4E+UwwTAYDVR0R +BEUwQ4INYXJ0ZW1pcy1uZ2lueIIPYXJ0ZW1pcy5leGFtcGxlgglsb2NhbGhvc3SH +BH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBABnZXfx2 +v7JyVzbWHTpbkgYgxLBy+eoiE2GW7YIaZYDrgGYb43rZT1uAa3KOLVogCbNyiw6F +lZuhOSLZdVBaQDJga11b5CT1lRMGSquU9Nacw4oVP7z1GvO7LEnvO/CK44e+zFST +s/emVdY8iwe3ruTaUoTj+zQe8OLEWiecZB/IVo08OsF8/txHMCSlb1sAO/4elFMP +Sulz/dNe8ELkGNOqUjl0BWhSkE4vlTskch2pAClGVGvoBxmLEwVIbu3tnX/fy8ms +2j1JXreRyDqxPlbQfsrVNz85VxmUdsuTqfrCpVYgpp1DAXX4b6u8/xJugBeXj4qH +mfDytfCfp0+9nTnZuMbX+zpao0S15s3+jLKeAxAJfMFQ/hxWfs67KqG3G4sqDhOf +PfwrDk2wudzr1/PRIb+ZCshRgv16ckaEcjPOulgjzGAlGlN1/33Zr8o+O7L2jOMU +gy8IcXbRERBj5tLBsooXHNitMZHyAYRKy0flCpU+qxPSpJDI4fowbfkp7Q== -----END CERTIFICATE----- diff --git a/src/test/cypress/certs/generate-certs.sh b/src/test/playwright/certs/generate-certs.sh similarity index 100% rename from src/test/cypress/certs/generate-certs.sh rename to src/test/playwright/certs/generate-certs.sh diff --git a/src/test/playwright/certs/rootCA.pem b/src/test/playwright/certs/rootCA.pem index df27e7384ca9..25b5da12b9d1 100644 --- a/src/test/playwright/certs/rootCA.pem +++ b/src/test/playwright/certs/rootCA.pem @@ -1,27 +1,27 @@ -----BEGIN CERTIFICATE----- -MIIEjTCCAvWgAwIBAgIQU1RC58hYfDrlnkOa5hWT9TANBgkqhkiG9w0BAQsFADBf -MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RA -MWY0ZmQzNzYzNmMyMSEwHwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIw -HhcNMjIxMjA1MDk0NTEzWhcNMzIxMjA1MDk0NTEzWjBfMR4wHAYDVQQKExVta2Nl -cnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RAMWY0ZmQzNzYzNmMyMSEw -HwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIwggGiMA0GCSqGSIb3DQEB -AQUAA4IBjwAwggGKAoIBgQC6XajL1Rz5RQW0D+TxZ16msa5xAcKt5hEclOUB+oK+ -ychkUiDDSeh4EpkA2txu823n8BLqdNrIZ2A/TW3OVRXcD/hYFTbODI+FZ4DFEHoP -b8QnMlbZfo1AVNOR5ZFAVuneytaauhznnd1+Y49ieeX2v81hmBUKTuwDGVwp69rS -WUqANAKuGim7yOyBX6oRaUPg42xIBVHdDXkWr06zcFC6Z1O5zUYmFYHyn/sP2ZRN -BzMM+lVHCbZlw9Yi5TGLDC4wUAmMS9rColE5E76UNuOvfUERZffdLCzkmbS+VqOI -28fWvO/C8qEB10lcjxxplah4JZSyveH3Q5aFzLK2FbnXfLa/ypr3Mn9rBm06CtaL -Moy6h5gxNFDDB4RTpP7kjz32RRbl5xxqgkTEEAGDr/bk/C3DT6pyvGX5k6cj0zWd -o2bAO42Uk4ehMEHEUhnmvJ1yeOLSabgzfTJ3dM1NkdPI3Ss7dJGfraPx0sGet4SB -KMKw3mqJIizd1vV19TA/oRECAwEAAaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1Ud -EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFKm4oAuSLB8udCabv4k0bZvEYAiDMA0G -CSqGSIb3DQEBCwUAA4IBgQBSPmSTP6Twe1T5UNqxfLSG/aKtT9yR1gAOW1zmM8KN -IZEwa1k6AH+y+dBT1MdFtSORVRagKNBwNsqqUMgPlXp9INpo+a5GFT4oAv73CrWu -bmXq7oiKkfk9UcdBNp3n8J+h+urXm4jEcUzTdNt8R631+seKOjYZz1SKOzpV5iz5 -20Mjk36RkiaiH3sxYvVo9ayXATz4Yq+0jWOHuQpYmFe2mH6wlle8hT/bRQb6/2z0 -wG/kKZl/CYbgRhc39np06mQ7FmAyXTtQORfjfhAUEu568JzPl/r2jYX1YgtEP4IL -GZXOdSYdtVC1vfpoWIo1YY+xqg3WRA/qgPFW33pZBk/qTaWE4ISMr0ewv613JDdK -ZOttGRDm2EDmh/FoeRoO5niydjb9DP915xObGMrYXKkPBpoIsQCfcmmFNQBAN4e4 -OQwSP9r/hh1cS8Usk6Ch0/iRhq7lv2Z5irLl/nQ4tPUZDVbBOhJ936t0gySVHbCl -GVOog28PQ8wI3fRZ2FZg6IM= +MIIEjjCCAvagAwIBAgIRAI6uW57QOeNpioQa9AKB664wDQYJKoZIhvcNAQELBQAw +XzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMRowGAYDVQQLDBFyb290 +QGQzNjZiZGIwMGRkODEhMB8GA1UEAwwYbWtjZXJ0IHJvb3RAZDM2NmJkYjAwZGQ4 +MB4XDTI0MDYyMzE2MzE0MVoXDTM0MDYyMzE2MzE0MVowXzEeMBwGA1UEChMVbWtj +ZXJ0IGRldmVsb3BtZW50IENBMRowGAYDVQQLDBFyb290QGQzNjZiZGIwMGRkODEh +MB8GA1UEAwwYbWtjZXJ0IHJvb3RAZDM2NmJkYjAwZGQ4MIIBojANBgkqhkiG9w0B +AQEFAAOCAY8AMIIBigKCAYEAnJfQZabKJd/qNqgxv7G+Wq6wpmNZH42PwziYqKdj +KL+FSFx7959TYxKTfEKXi2SkuA1aLuZUBH8Wle2U2OBPxRpXU/aB6LjDWaRAO4aT +IsYnGYd+n67bF1QYquFcZwRtcExwlFA7y13XP4Rui/9PmFB8BpSiUe9kX0NUqRrL +K69GYwDqKdJSEgQPROFuD6RETSVAZMN71FDgdiUR8ozJ75tl5sJAOkLKTFuxSyD3 +MSCtdpdCnnnu2myozbjbyFYHYFNRh8ZujvbdaF8wPd8sGiMlEF2BpSXQ3nfohNfJ +Arx++hbhHI4kj9vfIbOrCM8403BoPQyLubu9blCmuQAApQk8UKMKGNDSGXX82oVk +clhgHogz48fKzaNK4USiZi5PmvlGzovpPdqrY4C5kyy38pRSCFMHRpDUkHc/oFe4 +L/f1juqLO2FtevDuPDEuEoPd/AiX09OufMpmpVDYGdCVVeBkgURk9D4lITX3+Q64 +x5mYd+ujZVzV6+XIudQj/D6pAgMBAAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNV +HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQzmMmA0Q8av1gkkC6So7CcrgT5TDAN +BgkqhkiG9w0BAQsFAAOCAYEAY4B7i41qUmPEfXmI0Kp2lwtl/lXno69/a1jGC6ue +dm7V3mhejTzBVAKAP+0C9gmIhlZ/pXX0tNV50yK2KSlt/ukdXVdiLqavm5hQBJgm +nyWH+reIbH9IufDOz1N5vPchvZplHOTk9j2QGh/GVZ54BBMwdeEU2YP8WWibcZWh +LTvficJz67Rf57B/yhisbgaNe8y+8nY37vUdKMtUKlErEN7EaXJlCGhS7JkfEfsp +67EkO1ID+fMvBHBa3ZrPuWgWvkZB+V3hTKdKLca1Sc6eGx6nMRjflidB82rDOTEx +tfTdHvKqiomJIbENtlwSuHbhGCo0EWKqXOi7OeEP9kwUuSSfUgMvBQBLmQLJ1OQq +Y7p2icIuctbzvVIwv2DzKganz2Tz06pb/KMTUlAalfHpqRKB+wg8Sc53g1CtIKNu +Kd7jUSrRD3RBUNiq08GB6iS0cxjOwPpxm4vJp8TbsiDu2DEwhYkvyq2Q3mfBjSXc +zR2vjcpinbbB2wa+ENVB1mbX -----END CERTIFICATE----- diff --git a/src/test/playwright/e2e/exam/ExamAssessment.spec.ts b/src/test/playwright/e2e/exam/ExamAssessment.spec.ts index 1e5ce60aa8d5..3bfee439d546 100644 --- a/src/test/playwright/e2e/exam/ExamAssessment.spec.ts +++ b/src/test/playwright/e2e/exam/ExamAssessment.spec.ts @@ -6,7 +6,7 @@ import { Page, expect } from '@playwright/test'; import javaPartiallySuccessful from '../../fixtures/exercise/programming/java/partially_successful/submission.json'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Commands } from '../../support/commands'; import { ExamAPIRequests } from '../../support/requests/ExamAPIRequests'; import { ExamExerciseGroupCreationPage } from '../../support/pageobjects/exam/ExamExerciseGroupCreationPage'; diff --git a/src/test/playwright/e2e/exam/ExamCreationDeletion.spec.ts b/src/test/playwright/e2e/exam/ExamCreationDeletion.spec.ts index 5895880ab49e..757665204a13 100644 --- a/src/test/playwright/e2e/exam/ExamCreationDeletion.spec.ts +++ b/src/test/playwright/e2e/exam/ExamCreationDeletion.spec.ts @@ -4,7 +4,7 @@ import { Course } from 'app/entities/course.model'; import { dayjsToString, generateUUID, trimDate } from '../../support/utils'; import dayjs from 'dayjs'; import { expect } from '@playwright/test'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; /* * Common primitives diff --git a/src/test/playwright/e2e/exam/ExamManagement.spec.ts b/src/test/playwright/e2e/exam/ExamManagement.spec.ts index 743541cd5edb..a91b64b7f764 100644 --- a/src/test/playwright/e2e/exam/ExamManagement.spec.ts +++ b/src/test/playwright/e2e/exam/ExamManagement.spec.ts @@ -3,7 +3,7 @@ import { admin, instructor, studentOne } from '../../support/users'; import { generateUUID, newBrowserPage } from '../../support/utils'; import { test } from '../../support/fixtures'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Commands } from '../../support/commands'; import { CourseManagementAPIRequests } from '../../support/requests/CourseManagementAPIRequests'; diff --git a/src/test/playwright/e2e/exam/ExamParticipation.spec.ts b/src/test/playwright/e2e/exam/ExamParticipation.spec.ts index 8264232513e2..a27ffe2c25ab 100644 --- a/src/test/playwright/e2e/exam/ExamParticipation.spec.ts +++ b/src/test/playwright/e2e/exam/ExamParticipation.spec.ts @@ -5,7 +5,7 @@ import { admin, instructor, studentFour, studentOne, studentThree, studentTwo, t import { generateUUID } from '../../support/utils'; import javaAllSuccessfulSubmission from '../../fixtures/exercise/programming/java/all_successful/submission.json'; import dayjs from 'dayjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { expect } from '@playwright/test'; import { ExamStartEndPage } from '../../support/pageobjects/exam/ExamStartEndPage'; import { Commands } from '../../support/commands'; diff --git a/src/test/playwright/e2e/exam/ExamResults.spec.ts b/src/test/playwright/e2e/exam/ExamResults.spec.ts index 676e02c2e686..874a49e23089 100644 --- a/src/test/playwright/e2e/exam/ExamResults.spec.ts +++ b/src/test/playwright/e2e/exam/ExamResults.spec.ts @@ -1,5 +1,5 @@ import { test } from '../../support/fixtures'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { Commands } from '../../support/commands'; import { admin, instructor, studentOne, tutor } from '../../support/users'; import { Course } from 'app/entities/course.model'; diff --git a/src/test/playwright/e2e/exam/ExamTestRun.spec.ts b/src/test/playwright/e2e/exam/ExamTestRun.spec.ts index 732bbc4f1290..42e873ca2427 100644 --- a/src/test/playwright/e2e/exam/ExamTestRun.spec.ts +++ b/src/test/playwright/e2e/exam/ExamTestRun.spec.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import javaBuildErrorSubmission from '../../fixtures/exercise/programming/java/build_error/submission.json'; import { Exercise, ExerciseType } from '../../support/constants'; diff --git a/src/test/playwright/e2e/exam/test-exam/TestExamCreationDeletion.spec.ts b/src/test/playwright/e2e/exam/test-exam/TestExamCreationDeletion.spec.ts index 71ee1df974ec..d3f340fcb917 100644 --- a/src/test/playwright/e2e/exam/test-exam/TestExamCreationDeletion.spec.ts +++ b/src/test/playwright/e2e/exam/test-exam/TestExamCreationDeletion.spec.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { admin } from '../../../support/users'; import { dayjsToString, generateUUID, trimDate } from '../../../support/utils'; diff --git a/src/test/playwright/e2e/exam/test-exam/TestExamManagement.spec.ts b/src/test/playwright/e2e/exam/test-exam/TestExamManagement.spec.ts index 931b80bace33..9fc0bdb9cf5e 100644 --- a/src/test/playwright/e2e/exam/test-exam/TestExamManagement.spec.ts +++ b/src/test/playwright/e2e/exam/test-exam/TestExamManagement.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { admin, instructor, studentOne } from '../../../support/users'; diff --git a/src/test/playwright/e2e/exam/test-exam/TestExamParticipation.spec.ts b/src/test/playwright/e2e/exam/test-exam/TestExamParticipation.spec.ts index 4231108de78b..c66d2966576f 100644 --- a/src/test/playwright/e2e/exam/test-exam/TestExamParticipation.spec.ts +++ b/src/test/playwright/e2e/exam/test-exam/TestExamParticipation.spec.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import javaAllSuccessfulSubmission from '../../../fixtures/exercise/programming/java/all_successful/submission.json'; import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; diff --git a/src/test/playwright/e2e/exam/test-exam/TestExamStudentExams.spec.ts b/src/test/playwright/e2e/exam/test-exam/TestExamStudentExams.spec.ts index 12ae9b246554..1699d5539c22 100644 --- a/src/test/playwright/e2e/exam/test-exam/TestExamStudentExams.spec.ts +++ b/src/test/playwright/e2e/exam/test-exam/TestExamStudentExams.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { UserCredentials, admin, studentOne, studentThree, studentTwo, users } from '../../../support/users'; import { generateUUID } from '../../../support/utils'; import { Exercise, ExerciseType } from '../../../support/constants'; diff --git a/src/test/playwright/e2e/exam/test-exam/TestExamTestRun.spec.ts b/src/test/playwright/e2e/exam/test-exam/TestExamTestRun.spec.ts index 90cce7420bee..66b13ff3af12 100644 --- a/src/test/playwright/e2e/exam/test-exam/TestExamTestRun.spec.ts +++ b/src/test/playwright/e2e/exam/test-exam/TestExamTestRun.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; import { Exercise, ExerciseType } from '../../../support/constants'; diff --git a/src/test/playwright/e2e/exercise/ExerciseImport.spec.ts b/src/test/playwright/e2e/exercise/ExerciseImport.spec.ts index d08d08717ca7..8290457c32b9 100644 --- a/src/test/playwright/e2e/exercise/ExerciseImport.spec.ts +++ b/src/test/playwright/e2e/exercise/ExerciseImport.spec.ts @@ -2,9 +2,9 @@ import dayjs from 'dayjs'; import { Course } from 'app/entities/course.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import javaPartiallySuccessfulSubmission from '../../fixtures/exercise/programming/java/partially_successful/submission.json'; import multipleChoiceQuizTemplate from '../../fixtures/exercise/quiz/multiple_choice/template.json'; @@ -13,7 +13,7 @@ import { generateUUID } from '../../support/utils'; import { test } from '../../support/fixtures'; import { expect } from '@playwright/test'; import { Fixtures } from '../../fixtures/fixtures'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; diff --git a/src/test/playwright/e2e/exercise/modeling/ModelingExerciseManagement.spec.ts b/src/test/playwright/e2e/exercise/modeling/ModelingExerciseManagement.spec.ts index d1582cdc53b6..cd30ff8cf24b 100644 --- a/src/test/playwright/e2e/exercise/modeling/ModelingExerciseManagement.spec.ts +++ b/src/test/playwright/e2e/exercise/modeling/ModelingExerciseManagement.spec.ts @@ -1,5 +1,5 @@ import dayjs from 'dayjs'; -import { MODELING_EDITOR_CANVAS } from 'src/test/cypress/support/constants'; +import { MODELING_EDITOR_CANVAS } from '../../../support/constants'; import { Course } from 'app/entities/course.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; diff --git a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts index 199c19c87306..ba4e95fbb241 100644 --- a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts +++ b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseAssessment.spec.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs'; import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseAssessmentType } from '../../../support/constants'; import { admin, instructor, studentOne, tutor } from '../../../support/users'; diff --git a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseManagement.spec.ts b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseManagement.spec.ts index 51e6c7bb0bc2..e3a69adabc20 100644 --- a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseManagement.spec.ts +++ b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseManagement.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { admin, instructor, studentFour, studentOne, studentThree, studentTwo, tutor } from '../../../support/users'; import { test } from '../../../support/fixtures'; diff --git a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts index f0540dc21dc3..0a36076081ea 100644 --- a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts +++ b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseParticipation.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import javaAllSuccessfulSubmission from '../../../fixtures/exercise/programming/java/all_successful/submission.json'; import javaBuildErrorSubmission from '../../../fixtures/exercise/programming/java/build_error/submission.json'; diff --git a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts index ffeb96adc4d5..c11dac9b557e 100644 --- a/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts +++ b/src/test/playwright/e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import javaScaSubmission from '../../../fixtures/exercise/programming/java/static_code_analysis/submission.json'; import { admin, studentOne } from '../../../support/users'; diff --git a/src/test/playwright/e2e/exercise/text/TextExerciseAssessment.spec.ts b/src/test/playwright/e2e/exercise/text/TextExerciseAssessment.spec.ts index 61228fd2ef67..ed4b9a076c42 100644 --- a/src/test/playwright/e2e/exercise/text/TextExerciseAssessment.spec.ts +++ b/src/test/playwright/e2e/exercise/text/TextExerciseAssessment.spec.ts @@ -1,5 +1,5 @@ import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { admin, instructor, studentOne, tutor } from '../../../support/users'; import { test } from '../../../support/fixtures'; diff --git a/src/test/playwright/e2e/exercise/text/TextExerciseManagement.spec.ts b/src/test/playwright/e2e/exercise/text/TextExerciseManagement.spec.ts index 56ab3c12e168..5bf800e36484 100644 --- a/src/test/playwright/e2e/exercise/text/TextExerciseManagement.spec.ts +++ b/src/test/playwright/e2e/exercise/text/TextExerciseManagement.spec.ts @@ -1,12 +1,12 @@ import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { test } from '../../../support/fixtures'; import { admin } from '../../../support/users'; import { generateUUID } from '../../../support/utils'; import dayjs from 'dayjs'; import { expect } from '@playwright/test'; import { ExampleSubmission } from 'app/entities/example-submission.model'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; test.describe('Text exercise management', () => { let course: Course; diff --git a/src/test/playwright/e2e/exercise/text/TextExerciseParticipation.spec.ts b/src/test/playwright/e2e/exercise/text/TextExerciseParticipation.spec.ts index d476578e779c..b2ac84ef3284 100644 --- a/src/test/playwright/e2e/exercise/text/TextExerciseParticipation.spec.ts +++ b/src/test/playwright/e2e/exercise/text/TextExerciseParticipation.spec.ts @@ -1,11 +1,11 @@ import { Course } from 'app/entities/course.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import { admin, studentOne } from '../../../support/users'; import { test } from '../../../support/fixtures'; import { Fixtures } from '../../../fixtures/fixtures'; import { expect } from '@playwright/test'; -import { TextSubmission } from 'app/entities/text-submission.model'; +import { TextSubmission } from 'app/entities/text/text-submission.model'; test.describe('Text exercise participation', () => { let course: Course; diff --git a/src/test/playwright/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts b/src/test/playwright/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts index 2c060438098e..58d3557d19f3 100644 --- a/src/test/playwright/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts +++ b/src/test/playwright/support/pageobjects/exam/ExamExerciseGroupCreationPage.ts @@ -1,5 +1,5 @@ import { Page } from '@playwright/test'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamAPIRequests } from '../../requests/ExamAPIRequests'; import { ExerciseAPIRequests } from '../../requests/ExerciseAPIRequests'; import multipleChoiceTemplate from '../../../fixtures/exercise/quiz/multiple_choice/template.json'; @@ -8,7 +8,7 @@ import { generateUUID } from '../../utils'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Exercise } from 'app/entities/exercise.model'; -import { Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; export class ExamExerciseGroupCreationPage { private readonly page: Page; diff --git a/src/test/playwright/support/pageobjects/exam/ExamParticipationPage.ts b/src/test/playwright/support/pageobjects/exam/ExamParticipationPage.ts index cdec2ee5d8dc..e07b9e42f486 100644 --- a/src/test/playwright/support/pageobjects/exam/ExamParticipationPage.ts +++ b/src/test/playwright/support/pageobjects/exam/ExamParticipationPage.ts @@ -1,6 +1,6 @@ import { Page, expect } from '@playwright/test'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AdditionalData, ExerciseType } from '../../constants'; import { UserCredentials } from '../../users'; import { OnlineEditorPage, ProgrammingExerciseSubmission } from '../exercises/programming/OnlineEditorPage'; diff --git a/src/test/playwright/support/pageobjects/exam/ExamTestRunPage.ts b/src/test/playwright/support/pageobjects/exam/ExamTestRunPage.ts index a5e3891f5ef7..d741796fe898 100644 --- a/src/test/playwright/support/pageobjects/exam/ExamTestRunPage.ts +++ b/src/test/playwright/support/pageobjects/exam/ExamTestRunPage.ts @@ -1,6 +1,6 @@ import { Page } from '@playwright/test'; import { Course } from 'app/entities/course.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { UserCredentials } from '../../users'; import { Commands } from '../../commands'; import { ExamStartEndPage } from './ExamStartEndPage'; diff --git a/src/test/playwright/support/requests/CourseManagementAPIRequests.ts b/src/test/playwright/support/requests/CourseManagementAPIRequests.ts index 3b04f8571785..3003ec91e760 100644 --- a/src/test/playwright/support/requests/CourseManagementAPIRequests.ts +++ b/src/test/playwright/support/requests/CourseManagementAPIRequests.ts @@ -8,7 +8,7 @@ import lectureTemplate from '../../fixtures/lecture/template.json'; import { BASE_API, COURSE_ADMIN_BASE, COURSE_BASE, Exercise } from '../constants'; import { UserCredentials } from '../users'; import { Commands } from '../commands'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; /** * A class which encapsulates all API requests related to course management. diff --git a/src/test/playwright/support/requests/ExamAPIRequests.ts b/src/test/playwright/support/requests/ExamAPIRequests.ts index be8175b5a711..c250415fb2db 100644 --- a/src/test/playwright/support/requests/ExamAPIRequests.ts +++ b/src/test/playwright/support/requests/ExamAPIRequests.ts @@ -1,6 +1,6 @@ import { Course } from 'app/entities/course.model'; import dayjs from 'dayjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { dayjsToString, generateUUID, titleLowercase } from '../utils'; import examTemplate from '../../fixtures/exam/template.json'; import { Page } from '@playwright/test'; diff --git a/src/test/playwright/support/requests/ExerciseAPIRequests.ts b/src/test/playwright/support/requests/ExerciseAPIRequests.ts index 9b7bc73ace71..2fac212a5d73 100644 --- a/src/test/playwright/support/requests/ExerciseAPIRequests.ts +++ b/src/test/playwright/support/requests/ExerciseAPIRequests.ts @@ -4,7 +4,7 @@ import { Page } from 'playwright-core'; import { Course } from 'app/entities/course.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { TextExercise } from 'app/entities/text-exercise.model'; +import { TextExercise } from 'app/entities/text/text-exercise.model'; import fileUploadExerciseTemplate from '../../fixtures/exercise/file-upload/template.json'; import modelingExerciseSubmissionTemplate from '../../fixtures/exercise/modeling/submission.json'; @@ -34,16 +34,16 @@ import { } from '../constants'; import { dayjsToString, generateUUID, titleLowercase } from '../utils'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; -import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; +import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { Participation } from 'app/entities/participation/participation.model'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Team } from 'app/entities/team.model'; import { TeamAssignmentConfig } from 'app/entities/team-assignment-config.model'; import { ProgrammingExerciseSubmission } from '../pageobjects/exercises/programming/OnlineEditorPage'; import { Fixtures } from '../../fixtures/fixtures'; -import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming-exercise-test-case.model'; +import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; type PatchProgrammingExerciseTestVisibilityDto = { id: number; diff --git a/src/test/resources/config/application-theia.yml b/src/test/resources/config/application-theia.yml new file mode 100644 index 000000000000..883dea41098f --- /dev/null +++ b/src/test/resources/config/application-theia.yml @@ -0,0 +1,9 @@ +theia: + portal-url: https://your-theia-instance.com + + images: + java: + Java-17: "ghcr.io/ls1intum/theia/java-17:latest" + Java-Non-Existent: "this-is-not-a-valid-image" + c: + C: "ghcr.io/ls1intum/theia/c:latest" diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java index 250966bfd432..b096ba1201c6 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java @@ -15,9 +15,9 @@ public class EndpointAnalyzer { - private static String EndpointAnalysisResultPath = "endpointAnalysisResult.json"; + private static String ENDPOINT_ANALYSIS_RESULT_PATH = "endpointAnalysisResult.json"; - private static final Logger logger = LoggerFactory.getLogger(EndpointAnalyzer.class); + private static final Logger log = LoggerFactory.getLogger(EndpointAnalyzer.class); public static void main(String[] args) { analyzeEndpoints(); @@ -60,10 +60,13 @@ private static void analyzeEndpoints() { for (EndpointInformation endpoint : endpointClass.endpoints()) { String endpointURI = endpoint.buildComparableEndpointUri(); - List matchingRestCalls = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); + List restCallsWithMatchingURI = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); // Check for wildcard endpoints if no exact match is found - checkForWildcardEndpoints(endpoint, matchingRestCalls, endpointURI, restCallMap); + checkForWildcardEndpoints(endpoint, restCallsWithMatchingURI, endpointURI, restCallMap); + + List matchingRestCalls = restCallsWithMatchingURI.stream() + .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); if (matchingRestCalls.isEmpty()) { unusedEndpoints.add(endpoint); @@ -75,10 +78,10 @@ private static void analyzeEndpoints() { } EndpointAnalysis endpointAnalysis = new EndpointAnalysis(endpointsAndMatchingRestCalls, unusedEndpoints); - mapper.writeValue(new File(EndpointAnalysisResultPath), endpointAnalysis); + mapper.writeValue(new File(ENDPOINT_ANALYSIS_RESULT_PATH), endpointAnalysis); } catch (IOException e) { - logger.error("Failed to analyze endpoints", e); + log.error("Failed to analyze endpoints", e); } } @@ -119,26 +122,26 @@ private static void printEndpointAnalysisResult() { ObjectMapper mapper = new ObjectMapper(); EndpointAnalysis endpointsAndMatchingRestCalls = null; try { - endpointsAndMatchingRestCalls = mapper.readValue(new File(EndpointAnalysisResultPath), new TypeReference() { + endpointsAndMatchingRestCalls = mapper.readValue(new File(ENDPOINT_ANALYSIS_RESULT_PATH), new TypeReference() { }); } catch (IOException e) { - logger.error("Failed to deserialize endpoint analysis result", e); + log.error("Failed to deserialize endpoint analysis result", e); return; } endpointsAndMatchingRestCalls.unusedEndpoints().stream().forEach(endpoint -> { - logger.info("============================================="); - logger.info("Endpoint URI: {}", endpoint.buildCompleteEndpointURI()); - logger.info("HTTP method: {}", endpoint.httpMethodAnnotation()); - logger.info("File path: {}", endpoint.className()); - logger.info("Line: {}", endpoint.line()); - logger.info("============================================="); - logger.info("No matching REST call found for endpoint: {}", endpoint.buildCompleteEndpointURI()); - logger.info("---------------------------------------------"); - logger.info(""); + log.info("============================================="); + log.info("Endpoint URI: {}", endpoint.buildCompleteEndpointURI()); + log.info("HTTP method: {}", endpoint.httpMethodAnnotation()); + log.info("File path: {}", endpoint.className()); + log.info("Line: {}", endpoint.line()); + log.info("============================================="); + log.info("No matching REST call found for endpoint: {}", endpoint.buildCompleteEndpointURI()); + log.info("---------------------------------------------"); + log.info(""); }); - logger.info("Number of endpoints without matching REST calls: {}", endpointsAndMatchingRestCalls.unusedEndpoints().size()); + log.info("Number of endpoints without matching REST calls: {}", endpointsAndMatchingRestCalls.unusedEndpoints().size()); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java index 19b32dc8881b..52f33cef81e2 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java @@ -4,16 +4,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -public record EndpointInformation(String requestMapping, String endpoint, String httpMethodAnnotation, String URI, String className, int line, List otherAnnotations) { +public record EndpointInformation(String requestMapping, String endpoint, String httpMethodAnnotation, String uri, String className, int line, List otherAnnotations) { - public String buildCompleteEndpointURI() { + String buildCompleteEndpointURI() { StringBuilder result = new StringBuilder(); if (this.requestMapping != null && !this.requestMapping.isEmpty()) { // Remove quotes from the requestMapping as they are used to define the String in the source code but are not part of the URI result.append(this.requestMapping.replace("\"", "")); } // Remove quotes from the URI as they are used to define the String in the source code but are not part of the URI - result.append(this.URI.replace("\"", "")); + result.append(this.uri.replace("\"", "")); return result.toString(); } @@ -23,7 +23,7 @@ String buildComparableEndpointUri() { } @JsonIgnore - public String getHttpMethod() { + String getHttpMethod() { return switch (this.httpMethodAnnotation) { case "GetMapping" -> "get"; case "PostMapping" -> "post"; diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java index b0ab2cdb1f0b..9fb5a05b72da 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java @@ -36,7 +36,7 @@ public class EndpointParser { static final String REST_CALL_PARSING_RESULT_PATH = "restCalls.json"; - private static final Logger logger = LoggerFactory.getLogger(EndpointParser.class); + private static final Logger log = LoggerFactory.getLogger(EndpointParser.class); public static void main(String[] args) { final Path absoluteDirectoryPath = Path.of("../../src/main/java").toAbsolutePath().normalize(); @@ -48,7 +48,7 @@ public static void main(String[] args) { filesToParse = paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".java")).map(Path::toString).toArray(String[]::new); } catch (IOException e) { - logger.error("Error reading files from directory: {}", absoluteDirectoryPath, e); + log.error("Error reading files from directory: {}", absoluteDirectoryPath, e); } parseServerEndpoints(filesToParse); @@ -190,9 +190,9 @@ else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { */ private static void printFilesFailedToParse(List filesFailedToParse) { if (!filesFailedToParse.isEmpty()) { - logger.warn("Files failed to parse:", filesFailedToParse); + log.warn("Files failed to parse:", filesFailedToParse); for (String file : filesFailedToParse) { - logger.warn(file); + log.warn(file); } } } @@ -211,7 +211,7 @@ private static void writeEndpointsToFile(List endpoint new ObjectMapper().writeValue(new File(ENDPOINT_PARSING_RESULT_PATH), endpointClasses); } catch (IOException e) { - logger.error("Failed to write endpoint information to file", e); + log.error("Failed to write endpoint information to file", e); } } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java index aac71d6573bf..19f8cbc6c56f 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java @@ -17,7 +17,7 @@ public class RestCallAnalyzer { private static final String REST_CALL_ANALYSIS_RESULT_PATH = "restCallAnalysisResult.json"; - private static final Logger logger = LoggerFactory.getLogger(RestCallAnalyzer.class); + private static final Logger log = LoggerFactory.getLogger(RestCallAnalyzer.class); public static void main(String[] args) { analyzeRestCalls(); @@ -58,16 +58,19 @@ private static void analyzeRestCalls() { for (RestCallFileInformation restCallFile : restCalls) { for (RestCallInformation restCall : restCallFile.restCalls()) { String restCallURI = restCall.buildComparableRestCallUri(); - List matchingEndpoints = endpointMap.getOrDefault(restCallURI, new ArrayList<>()); + List endpointsWithMatchingUri = endpointMap.getOrDefault(restCallURI, new ArrayList<>()); - checkForWildcardMatches(restCall, matchingEndpoints, restCallURI, endpointMap); + checkForWildcardMatches(restCall, endpointsWithMatchingUri, restCallURI, endpointMap); - if (matchingEndpoints.isEmpty()) { + List endpointsWithMatchingHttpMethod = endpointsWithMatchingUri.stream() + .filter(endpoint -> endpoint.getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())).toList(); + + if (endpointsWithMatchingHttpMethod.isEmpty()) { restCallsWithoutMatchingEndpoint.add(restCall); } else { - for (EndpointInformation endpoint : matchingEndpoints) { - restCallsWithMatchingEndpoint.add(new RestCallWithMatchingEndpoint(endpoint, restCall, restCall.fileName())); + for (EndpointInformation endpoint : endpointsWithMatchingHttpMethod) { + restCallsWithMatchingEndpoint.add(new RestCallWithMatchingEndpoint(endpoint, restCall, restCall.filePath())); } } } @@ -77,7 +80,7 @@ private static void analyzeRestCalls() { mapper.writeValue(new File(REST_CALL_ANALYSIS_RESULT_PATH), restCallAnalysis); } catch (IOException e) { - logger.error("Failed to analyze REST calls", e); + log.error("Failed to analyze REST calls", e); } } @@ -124,21 +127,22 @@ private static void printRestCallAnalysisResult() { }); } catch (IOException e) { - logger.error("Failed to deserialize rest call analysis results", e); + log.error("Failed to deserialize rest call analysis results", e); + return; } restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().stream().forEach(endpoint -> { - logger.info("============================================="); - logger.info("REST call URI: {}", endpoint.buildCompleteRestCallURI()); - logger.info("HTTP method: {}", endpoint.method()); - logger.info("File path: {}", endpoint.fileName()); - logger.info("Line: {}", endpoint.line()); - logger.info("============================================="); - logger.info("No matching endpoint found for REST call: {}", endpoint.buildCompleteRestCallURI()); - logger.info("---------------------------------------------"); - logger.info(""); + log.info("============================================="); + log.info("REST call URI: {}", endpoint.buildCompleteRestCallURI()); + log.info("HTTP method: {}", endpoint.method()); + log.info("File path: {}", endpoint.filePath()); + log.info("Line: {}", endpoint.line()); + log.info("============================================="); + log.info("No matching endpoint found for REST call: {}", endpoint.buildCompleteRestCallURI()); + log.info("---------------------------------------------"); + log.info(""); }); - logger.info("Number of REST calls without matching endpoints: {}", restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().size()); + log.info("Number of REST calls without matching endpoints: {}", restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().size()); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java index 847ec03b1561..2582f57443cc 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java @@ -1,4 +1,6 @@ package de.tum.cit.endpointanalysis; -public record RestCallFileInformation(String fileName, RestCallInformation[] restCalls) { +import java.util.List; + +public record RestCallFileInformation(String filePath, List restCalls) { } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java index fb1e44f92f2a..b6567d707a47 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java @@ -1,18 +1,23 @@ package de.tum.cit.endpointanalysis; -public record RestCallInformation(String method, String url, int line, String fileName) { +public record RestCallInformation(String method, String url, String filePath, int line) { - public String buildCompleteRestCallURI() { + String buildCompleteRestCallURI() { return this.url.replace("`", ""); } - public String buildComparableRestCallUri() { + String buildComparableRestCallUri() { // Replace arguments with placeholder String result = this.buildCompleteRestCallURI().replaceAll("\\$\\{.*?\\}", ":param:"); // Remove query parameters result = result.split("\\?")[0]; + // Some URIs in the artemis client start with a redundant `/`. To be able to compare them to the endpoint URIs, we remove it. + if (result.startsWith("/")) { + result = result.substring(1); + } + return result; } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts index 18b54a5f0ac4..46b95a821a18 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts @@ -5,7 +5,7 @@ interface RestCall { method: string; url: string; line: number; - fileName: string; + filePath: string; } enum ParsingResultType { @@ -38,17 +38,17 @@ class ParsingResult { } export class Postprocessor { - static filesWithRestCalls: { fileName: string, restCalls: RestCall[] }[] = []; + static filesWithRestCalls: { filePath: string, restCalls: RestCall[] }[] = []; private readonly restCalls: RestCall[] = []; - private readonly fileName: string; + private readonly filePath: string; private readonly ast: TSESTree.Program; /** - * @param fileName - The name of the file being processed. + * @param filePath - The path of the file being processed. * @param ast - The abstract syntax tree (AST) of the processed file. */ - constructor(fileName: string, ast: TSESTree.Program) { - this.fileName = fileName; + constructor(filePath: string, ast: TSESTree.Program) { + this.filePath = filePath; this.ast = ast; } @@ -61,7 +61,7 @@ export class Postprocessor { } }); if (this.restCalls.length > 0) { - Postprocessor.filesWithRestCalls.push( {fileName: this.fileName, restCalls: this.restCalls} ); + Postprocessor.filesWithRestCalls.push({ filePath: this.filePath, restCalls: this.restCalls }); } } @@ -108,10 +108,10 @@ export class Postprocessor { urlEvaluationResult = this.evaluateUrl(node.arguments[0], methodDefinition, node, classBody); } - const fileName = this.fileName; + const filePath = this.filePath; if (urlEvaluationResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { - for (let url of urlEvaluationResult.result) { - this.restCalls.push({ method, url, line, fileName }); + for (const url of urlEvaluationResult.result) { + this.restCalls.push({ method, url, line, filePath }); } } } @@ -175,6 +175,7 @@ export class Postprocessor { } /** + * Evaluates a template literal AST node to determine its URL value. * Evaluates a template literal AST node to determine its URL value. * * This method evaluates the provided template literal node by calling `evaluateTemplateLiteralExpression`. @@ -541,7 +542,7 @@ export class Postprocessor { simpleTraverse(methodDefinition, { enter: (node) => { if (node.type === 'VariableDeclaration') { - for (let decl of node.declarations) { + for (const decl of node.declarations) { if (decl.id.type === 'Identifier' && decl.id.name === name && decl.init) { const tempResult = this.evaluateUrl(decl.init, methodDefinition, restCall, classBody); if (tempResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { @@ -587,7 +588,7 @@ export class Postprocessor { * @returns An array of AST nodes representing the parameters of the constructor. */ getConstructorArgumentsFromClassBody(classBody: TSESTree.ClassBody): TSESTree.Parameter[] { - for (let node of classBody.body) { + for (const node of classBody.body) { if (node.type === 'MethodDefinition' && node.key.type === 'Identifier' && node.key.name === 'constructor') { return node.value.params; } @@ -615,11 +616,11 @@ export class Postprocessor { const superClass = Preprocessor.PREPROCESSING_RESULTS.get(this.getClassNameFromClassBody(classBody)); if (superClass) { const constructorArguments = this.getConstructorArgumentsFromClassBody(classBody.body); - for (let superConstructorCallArguments of superClass.superConstructorCalls) { + for (const superConstructorCallArguments of superClass.superConstructorCalls) { for (let i = 0; i < superConstructorCallArguments.arguments.length; i++) { - let constructorArgument = constructorArguments[i]; + const constructorArgument = constructorArguments[i]; if (superConstructorCallArguments.arguments[i] !== '' && constructorArgument.type === 'TSParameterProperty' - && constructorArgument.parameter.type === 'Identifier' && constructorArgument.parameter.name === memberExprKey) { + && constructorArgument.parameter.type === 'Identifier' && constructorArgument.parameter.name === memberExprKey) { memberExpressionResult.push(superConstructorCallArguments.arguments[i]); resultType = ParsingResultType.EVALUATE_MEMBER_EXPRESSION_SUCCESS; } diff --git a/supporting_scripts/generate_code_cov_table/README.md b/supporting_scripts/generate_code_cov_table/README.md index 925e16d3d61c..1953c39aef5a 100644 --- a/supporting_scripts/generate_code_cov_table/README.md +++ b/supporting_scripts/generate_code_cov_table/README.md @@ -7,7 +7,7 @@ The generated report is copied to the clipboard and pasted into a pull request o ### Prerequisites -- Python 3.6 or higher +- Python 3.12 or higher - pip - git diff --git a/supporting_scripts/generate_code_cov_table/requirements.txt b/supporting_scripts/generate_code_cov_table/requirements.txt index 7472e56611de..78379bda1531 100644 --- a/supporting_scripts/generate_code_cov_table/requirements.txt +++ b/supporting_scripts/generate_code_cov_table/requirements.txt @@ -1,6 +1,6 @@ -requests==2.31.0 -gitpython==3.1.41 -beautifulsoup4==4.12.2 -pyperclip==1.8.2 -python-dotenv==1.0.0 -tqdm==4.65.0 +requests==2.32.3 +gitpython==3.1.43 +beautifulsoup4==4.12.3 +pyperclip==1.9.0 +python-dotenv==1.0.1 +tqdm==4.66.5