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 c3cb01c72352..a8ba374dd6b6 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 @@ -66,9 +66,10 @@ import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { CodeEditorMonacoComponent } from 'app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component'; import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; import { mockCodeEditorMonacoViewChildren } from '../../helpers/mocks/mock-instance.helper'; +import { REPOSITORY } from 'app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component'; describe('CodeEditorInstructorIntegration', () => { - let container: CodeEditorInstructorAndEditorContainerComponent; + let comp: CodeEditorInstructorAndEditorContainerComponent; let containerFixture: ComponentFixture; let containerDebugElement: DebugElement; let domainService: DomainService; @@ -81,6 +82,7 @@ describe('CodeEditorInstructorIntegration', () => { let getBuildLogsStub: jest.SpyInstance; let findWithParticipationsStub: jest.SpyInstance; let getLatestResultWithFeedbacksStub: jest.SpyInstance; + let navigateSpy: jest.SpyInstance; let checkIfRepositoryIsCleanSubject: Subject<{ isClean: boolean }>; let getRepositoryContentSubject: Subject<{ [fileName: string]: FileType }>; @@ -142,7 +144,7 @@ describe('CodeEditorInstructorIntegration', () => { .compileComponents() .then(() => { containerFixture = TestBed.createComponent(CodeEditorInstructorAndEditorContainerComponent); - container = containerFixture.componentInstance; + comp = containerFixture.componentInstance; containerDebugElement = containerFixture.debugElement; const codeEditorRepositoryService = containerDebugElement.injector.get(CodeEditorRepositoryService); @@ -172,6 +174,7 @@ describe('CodeEditorInstructorIntegration', () => { .spyOn(programmingExerciseParticipationService, 'getLatestResultWithFeedback') .mockReturnValue(throwError(() => new Error('no result'))); getBuildLogsStub = jest.spyOn(buildLogService, 'getBuildLogs'); + navigateSpy = jest.spyOn(TestBed.inject(Router), 'navigate'); findWithParticipationsStub = jest.spyOn(programmingExerciseService, 'findWithTemplateAndSolutionParticipationAndResults'); findWithParticipationsStub.mockReturnValue(findWithParticipationsSubject); @@ -203,12 +206,12 @@ describe('CodeEditorInstructorIntegration', () => { }); const initContainer = (exercise: ProgrammingExercise) => { - container.ngOnInit(); + comp.ngOnInit(); routeSubject.next({ exerciseId: 1 }); - expect(container.codeEditorContainer).toBeUndefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer).toBeUndefined(); // Have to use this as it's a component expect(findWithParticipationsStub).toHaveBeenCalledOnce(); expect(findWithParticipationsStub).toHaveBeenCalledWith(exercise.id); - expect(container.loadingState).toBe(container.LOADING_STATE.INITIALIZING); + expect(comp.loadingState).toBe(comp.LOADING_STATE.INITIALIZING); }; it('should load the exercise and select the template participation if no participation id is provided', () => { @@ -232,7 +235,7 @@ describe('CodeEditorInstructorIntegration', () => { getFeedbackDetailsForResultStub.mockReturnValue(of([])); const setDomainSpy = jest.spyOn(domainService, 'setDomain'); // @ts-ignore - (container.router as MockRouter).setUrl('code-editor-instructor/1'); + (comp.router as MockRouter).setUrl('code-editor-instructor/1'); initContainer(exercise); findWithParticipationsSubject.next({ body: exercise }); @@ -240,14 +243,14 @@ describe('CodeEditorInstructorIntegration', () => { expect(getLatestResultWithFeedbacksStub).not.toHaveBeenCalled(); expect(setDomainSpy).toHaveBeenCalledOnce(); expect(setDomainSpy).toHaveBeenCalledWith([DomainType.PARTICIPATION, exercise.templateParticipation]); - expect(container.exercise).toEqual(exercise); - expect(container.selectedRepository).toBe(container.REPOSITORY.TEMPLATE); - expect(container.selectedParticipation).toEqual(container.selectedParticipation); - expect(container.loadingState).toBe(container.LOADING_STATE.CLEAR); - expect(container.domainChangeSubscription).toBeDefined(); // External complex object + expect(comp.exercise).toEqual(exercise); + expect(comp.selectedRepository).toBe(comp.REPOSITORY.TEMPLATE); + expect(comp.selectedParticipation).toEqual(comp.selectedParticipation); + expect(comp.loadingState).toBe(comp.LOADING_STATE.CLEAR); + expect(comp.domainChangeSubscription).toBeDefined(); // External complex object containerFixture.detectChanges(); - expect(container.codeEditorContainer.grid).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.grid).toBeDefined(); // Have to use this as it's a component checkIfRepositoryIsCleanSubject.next({ isClean: true }); getRepositoryContentSubject.next({ file: FileType.FILE, folder: FileType.FOLDER }); @@ -258,13 +261,13 @@ describe('CodeEditorInstructorIntegration', () => { // Once called by each build-output & instructions expect(getFeedbackDetailsForResultStub).toHaveBeenCalledTimes(2); - expect(container.codeEditorContainer.grid).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.fileBrowser).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.actions).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions.participation).toEqual(exercise.templateParticipation); - expect(container.resultComp).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.grid).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.fileBrowser).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.actions).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions.participation).toEqual(exercise.templateParticipation); + expect(comp.resultComp).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component // Called once by each build-output, instructions, result and twice by instructor-exercise-status (=templateParticipation,solutionParticipation) & expect(subscribeForLatestResultOfParticipationStub).toHaveBeenCalledTimes(5); @@ -278,11 +281,11 @@ describe('CodeEditorInstructorIntegration', () => { findWithParticipationsSubject.error('fatal error'); expect(setDomainSpy).not.toHaveBeenCalled(); - expect(container.loadingState).toBe(container.LOADING_STATE.FETCHING_FAILED); - expect(container.selectedRepository).toBeUndefined(); + expect(comp.loadingState).toBe(comp.LOADING_STATE.FETCHING_FAILED); + expect(comp.selectedRepository).toBeUndefined(); containerFixture.detectChanges(); - expect(container.codeEditorContainer).toBeUndefined(); + expect(comp.codeEditorContainer).toBeUndefined(); }); it('should load test repository if specified in url', () => { @@ -296,37 +299,37 @@ describe('CodeEditorInstructorIntegration', () => { } as ProgrammingExercise; const setDomainSpy = jest.spyOn(domainService, 'setDomain'); // @ts-ignore - (container.router as MockRouter).setUrl('code-editor-instructor/1/test'); - container.ngOnDestroy(); + (comp.router as MockRouter).setUrl('code-editor-instructor/1/test'); + comp.ngOnDestroy(); initContainer(exercise); findWithParticipationsSubject.next({ body: exercise }); expect(setDomainSpy).toHaveBeenCalledOnce(); expect(setDomainSpy).toHaveBeenCalledWith([DomainType.TEST_REPOSITORY, exercise]); - expect(container.selectedParticipation).toEqual(exercise.templateParticipation); - expect(container.selectedRepository).toBe(container.REPOSITORY.TEST); + expect(comp.selectedParticipation).toEqual(exercise.templateParticipation); + expect(comp.selectedRepository).toBe(comp.REPOSITORY.TEST); expect(getBuildLogsStub).not.toHaveBeenCalled(); expect(getFeedbackDetailsForResultStub).not.toHaveBeenCalled(); containerFixture.detectChanges(); - expect(container.codeEditorContainer).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions.participation).toEqual(exercise.templateParticipation); - expect(container.resultComp).toBeUndefined(); - expect(container.codeEditorContainer.buildOutput).toBeUndefined(); + expect(comp.codeEditorContainer).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions.participation).toEqual(exercise.templateParticipation); + expect(comp.resultComp).toBeUndefined(); + expect(comp.codeEditorContainer.buildOutput).toBeUndefined(); }); const checkSolutionRepository = (exercise: ProgrammingExercise) => { - expect(container.selectedRepository).toBe(container.REPOSITORY.SOLUTION); - expect(container.selectedParticipation).toEqual(exercise.solutionParticipation); - expect(container.codeEditorContainer).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions).toBeDefined(); // Have to use this as it's a component - expect(container.resultComp).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.buildOutput.participation).toEqual(exercise.solutionParticipation); - expect(container.editableInstructions.participation).toEqual(exercise.solutionParticipation); + expect(comp.selectedRepository).toBe(comp.REPOSITORY.SOLUTION); + expect(comp.selectedParticipation).toEqual(exercise.solutionParticipation); + expect(comp.codeEditorContainer).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions).toBeDefined(); // Have to use this as it's a component + expect(comp.resultComp).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.buildOutput.participation).toEqual(exercise.solutionParticipation); + expect(comp.editableInstructions.participation).toEqual(exercise.solutionParticipation); }; it('should be able to switch between the repos and update the child components accordingly', () => { @@ -345,25 +348,25 @@ describe('CodeEditorInstructorIntegration', () => { // Start with assignment repository // @ts-ignore - (container.router as MockRouter).setUrl('code-editor-instructor/1/2'); - container.ngOnInit(); + (comp.router as MockRouter).setUrl('code-editor-instructor/1/2'); + comp.ngOnInit(); routeSubject.next({ exerciseId: 1, participationId: 2 }); findWithParticipationsSubject.next({ body: exercise }); containerFixture.detectChanges(); - expect(container.selectedRepository).toBe(container.REPOSITORY.ASSIGNMENT); - expect(container.selectedParticipation).toEqual(exercise.studentParticipations[0]); - expect(container.codeEditorContainer).toBeDefined(); // Have to use this as it's a component - expect(container.editableInstructions).toBeDefined(); // Have to use this as it's a component - expect(container.resultComp).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component - expect(container.codeEditorContainer.buildOutput.participation).toEqual(exercise.studentParticipations[0]); - expect(container.editableInstructions.participation).toEqual(exercise.studentParticipations[0]); + expect(comp.selectedRepository).toBe(comp.REPOSITORY.ASSIGNMENT); + expect(comp.selectedParticipation).toEqual(exercise.studentParticipations[0]); + expect(comp.codeEditorContainer).toBeDefined(); // Have to use this as it's a component + expect(comp.editableInstructions).toBeDefined(); // Have to use this as it's a component + expect(comp.resultComp).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.buildOutput).toBeDefined(); // Have to use this as it's a component + expect(comp.codeEditorContainer.buildOutput.participation).toEqual(exercise.studentParticipations[0]); + expect(comp.editableInstructions.participation).toEqual(exercise.studentParticipations[0]); // New select solution repository // @ts-ignore - (container.router as MockRouter).setUrl('code-editor-instructor/1/4'); + (comp.router as MockRouter).setUrl('code-editor-instructor/1/4'); routeSubject.next({ exerciseId: 1, participationId: 4 }); containerFixture.detectChanges(); @@ -393,8 +396,8 @@ describe('CodeEditorInstructorIntegration', () => { // Start with assignment repository // @ts-ignore - (container.router as MockRouter).setUrl('code-editor-instructor/1/3'); - container.ngOnInit(); + (comp.router as MockRouter).setUrl('code-editor-instructor/1/3'); + comp.ngOnInit(); routeSubject.next({ exerciseId: 1, participationId: 3 }); findWithParticipationsSubject.next({ body: exercise }); @@ -404,4 +407,89 @@ describe('CodeEditorInstructorIntegration', () => { expect(setDomainSpy).toHaveBeenCalledWith([DomainType.PARTICIPATION, exercise.solutionParticipation]); checkSolutionRepository(exercise); }); + + describe('Repository Navigation', () => { + const exercise = { + id: 1, + problemStatement, + studentParticipations: [{ id: 2 }], + templateParticipation: { id: 3 }, + solutionParticipation: { id: 4 }, + course: { id: 1 }, + } as ProgrammingExercise; + + beforeEach(() => { + comp.exercise = exercise; + }); + + it('should navigate to template participation repository from auxiliary repository', () => { + comp.selectedRepository = REPOSITORY.AUXILIARY; + comp.selectTemplateParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['../..', exercise.templateParticipation!.id], expect.any(Object)); + }); + + it('should navigate to template participation repository from test repository', () => { + comp.selectedRepository = REPOSITORY.TEST; + comp.selectTemplateParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['..', exercise.templateParticipation!.id], expect.any(Object)); + }); + + it('should navigate to solution participation repository from auxiliary repository', () => { + comp.selectedRepository = REPOSITORY.AUXILIARY; + comp.selectSolutionParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['../..', exercise.solutionParticipation!.id], expect.any(Object)); + }); + + it('should navigate to solution participation repository from test repository', () => { + comp.selectedRepository = REPOSITORY.TEST; + comp.selectSolutionParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['..', exercise.solutionParticipation!.id], expect.any(Object)); + }); + + it('should navigate to assignment participation repository from auxiliary repository', () => { + comp.selectedRepository = REPOSITORY.AUXILIARY; + comp.selectAssignmentParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['../..', exercise.studentParticipations![0].id], expect.any(Object)); + }); + + it('should navigate to assignment participation repository from test repository', () => { + comp.selectedRepository = REPOSITORY.TEST; + comp.selectAssignmentParticipation(); + expect(navigateSpy).toHaveBeenCalledWith(['..', exercise.studentParticipations![0].id], expect.any(Object)); + }); + + it('should navigate to test repository from auxiliary repository', () => { + comp.selectedRepository = REPOSITORY.AUXILIARY; + comp.selectTestRepository(); + expect(navigateSpy).toHaveBeenCalledWith(['../..', 'test'], expect.any(Object)); + }); + + it('should navigate to test repository from test repository', () => { + comp.selectedRepository = REPOSITORY.TEST; + comp.selectTestRepository(); + expect(navigateSpy).toHaveBeenCalledWith(['..', 'test'], expect.any(Object)); + }); + + it('should navigate to auxiliary repository with provided repositoryId', () => { + const repositoryId = 4; + comp.selectedRepository = REPOSITORY.AUXILIARY; + comp.selectAuxiliaryRepository(repositoryId); + expect(navigateSpy).toHaveBeenCalledWith(['../..', 'auxiliary', repositoryId], expect.any(Object)); + }); + + it('should navigate to auxiliary repository from test repository', () => { + const repositoryId = 4; + comp.selectedRepository = REPOSITORY.TEST; + comp.selectAuxiliaryRepository(repositoryId); + expect(navigateSpy).toHaveBeenCalledWith(['..', 'auxiliary', repositoryId], expect.any(Object)); + }); + + it('should return the correct navigation path based on selected repository', () => { + comp.selectedRepository = REPOSITORY.AUXILIARY; + expect(comp.up()).toBe('../..'); + + comp.selectedRepository = REPOSITORY.TEST; // Or any other non-auxiliary value + expect(comp.up()).toBe('..'); + }); + }); });