Skip to content

Commit

Permalink
Re-add and fix client tests
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-glombik committed Nov 18, 2024
1 parent 06d0d25 commit 6253315
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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, of } from 'rxjs';
import { Subject, Subscription } from 'rxjs';
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';
Expand Down Expand Up @@ -57,9 +57,8 @@ import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-setting
import { Detail } from 'app/detail-overview-list/detail.model';
import { Competency } from 'app/entities/competency.model';
import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.service';
import { switchMap, tap } from 'rxjs/operators';
import { mergeMap, tap } from 'rxjs/operators';
import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model';
import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto';

@Component({
selector: 'jhi-programming-exercise-detail',
Expand Down Expand Up @@ -194,7 +193,7 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.loadingTemplateParticipationResults = false;
this.loadingSolutionParticipationResults = false;
}),
switchMap(() => this.profileService.getProfileInfo()),
mergeMap(() => this.profileService.getProfileInfo()),
tap(async (profileInfo) => {
if (profileInfo) {
if (this.programmingExercise.projectKey && this.programmingExercise.templateParticipation?.buildPlanId) {
Expand Down Expand Up @@ -224,22 +223,14 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
}
}
}),
switchMap(() => this.programmingExerciseSubmissionPolicyService.getSubmissionPolicyOfProgrammingExercise(exerciseId!)),
mergeMap(() => this.programmingExerciseSubmissionPolicyService.getSubmissionPolicyOfProgrammingExercise(exerciseId!)),
tap((submissionPolicy) => {
this.programmingExercise.submissionPolicy = submissionPolicy;
}),
switchMap(() => this.programmingExerciseService.getDiffReport(this.programmingExercise.id!)),
mergeMap(() => this.programmingExerciseService.getDiffReport(this.programmingExercise.id!)),
tap((gitDiffReport) => {
this.processGitDiffReport(gitDiffReport);
}),
switchMap(() =>
this.programmingExercise.isAtLeastEditor ? this.programmingExerciseService.getBuildLogStatistics(exerciseId!) : of([] as BuildLogStatisticsDTO),
),
tap((buildLogStatistics) => {
if (this.programmingExercise.isAtLeastEditor) {
this.programmingExercise.buildLogStatistics = buildLogStatistics;
}
}),
)
.subscribe({
next: () => {
Expand All @@ -248,6 +239,8 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.plagiarismCheckSupported = this.programmingLanguageFeatureService.getProgrammingLanguageFeature(
programmingExercise.programmingLanguage,
).plagiarismCheckSupported;

/** we make sure to await the results of the subscriptions (mergeMap) to only call {@link getExerciseDetails} once */
this.exerciseDetailSections = this.getExerciseDetails();
},
error: (error) => {
Expand All @@ -258,6 +251,13 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.exerciseStatisticsSubscription = this.statisticsService.getExerciseStatistics(exerciseId!).subscribe((statistics: ExerciseManagementStatisticsDto) => {
this.doughnutStats = statistics;
});

if (this.programmingExercise.isAtLeastEditor) {
this.buildLogsSubscription = this.programmingExerciseService
.getBuildLogStatistics(exerciseId!)
.subscribe((buildLogStatistics) => (this.programmingExercise.buildLogStatistics = buildLogStatistics));
this.exerciseDetailSections = this.getExerciseDetails();
}
});
}

Expand All @@ -272,6 +272,13 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.exerciseStatisticsSubscription?.unsubscribe();
}

/**
* <strong>BE CAREFUL WHEN CALLING THIS METHOD!</strong><br>
* This method can cause child components to re-render, which can lead to re-initializations resulting
* in unnecessary requests putting load on the server.
*
* <strong>When adding a new call to this method, make sure that no duplicated and unnecessary requests are made.</strong>
*/
getExerciseDetails(): DetailOverviewSection[] {
const exercise = this.programmingExercise;
exercise.buildConfig = this.programmingExerciseBuildConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
} from 'app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service';
import { MockRouter } from '../../helpers/mocks/mock-router';
import { BuildConfig } from '../../../../../main/webapp/app/entities/programming/build-config.model';
import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model';
import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto';

describe('ProgrammingExerciseDetailComponent', () => {
let comp: ProgrammingExerciseDetailComponent;
Expand All @@ -43,6 +45,8 @@ describe('ProgrammingExerciseDetailComponent', () => {
let profileService: ProfileService;
let programmingLanguageFeatureService: ProgrammingLanguageFeatureService;
let statisticsServiceStub: jest.SpyInstance;
let gitDiffReportStub: jest.SpyInstance;
let buildLogStatisticsStub: jest.SpyInstance;
let findWithTemplateAndSolutionParticipationStub: jest.SpyInstance;
let router: Router;
let modalService: NgbModal;
Expand Down Expand Up @@ -75,6 +79,30 @@ describe('ProgrammingExerciseDetailComponent', () => {
resolvedPostsInPercent: 50,
} as ExerciseManagementStatisticsDto;

const gitDiffReport = {
templateRepositoryCommitHash: 'x1',
solutionRepositoryCommitHash: 'x2',
entries: [
{
previousFilePath: '/src/test.java',
filePath: '/src/test.java',
previousStartLine: 1,
startLine: 1,
previousLineCount: 2,
lineCount: 2,
},
],
} as ProgrammingExerciseGitDiffReport;

const buildLogStatistics = {
buildCount: 5,
agentSetupDuration: 2.5,
testDuration: 3,
scaDuration: 2,
totalJobDuration: 7.5,
dependenciesDownloadedCount: 6,
} as BuildLogStatisticsDTO;

const profileInfo = {
activeProfiles: [],
} as unknown as ProfileInfo;
Expand Down Expand Up @@ -111,6 +139,8 @@ describe('ProgrammingExerciseDetailComponent', () => {
findWithTemplateAndSolutionParticipationStub = jest
.spyOn(exerciseService, 'findWithTemplateAndSolutionParticipationAndLatestResults')
.mockReturnValue(of(new HttpResponse<ProgrammingExercise>({ body: mockProgrammingExercise })));
gitDiffReportStub = jest.spyOn(exerciseService, 'getDiffReport').mockReturnValue(of(gitDiffReport));
buildLogStatisticsStub = jest.spyOn(exerciseService, 'getBuildLogStatistics').mockReturnValue(of(buildLogStatistics));

jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo));
jest.spyOn(programmingLanguageFeatureService, 'getProgrammingLanguageFeature').mockReturnValue({
Expand All @@ -131,6 +161,7 @@ describe('ProgrammingExerciseDetailComponent', () => {
comp.onParticipationChange();
tick();
expect(loadDiffSpy).toHaveBeenCalledOnce();
expect(gitDiffReportStub).toHaveBeenCalledOnce();
expect(comp.programmingExercise.coveredLinesRatio).toBe(0.5);
}));

Expand All @@ -157,6 +188,25 @@ describe('ProgrammingExerciseDetailComponent', () => {
expect(comp.doughnutStats.resolvedPostsInPercent).toBe(50);
expect(comp.doughnutStats.absoluteAveragePoints).toBe(5);
});

it.each([true, false])(
'should only call service method to get build log statistics onInit if the user is at least an editor for this exercise',
async (isEditor: boolean) => {
const programmingExercise = new ProgrammingExercise(new Course(), undefined);
programmingExercise.id = 123;
programmingExercise.isAtLeastEditor = isEditor;
jest.spyOn(exerciseService, 'findWithTemplateAndSolutionParticipationAndLatestResults').mockReturnValue(
of({ body: programmingExercise } as unknown as HttpResponse<ProgrammingExercise>),
);
comp.ngOnInit();
await new Promise((r) => setTimeout(r, 100));
if (isEditor) {
expect(buildLogStatisticsStub).toHaveBeenCalledOnce();
} else {
expect(buildLogStatisticsStub).not.toHaveBeenCalled();
}
},
);
});

describe('onInit for exam exercise', () => {
Expand Down

0 comments on commit 6253315

Please sign in to comment.