Skip to content

Commit

Permalink
improve client code test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonEntholzer committed Oct 29, 2024
1 parent a4d86a8 commit e230bc4
Showing 1 changed file with 140 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<CodeEditorInstructorAndEditorContainerComponent>;
let containerDebugElement: DebugElement;
let domainService: DomainService;
Expand All @@ -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 }>;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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', () => {
Expand All @@ -232,22 +235,22 @@ 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 });

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 });
Expand All @@ -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);
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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();
Expand Down Expand Up @@ -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 });

Expand All @@ -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('..');
});
});
});

0 comments on commit e230bc4

Please sign in to comment.