From 0ffa44f80c3654f8e0e18ccee007a6acf6bf0e9f Mon Sep 17 00:00:00 2001 From: Simon Entholzer <33342534+SimonEntholzer@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:12:17 +0100 Subject: [PATCH 1/7] Lectures: Use attachment name for file downloads (#9775) --- .../aet/artemis/core/web/FileResource.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java index ac1e03f866da..27332e5343a7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java @@ -219,7 +219,7 @@ public ResponseEntity getMarkdownFileForConversation(@PathVariable Long public ResponseEntity getMarkdownFile(@PathVariable String filename) { log.debug("REST request to get file : {}", filename); sanitizeFilenameElseThrow(filename); - return buildFileResponse(FilePathService.getMarkdownFilePath(), filename); + return buildFileResponse(FilePathService.getMarkdownFilePath(), filename, false); } /** @@ -431,7 +431,7 @@ public ResponseEntity getLectureAttachment(@PathVariable Long lectureId, // check if the user is authorized to access the requested attachment unit checkAttachmentAuthorizationOrThrow(course, attachment); - return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); + return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), Optional.of(attachment.getName())); } /** @@ -470,13 +470,13 @@ public ResponseEntity getLecturePdfAttachmentsMerged(@PathVariable Long /** * GET files/attachments/attachment-unit/:attachmentUnitId/:filename : Get the lecture unit attachment + * Accesses to this endpoint are created by the server itself in the FilePathService * * @param attachmentUnitId ID of the attachment unit, the attachment belongs to * @return The requested file, 403 if the logged-in user is not allowed to access it, or 404 if the file doesn't exist */ @GetMapping("files/attachments/attachment-unit/{attachmentUnitId}/*") @EnforceAtLeastStudent - // TODO: this method is kind of redundant to the method getAttachmentFile below, double check if both are actually needed public ResponseEntity getAttachmentUnitAttachment(@PathVariable Long attachmentUnitId) { log.debug("REST request to get the file for attachment unit {} for students", attachmentUnitId); AttachmentUnit attachmentUnit = attachmentUnitRepository.findByIdElseThrow(attachmentUnitId); @@ -487,8 +487,7 @@ public ResponseEntity getAttachmentUnitAttachment(@PathVariable Long att // check if the user is authorized to access the requested attachment unit checkAttachmentAuthorizationOrThrow(course, attachment); - - return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); + return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), Optional.of(attachment.getName())); } /** @@ -567,36 +566,49 @@ public ResponseEntity getAttachmentUnitAttachmentSlide(@PathVariable Lon } /** - * Builds the response with headers, body and content type for specified path and file name + * Builds the response with headers, body and content type for specified path containing the file name * - * @param path to the file - * @param filename the name of the file + * @param path to the file including the file name + * @param cache true if the response should contain a header that allows caching; false otherwise * @return response entity */ - private ResponseEntity buildFileResponse(Path path, String filename) { - return buildFileResponse(path, filename, false); + private ResponseEntity buildFileResponse(Path path, boolean cache) { + return buildFileResponse(path.getParent(), path.getFileName().toString(), Optional.empty(), cache); } /** * Builds the response with headers, body and content type for specified path containing the file name * - * @param path to the file including the file name - * @param cache true if the response should contain a header that allows caching; false otherwise + * @param path to the file including the file name + * @param filename the name of the file + * @param cache true if the response should contain a header that allows caching; false otherwise * @return response entity */ - private ResponseEntity buildFileResponse(Path path, boolean cache) { - return buildFileResponse(path.getParent(), path.getFileName().toString(), cache); + private ResponseEntity buildFileResponse(Path path, String filename, boolean cache) { + return buildFileResponse(path, filename, Optional.empty(), cache); } /** * Builds the response with headers, body and content type for specified path and file name * - * @param path to the file - * @param filename the name of the file - * @param cache true if the response should contain a header that allows caching; false otherwise + * @param path to the file + * @param replaceFilename replaces the downloaded file's name, if provided * @return response entity */ - private ResponseEntity buildFileResponse(Path path, String filename, boolean cache) { + private ResponseEntity buildFileResponse(Path path, Optional replaceFilename) { + return buildFileResponse(path.getParent(), path.getFileName().toString(), replaceFilename, false); + } + + /** + * Builds the response with headers, body and content type for specified path and file name + * + * @param path to the file + * @param filename the name of the file + * @param replaceFilename replaces the downloaded file's name, if provided + * @param cache true if the response should contain a header that allows caching; false otherwise + * @return response entity + */ + private ResponseEntity buildFileResponse(Path path, String filename, Optional replaceFilename, boolean cache) { try { Path actualPath = path.resolve(filename); byte[] file = fileService.getFileForPath(actualPath); @@ -611,7 +623,7 @@ private ResponseEntity buildFileResponse(Path path, String filename, boo String contentType = lowerCaseFilename.endsWith("htm") || lowerCaseFilename.endsWith("html") || lowerCaseFilename.endsWith("svg") || lowerCaseFilename.endsWith("svgz") ? "attachment" : "inline"; - headers.setContentDisposition(ContentDisposition.builder(contentType).filename(filename).build()); + headers.setContentDisposition(ContentDisposition.builder(contentType).filename(replaceFilename.orElse(filename)).build()); var response = ResponseEntity.ok().headers(headers).contentType(getMediaTypeFromFilename(filename)).header("filename", filename); if (cache) { From 7bf8bcdb5850129c511849cff81960a919623483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= <38322605+JohannesStoehr@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:13:43 +0100 Subject: [PATCH 2/7] Adaptive learning: Improve import of competencies when some already exist (#9774) --- .../service/competency/CompetencyService.java | 14 +----- .../competency/CourseCompetencyService.java | 44 ++++++++++--------- .../competency/PrerequisiteService.java | 14 +----- .../competency-management-table.component.ts | 3 +- .../competency-management.component.ts | 5 ++- ...CompetencyPrerequisiteIntegrationTest.java | 24 ++++++++++ .../competency/CompetencyIntegrationTest.java | 6 +++ .../CourseCompetencyIntegrationTest.java | 6 +++ .../PrerequisiteIntegrationTest.java | 6 +++ 9 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java index 9fc79520601b..705c162c6341 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java @@ -2,9 +2,7 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -68,17 +66,7 @@ public CompetencyService(CompetencyRepository competencyRepository, Authorizatio * @return The set of imported competencies, each also containing the relations it is the tail competency for. */ public Set importCompetencies(Course course, Collection competencies, CompetencyImportOptionsDTO importOptions) { - var idToImportedCompetency = new HashMap(); - - for (var competency : competencies) { - Competency importedCompetency = new Competency(competency); - importedCompetency.setCourse(course); - - importedCompetency = competencyRepository.save(importedCompetency); - idToImportedCompetency.put(competency.getId(), new CompetencyWithTailRelationDTO(importedCompetency, new ArrayList<>())); - } - - return importCourseCompetencies(course, competencies, idToImportedCompetency, importOptions); + return importCourseCompetencies(course, competencies, importOptions, Competency::new); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index 8e47f7443297..cbe33e70b710 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -213,34 +212,39 @@ public void filterOutLearningObjectsThatUserShouldNotSee(CourseCompetency compet * @return The set of imported course competencies, each also containing the relations it is the tail competency for. */ public Set importCourseCompetencies(Course course, Collection courseCompetencies, CompetencyImportOptionsDTO importOptions) { - var idToImportedCompetency = new HashMap(); - - for (var courseCompetency : courseCompetencies) { - CourseCompetency importedCompetency = switch (courseCompetency) { - case Competency competency -> new Competency(competency); - case Prerequisite prerequisite -> new Prerequisite(prerequisite); - default -> throw new IllegalStateException("Unexpected value: " + courseCompetency); - }; - importedCompetency.setCourse(course); + Function createNewCourseCompetency = courseCompetency -> switch (courseCompetency) { + case Competency competency -> new Competency(competency); + case Prerequisite prerequisite -> new Prerequisite(prerequisite); + default -> throw new IllegalStateException("Unexpected value: " + courseCompetency); + }; - importedCompetency = courseCompetencyRepository.save(importedCompetency); - idToImportedCompetency.put(courseCompetency.getId(), new CompetencyWithTailRelationDTO(importedCompetency, new ArrayList<>())); - } - - return importCourseCompetencies(course, courseCompetencies, idToImportedCompetency, importOptions); + return importCourseCompetencies(course, courseCompetencies, importOptions, createNewCourseCompetency); } /** * Imports the given competencies and relations into a course * - * @param course the course to import into - * @param competenciesToImport the source competencies that were imported - * @param idToImportedCompetency map of original competency id to imported competency - * @param importOptions the import options + * @param course the course to import into + * @param competenciesToImport the source competencies that were imported + * @param importOptions the import options + * @param createNewCourseCompetency the function that creates new course competencies * @return The set of imported competencies, each also containing the relations it is the tail competency for. */ public Set importCourseCompetencies(Course course, Collection competenciesToImport, - Map idToImportedCompetency, CompetencyImportOptionsDTO importOptions) { + CompetencyImportOptionsDTO importOptions, Function createNewCourseCompetency) { + var idToImportedCompetency = new HashMap(); + + Set competenciesInCourse = courseCompetencyRepository.findAllForCourse(course.getId()); + + for (var courseCompetency : competenciesToImport) { + Optional existingCompetency = competenciesInCourse.stream().filter(competency -> competency.getTitle().equals(courseCompetency.getTitle())) + .filter(competency -> competency.getType().equals(courseCompetency.getType())).findFirst(); + CourseCompetency importedCompetency = existingCompetency.orElse(createNewCourseCompetency.apply(courseCompetency)); + importedCompetency.setCourse(course); + idToImportedCompetency.put(courseCompetency.getId(), new CompetencyWithTailRelationDTO(importedCompetency, new ArrayList<>())); + } + courseCompetencyRepository.saveAll(idToImportedCompetency.values().stream().map(CompetencyWithTailRelationDTO::competency).toList()); + if (course.getLearningPathsEnabled()) { var importedCompetencies = idToImportedCompetency.values().stream().map(CompetencyWithTailRelationDTO::competency).toList(); learningPathService.linkCompetenciesToLearningPathsOfCourse(importedCompetencies, course.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java index 996ebd7d4385..eb66a98d641f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java @@ -2,9 +2,7 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Set; @@ -59,17 +57,7 @@ public PrerequisiteService(PrerequisiteRepository prerequisiteRepository, Author * @return The set of imported prerequisites, each also containing the relations for which it is the tail prerequisite for. */ public Set importPrerequisites(Course course, Collection prerequisites, CompetencyImportOptionsDTO importOptions) { - var idToImportedPrerequisite = new HashMap(); - - for (var prerequisite : prerequisites) { - Prerequisite importedPrerequisite = new Prerequisite(prerequisite); - importedPrerequisite.setCourse(course); - - importedPrerequisite = prerequisiteRepository.save(importedPrerequisite); - idToImportedPrerequisite.put(prerequisite.getId(), new CompetencyWithTailRelationDTO(importedPrerequisite, new ArrayList<>())); - } - - return importCourseCompetencies(course, prerequisites, idToImportedPrerequisite, importOptions); + return importCourseCompetencies(course, prerequisites, importOptions, Prerequisite::new); } /** diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts index 28d4e4399ed9..7d939bdf5c67 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts @@ -95,7 +95,8 @@ export class CompetencyManagementTableComponent implements OnInit, OnDestroy { */ updateDataAfterImportAll(res: Array) { const importedCompetencies = res.map((dto) => dto.competency).filter((element): element is CourseCompetency => !!element); - this.courseCompetencies.push(...importedCompetencies); + const newCourseCompetencies = importedCompetencies.filter((competency) => !this.courseCompetencies.some((existingCompetency) => existingCompetency.id === competency.id)); + this.courseCompetencies.push(...newCourseCompetencies); this.allCompetencies.update((allCourseCompetencies) => allCourseCompetencies.concat(importedCompetencies)); } diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts index ddad887fc1a2..c0c3c3936f71 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts @@ -155,7 +155,10 @@ export class CompetencyManagementComponent implements OnInit { */ updateDataAfterImportAll(res: Array) { const importedCourseCompetencies = res.map((dto) => dto.competency!); - this.courseCompetencies.update((courseCompetencies) => courseCompetencies.concat(importedCourseCompetencies)); + const newCourseCompetencies = importedCourseCompetencies.filter( + (competency) => !this.courseCompetencies().some((existingCompetency) => existingCompetency.id === competency.id), + ); + this.courseCompetencies.update((courseCompetencies) => courseCompetencies.concat(newCourseCompetencies)); } onRemoveCompetency(competencyId: number) { diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java index f2f393231477..3a2f639c719f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java @@ -2,6 +2,7 @@ import static de.tum.cit.aet.artemis.core.util.TestResourceUtils.HalfSecond; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -16,6 +17,7 @@ import org.springframework.http.HttpStatus; import de.tum.cit.aet.artemis.atlas.AbstractAtlasIntegrationTest; +import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; @@ -27,6 +29,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportResponseDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; import de.tum.cit.aet.artemis.core.domain.Course; +import de.tum.cit.aet.artemis.core.domain.DomainObject; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.ExerciseMode; import de.tum.cit.aet.artemis.exercise.domain.IncludedInOverallScore; @@ -511,6 +514,27 @@ void shouldImportAllCompetencies(Function createCourse assertThat(competencyDTOList.get(1).tailRelations()).isNull(); } + // Test + void shouldImportAllCompetenciesWithSomeExisting(Function copyCourseCompetency, int expectedCourseCompetencies) throws Exception { + competencyUtilService.createCompetencies(course, 2); + prerequisiteUtilService.createPrerequisites(course, 2); + + CourseCompetency newCompetency = copyCourseCompetency.apply(courseCompetency); + newCompetency.setCourse(course2); + newCompetency = courseCompetencyRepository.save(newCompetency); + + CompetencyImportOptionsDTO importOptions = new CompetencyImportOptionsDTO(Set.of(), Optional.of(course.getId()), false, false, false, Optional.empty(), false); + importAllCall(course2.getId(), importOptions, HttpStatus.CREATED); + + course2 = courseRepository.findByIdWithExercisesAndLecturesAndLectureUnitsAndCompetenciesElseThrow(course2.getId()); + assertThat(course2.getPrerequisites().size() + course2.getCompetencies().size()).isEqualTo(expectedCourseCompetencies); + switch (newCompetency) { + case Competency competency -> assertThat(course2.getCompetencies().stream().map(DomainObject::getId)).contains(competency.getId()); + case Prerequisite prerequisite -> assertThat(course2.getPrerequisites().stream().map(DomainObject::getId)).contains(prerequisite.getId()); + default -> fail("Unexpected CourseCompetency subclass"); + } + } + // Test void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { createProgrammingExercise(ZonedDateTime.now(), ZonedDateTime.now()); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java index e4e59099d9e5..2f1ccc5c8536 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java @@ -284,6 +284,12 @@ void shouldImportAllCompetencies() throws Exception { super.shouldImportAllCompetencies(competencyUtilService::createCompetency); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void shouldImportAllCompetenciesWithSomeExisting() throws Exception { + shouldImportAllCompetenciesWithSomeExisting(Competency::new, 3); + } + @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java index 9b33684117fa..b12b09b6e191 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java @@ -194,6 +194,12 @@ void shouldImportAllCompetencies() throws Exception { super.shouldImportAllCompetencies(competencyUtilService::createCompetency); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void shouldImportAllCompetenciesWithSomeExisting() throws Exception { + shouldImportAllCompetenciesWithSomeExisting(Competency::new, 5); + } + @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java index 42293964287f..9e84044aa6c9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java @@ -278,6 +278,12 @@ void shouldImportAllCompetencies() throws Exception { super.shouldImportAllCompetencies(prerequisiteUtilService::createPrerequisite); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void shouldImportAllCompetenciesWithSomeExisting() throws Exception { + shouldImportAllCompetenciesWithSomeExisting(Prerequisite::new, 3); + } + @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { From 9d543b972cb1c63a18d99d764c2eff1c385962c3 Mon Sep 17 00:00:00 2001 From: Asli Aykan <56061820+asliayk@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:18:12 +0100 Subject: [PATCH 3/7] Communication: Add profile picture to user selector (#9764) --- .../course-users-selector.component.html | 19 ++++++++++++++++++- .../course-users-selector.module.ts | 3 ++- .../course-users-selector.component.spec.ts | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.html b/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.html index e2bc97df120d..d3e4254e97d6 100644 --- a/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.html +++ b/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.html @@ -13,13 +13,30 @@ [placeholder]="'artemisApp.userSelector.placeholderText' | artemisTranslate" [ngbTypeahead]="search" [inputFormatter]="usersFormatter" - [resultFormatter]="usersFormatter" + [resultTemplate]="resultTemplate" [editable]="false" [focusFirst]="false" (input)="onInputChange($event)" placement="bottom-start" #instance="ngbTypeahead" /> + +
+ + {{ getUserLabel(user) }} +
+
@if (isSearching) {
diff --git a/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts b/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts index ca44726f11bb..8c47180607d0 100644 --- a/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts +++ b/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts @@ -4,9 +4,10 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { NgModule } from '@angular/core'; import { CourseUsersSelectorComponent } from 'app/shared/course-users-selector/course-users-selector.component'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; @NgModule({ - imports: [CommonModule, FormsModule, ReactiveFormsModule, ArtemisSharedModule, ArtemisSharedComponentModule], + imports: [CommonModule, FormsModule, ReactiveFormsModule, ArtemisSharedModule, ArtemisSharedComponentModule, ProfilePictureComponent], exports: [CourseUsersSelectorComponent], declarations: [CourseUsersSelectorComponent], }) diff --git a/src/test/javascript/spec/component/shared/course-users-selector/course-users-selector.component.spec.ts b/src/test/javascript/spec/component/shared/course-users-selector/course-users-selector.component.spec.ts index ffb59f1825a3..74693eb15ba5 100644 --- a/src/test/javascript/spec/component/shared/course-users-selector/course-users-selector.component.spec.ts +++ b/src/test/javascript/spec/component/shared/course-users-selector/course-users-selector.component.spec.ts @@ -6,7 +6,7 @@ import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap'; import { By } from '@angular/platform-browser'; import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; -import { MockPipe, MockProvider } from 'ng-mocks'; +import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { CourseUsersSelectorComponent, SearchRoleGroup } from 'app/shared/course-users-selector/course-users-selector.component'; @@ -14,6 +14,7 @@ import { UserPublicInfoDTO } from 'app/core/user/user.model'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { TranslateModule } from '@ngx-translate/core'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; @Component({ template: ` @@ -48,7 +49,7 @@ describe('CourseUsersSelectorComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [CommonModule, FormsModule, ReactiveFormsModule, NgbTypeaheadModule, ArtemisSharedModule, ArtemisSharedComponentModule, TranslateModule.forRoot()], - declarations: [CourseUsersSelectorComponent, WrapperComponent, MockPipe(ArtemisTranslatePipe)], + declarations: [CourseUsersSelectorComponent, WrapperComponent, MockPipe(ArtemisTranslatePipe), MockComponent(ProfilePictureComponent)], providers: [MockProvider(CourseManagementService)], }).compileComponents(); })); @@ -142,6 +143,18 @@ describe('CourseUsersSelectorComponent', () => { expect(userSelectorComponent.selectedUsers).toEqual([exampleUserPublicInfoDTO]); expect(fixture.debugElement.query(By.css('.delete-user'))).toBeFalsy(); })); + + it('should render profile picture for users in dropdown', fakeAsync(() => { + const user = generateExampleUserPublicInfoDTO({}); + searchUsersSpy.mockReturnValue(of(new HttpResponse({ body: [user], status: 200 }))); + + changeInput(fixture.debugElement.nativeElement, 'test'); + tick(1000); + fixture.detectChanges(); + + const profilePicture = fixture.debugElement.query(By.directive(MockComponent(ProfilePictureComponent))); + expect(profilePicture).not.toBeNull(); + })); }); function getNativeInput(element: HTMLElement): HTMLInputElement { From 7bf4a93b29fff1a9e5a990897325165fb054d0f0 Mon Sep 17 00:00:00 2001 From: Simon Entholzer <33342534+SimonEntholzer@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:19:26 +0100 Subject: [PATCH 4/7] Development: Reactivate auxiliary repository integration tests for LocalVC (#9763) --- ...grammingIntegrationLocalCILocalVCTest.java | 3 + ...gExerciseIntegrationJenkinsGitlabTest.java | 18 ++ ...rammingExerciseIntegrationTestService.java | 37 ++-- ...rammingExerciseLocalVCIntegrationTest.java | 203 ++++++++++++++++++ 4 files changed, 238 insertions(+), 23 deletions(-) create mode 100644 src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java index fcbdc0bdef78..56adec3eb361 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/AbstractProgrammingIntegrationLocalCILocalVCTest.java @@ -52,6 +52,9 @@ public abstract class AbstractProgrammingIntegrationLocalCILocalVCTest extends A // External Repositories // Services + @Autowired + protected ProgrammingExerciseIntegrationTestService programmingExerciseIntegrationTestService; + @Autowired protected AeolusRequestMockProvider aeolusRequestMockProvider; diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java index 5812c2923e0e..4646464e8458 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationJenkinsGitlabTest.java @@ -74,6 +74,18 @@ void testExportSubmissionsByParticipationIds_includePracticeSubmissions() throws programmingExerciseIntegrationTestService.testExportSubmissionsByParticipationIds_includePracticeSubmissions(); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testExportSubmissionsByParticipationIds_addParticipantIdentifierToProjectNameError() throws Exception { + programmingExerciseIntegrationTestService.testExportSubmissionsByParticipationIds_addParticipantIdentifierToProjectNameError(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testExportSubmissionsByParticipationIds_addParticipantIdentifierToProjectName() throws Exception { + programmingExerciseIntegrationTestService.testExportSubmissionsByParticipationIds_addParticipantIdentifierToProjectName(); + } + @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testExportSubmissionsByParticipationIds() throws Exception { @@ -562,6 +574,12 @@ void createProgrammingExercise_projectTypeNotExpected_badRequest() throws Except programmingExerciseIntegrationTestService.createProgrammingExercise_projectTypeNotExpected_badRequest(); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void createProgrammingExercise_onlineCodeEditorNotExpected_badRequest() throws Exception { + programmingExerciseIntegrationTestService.createProgrammingExercise_onlineCodeEditorNotExpected_badRequest(); + } + private static Set generateSupportedLanguagesWithoutHaskell() { Set supportedLanguages = ArgumentSources.generateJenkinsSupportedLanguages(); supportedLanguages.remove(ProgrammingLanguage.HASKELL); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java index d35b974e32c7..6b716d45b5a9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseIntegrationTestService.java @@ -16,6 +16,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -292,10 +293,6 @@ void setup(String userPrefix, MockDelegate mockDelegate, VersionControlService v GitService.commit(localGit).setMessage("empty").setAllowEmpty(true).setSign(false).setAuthor("test", "test@test.com").call(); localGit.push().call(); - // we use the temp repository as remote origin for all repositories that are created during the - // TODO: distinguish between template, test and solution - doReturn(new GitUtilService.MockFileRepositoryUri(remoteRepoFile)).when(versionControlService).getCloneRepositoryUri(anyString(), anyString()); - this.plagiarismChecksTestReposDir = Files.createTempDirectory("jplag-repos").toFile(); } @@ -1433,6 +1430,8 @@ void importProgrammingExercise_vcsProjectWithSameTitleAlreadyExists_badRequest() } void importProgrammingExercise_updatesTestCaseIds() throws Exception { + doReturn(new GitUtilService.MockFileRepositoryUri(remoteRepoFile)).when(versionControlService).getCloneRepositoryUri(anyString(), anyString()); + programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationAndAuxiliaryRepositoriesElseThrow(programmingExercise.getId()); var tests = programmingExerciseUtilService.addTestCasesToProgrammingExercise(programmingExercise); var test1 = tests.getFirst(); @@ -1464,6 +1463,8 @@ void importProgrammingExercise_updatesTestCaseIds() throws Exception { var savedProgrammingExercise = programmingExerciseRepository.findByIdElseThrow(response.getId()); assertThat(savedProgrammingExercise.getProblemStatement()).isEqualTo(newProblemStatement); + + reset(versionControlService); } void exportSubmissionsByStudentLogins_notInstructorForExercise_forbidden() throws Exception { @@ -1920,11 +1921,11 @@ void testGetPlagiarismResultWithoutExercise() throws Exception { void testValidateValidAuxiliaryRepository() throws Exception { AuxiliaryRepositoryBuilder auxRepoBuilder = AuxiliaryRepositoryBuilder.defaults(); - testAuxRepo(auxRepoBuilder, HttpStatus.CREATED); + testAuxRepo(auxRepoBuilder, HttpStatus.OK); } void testValidateAuxiliaryRepositoryIdSetOnRequest() throws Exception { - testAuxRepo(AuxiliaryRepositoryBuilder.defaults().withId(0L), HttpStatus.BAD_REQUEST); + testAuxRepo(AuxiliaryRepositoryBuilder.defaults().withId(0L), HttpStatus.INTERNAL_SERVER_ERROR); } @@ -1953,12 +1954,12 @@ void testValidateAuxiliaryRepositoryWithInvalidCheckoutDirectory() throws Except void testValidateAuxiliaryRepositoryWithoutCheckoutDirectory() throws Exception { AuxiliaryRepositoryBuilder auxRepoBuilder = AuxiliaryRepositoryBuilder.defaults().withoutCheckoutDirectory(); - testAuxRepo(auxRepoBuilder, HttpStatus.CREATED); + testAuxRepo(auxRepoBuilder, HttpStatus.OK); } void testValidateAuxiliaryRepositoryWithBlankCheckoutDirectory() throws Exception { AuxiliaryRepositoryBuilder auxRepoBuilder = AuxiliaryRepositoryBuilder.defaults().withCheckoutDirectory(" "); - testAuxRepo(auxRepoBuilder, HttpStatus.CREATED); + testAuxRepo(auxRepoBuilder, HttpStatus.OK); } void testValidateAuxiliaryRepositoryWithTooLongCheckoutDirectory() throws Exception { @@ -1981,7 +1982,7 @@ void testValidateAuxiliaryRepositoryWithTooLongDescription() throws Exception { void testValidateAuxiliaryRepositoryWithoutDescription() throws Exception { AuxiliaryRepositoryBuilder auxRepoBuilder = AuxiliaryRepositoryBuilder.defaults().withoutDescription(); - testAuxRepo(auxRepoBuilder, HttpStatus.CREATED); + testAuxRepo(auxRepoBuilder, HttpStatus.OK); } void testGetAuxiliaryRepositoriesMissingExercise() throws Exception { @@ -2158,7 +2159,7 @@ public void addAuxiliaryRepositoryToExercise(ProgrammingExercise exercise) { } private String defaultAuxiliaryRepositoryEndpoint() { - return "/api/programming-exercises/setup"; + return "/api/programming-exercises"; } private String defaultResetEndpoint() { @@ -2191,17 +2192,7 @@ private void testAuxRepo(AuxiliaryRepositoryBuilder body, HttpStatus expectedSta private void testAuxRepo(List body, HttpStatus expectedStatus) throws Exception { programmingExercise.setAuxiliaryRepositories(body); - programmingExercise.setId(null); - programmingExercise.setSolutionParticipation(null); - programmingExercise.setTemplateParticipation(null); - programmingExercise.setChannelName("pe-test"); - programmingExercise.setShortName("ExerciseTitle"); - programmingExercise.setTitle("Title"); - if (expectedStatus == HttpStatus.CREATED) { - mockDelegate.mockConnectorRequestsForSetup(programmingExercise, false, false, false); - mockDelegate.mockGetProjectKeyFromAnyUrl(programmingExercise.getProjectKey()); - } - request.postWithResponseBody(defaultAuxiliaryRepositoryEndpoint(), programmingExercise, ProgrammingExercise.class, expectedStatus); + request.putWithResponseBody(defaultAuxiliaryRepositoryEndpoint(), programmingExercise, ProgrammingExercise.class, expectedStatus); } private static class AuxiliaryRepositoryBuilder { @@ -2275,8 +2266,8 @@ AuxiliaryRepository get() { } } - void testReEvaluateAndUpdateProgrammingExercise_instructorNotInCourse_forbidden() throws Exception { - userUtilService.addInstructor("other-instructors", userPrefix + "instructoralt"); + void testReEvaluateAndUpdateProgrammingExercise_instructorNotInCourse_forbidden(String testPrefix) throws Exception { + userUtilService.addInstructor("other-instructors", testPrefix + "instructoralt"); programmingExerciseUtilService.addCourseWithOneProgrammingExercise(); ProgrammingExercise programmingExercise = programmingExerciseTestRepository.findAllWithEagerTemplateAndSolutionParticipations().getFirst(); request.put("/api/programming-exercises/" + programmingExercise.getId() + "/re-evaluate", programmingExercise, HttpStatus.FORBIDDEN); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java new file mode 100644 index 000000000000..0194806e6028 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/ProgrammingExerciseLocalVCIntegrationTest.java @@ -0,0 +1,203 @@ +package de.tum.cit.aet.artemis.programming; + +import java.io.IOException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.test.context.support.WithMockUser; + +class ProgrammingExerciseLocalVCIntegrationTest extends AbstractProgrammingIntegrationLocalCILocalVCTest { + + private static final String TEST_PREFIX = "programmingexerciselocalvc"; + + @BeforeEach + void initTestCase() throws Exception { + programmingExerciseIntegrationTestService.setup(TEST_PREFIX, this, versionControlService, continuousIntegrationService); + } + + @AfterEach + void tearDown() throws IOException { + programmingExerciseIntegrationTestService.tearDown(); + } + + protected String getTestPrefix() { + return TEST_PREFIX; + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateValidAuxiliaryRepository() throws Exception { + programmingExerciseIntegrationTestService.testValidateValidAuxiliaryRepository(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryIdSetOnRequest() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryIdSetOnRequest(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithoutName() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithoutName(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithTooLongName() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithTooLongName(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithDuplicatedName() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithDuplicatedName(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithRestrictedName() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithRestrictedName(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithInvalidCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithInvalidCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithoutCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithoutCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithBlankCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithBlankCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithTooLongCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithTooLongCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithDuplicatedCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithDuplicatedCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithNullCheckoutDirectory() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithNullCheckoutDirectory(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithTooLongDescription() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithTooLongDescription(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testValidateAuxiliaryRepositoryWithoutDescription() throws Exception { + programmingExerciseIntegrationTestService.testValidateAuxiliaryRepositoryWithoutDescription(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testGetAuxiliaryRepositoriesMissingExercise() throws Exception { + programmingExerciseIntegrationTestService.testGetAuxiliaryRepositoriesMissingExercise(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testGetAuxiliaryRepositoriesOk() throws Exception { + programmingExerciseIntegrationTestService.testGetAuxiliaryRepositoriesOk(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testGetAuxiliaryRepositoriesEmptyOk() throws Exception { + programmingExerciseIntegrationTestService.testGetAuxiliaryRepositoriesEmptyOk(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void testGetAuxiliaryRepositoriesForbidden() throws Exception { + programmingExerciseIntegrationTestService.testGetAuxiliaryRepositoriesForbidden(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void testExportAuxiliaryRepositoryForbidden() throws Exception { + programmingExerciseIntegrationTestService.testExportAuxiliaryRepositoryForbidden(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testExportAuxiliaryRepositoryBadRequest() throws Exception { + programmingExerciseIntegrationTestService.testExportAuxiliaryRepositoryBadRequest(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testExportAuxiliaryRepositoryExerciseNotFound() throws Exception { + programmingExerciseIntegrationTestService.testExportAuxiliaryRepositoryExerciseNotFound(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testExportAuxiliaryRepositoryRepositoryNotFound() throws Exception { + programmingExerciseIntegrationTestService.testExportAuxiliaryRepositoryRepositoryNotFound(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructoralt1", roles = "INSTRUCTOR") + void testReEvaluateAndUpdateProgrammingExercise_instructorNotInCourse_forbidden() throws Exception { + programmingExerciseIntegrationTestService.testReEvaluateAndUpdateProgrammingExercise_instructorNotInCourse_forbidden(TEST_PREFIX); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testReEvaluateAndUpdateProgrammingExercise_notFound() throws Exception { + programmingExerciseIntegrationTestService.testReEvaluateAndUpdateProgrammingExercise_notFound(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testReEvaluateAndUpdateProgrammingExercise_isNotSameGivenExerciseIdInRequestBody_conflict() throws Exception { + programmingExerciseIntegrationTestService.testReEvaluateAndUpdateProgrammingExercise_isNotSameGivenExerciseIdInRequestBody_conflict(); + } + // + // @Test + // @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + // void test_redirectGetSolutionRepositoryFilesWithoutContent() throws Exception { + // programmingExerciseIntegrationTestService.test_redirectGetSolutionRepositoryFilesWithoutContent(); + // } + // + // @Test + // @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + // void test_redirectGetTemplateRepositoryFilesWithContent() throws Exception { + // programmingExerciseIntegrationTestService.test_redirectGetTemplateRepositoryFilesWithContent(); + // } + // + // @Test + // @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + // void testRedirectGetParticipationRepositoryFilesWithContentAtCommit() throws Exception { + // programmingExerciseIntegrationTestService.testRedirectGetParticipationRepositoryFilesWithContentAtCommit(); + // } + // + // @Test + // @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + // void testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden() throws Exception { + // programmingExerciseIntegrationTestService.testRedirectGetParticipationRepositoryFilesWithContentAtCommitForbidden(); + // } + + // TODO add all other tests +} From f05b0a5214d0f59d6722113a22c437fab927c577 Mon Sep 17 00:00:00 2001 From: Ajayvir Singh <38434017+AjayvirS@users.noreply.github.com> Date: Sat, 16 Nov 2024 10:22:40 +0100 Subject: [PATCH 5/7] Plagiarism checks: Fix scrolling issue on side-by-side viewer for modeling (#9748) --- .../shared/modeling-editor.component.html | 6 +++++- .../shared/modeling-editor.component.scss | 19 ++++++++++++++++++- .../exam/ExamExerciseGroupsPage.ts | 3 ++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.html b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.html index d29c46e6c02f..1c7637eb8455 100644 --- a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.html +++ b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.html @@ -108,7 +108,11 @@