diff --git a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html
index 11e5ee4c828f..ba2349c564d3 100644
--- a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html
+++ b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html
@@ -1,7 +1,7 @@
@if (headlines?.length && headlines.length > 1) {
}
-@for (section of sections; track section) {
+@for (section of sections(); track section) {
{{ section.headline | artemisTranslate }}
@for (detail of section.details; track $index) {
diff --git a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts
index 25a5a6ca72e6..27eeca162243 100644
--- a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts
+++ b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts
@@ -1,7 +1,7 @@
-import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
+import { Component, OnDestroy, OnInit, ViewEncapsulation, inject, input } from '@angular/core';
import { isEmpty } from 'lodash-es';
import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service';
-import { ButtonSize, TooltipPlacement } from 'app/shared/components/button.component';
+import { ButtonSize } from 'app/shared/components/button.component';
import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model';
import { ModelingExerciseService } from 'app/exercises/modeling/manage/modeling-exercise.service';
import { AlertService } from 'app/core/util/alert.service';
@@ -50,11 +50,13 @@ export class DetailOverviewListComponent implements OnInit, OnDestroy {
protected readonly FeatureToggle = FeatureToggle;
protected readonly ButtonSize = ButtonSize;
protected readonly ProgrammingExerciseParticipationType = ProgrammingExerciseParticipationType;
+ protected readonly CHAT = IrisSubSettingsType.CHAT;
- readonly CHAT = IrisSubSettingsType.CHAT;
+ private readonly modelingExerciseService = inject(ModelingExerciseService);
+ private readonly alertService = inject(AlertService);
+ private readonly profileService = inject(ProfileService);
- @Input()
- sections: DetailOverviewSection[];
+ sections = input.required();
// headline list for navigation bar
headlines: { id: string; translationKey: string }[];
@@ -64,14 +66,8 @@ export class DetailOverviewListComponent implements OnInit, OnDestroy {
profileSubscription: Subscription;
isLocalVC = false;
- constructor(
- private modelingExerciseService: ModelingExerciseService,
- private alertService: AlertService,
- private profileService: ProfileService,
- ) {}
-
ngOnInit() {
- this.headlines = this.sections.map((section) => {
+ this.headlines = this.sections().map((section) => {
return {
id: section.headline.replaceAll('.', '-'),
translationKey: section.headline,
@@ -98,6 +94,4 @@ export class DetailOverviewListComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.profileSubscription?.unsubscribe();
}
-
- protected readonly TooltipPlacement = TooltipPlacement;
}
diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts
index 1a1a95462530..baed01da2383 100644
--- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts
+++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts
@@ -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 } from 'rxjs';
+import { Subject, Subscription, of } 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';
@@ -57,6 +57,9 @@ 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 { 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',
@@ -65,15 +68,32 @@ import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.s
encapsulation: ViewEncapsulation.None,
})
export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
- readonly dayjs = dayjs;
- readonly ActionType = ActionType;
- readonly ProgrammingExerciseParticipationType = ProgrammingExerciseParticipationType;
- readonly FeatureToggle = FeatureToggle;
- readonly ProgrammingLanguage = ProgrammingLanguage;
- readonly PROGRAMMING = ExerciseType.PROGRAMMING;
- readonly ButtonSize = ButtonSize;
- readonly AssessmentType = AssessmentType;
- readonly documentationType: DocumentationType = 'Programming';
+ protected readonly dayjs = dayjs;
+ protected readonly ActionType = ActionType;
+ protected readonly ProgrammingExerciseParticipationType = ProgrammingExerciseParticipationType;
+ protected readonly FeatureToggle = FeatureToggle;
+ protected readonly ProgrammingLanguage = ProgrammingLanguage;
+ protected readonly PROGRAMMING = ExerciseType.PROGRAMMING;
+ protected readonly ButtonSize = ButtonSize;
+ protected readonly AssessmentType = AssessmentType;
+ protected readonly documentationType: DocumentationType = 'Programming';
+
+ protected readonly faUndo = faUndo;
+ protected readonly faTrash = faTrash;
+ protected readonly faBook = faBook;
+ protected readonly faWrench = faWrench;
+ protected readonly faCheckDouble = faCheckDouble;
+ protected readonly faTable = faTable;
+ protected readonly faExclamationTriangle = faExclamationTriangle;
+ protected readonly faFileSignature = faFileSignature;
+ protected readonly faListAlt = faListAlt;
+ protected readonly faChartBar = faChartBar;
+ protected readonly faLightbulb = faLightbulb;
+ protected readonly faPencilAlt = faPencilAlt;
+ protected readonly faUsers = faUsers;
+ protected readonly faEye = faEye;
+ protected readonly faUserCheck = faUserCheck;
+ protected readonly faRobot = faRobot;
programmingExercise: ProgrammingExercise;
programmingExerciseBuildConfig?: ProgrammingExerciseBuildConfig;
@@ -106,10 +126,7 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
private activatedRouteSubscription: Subscription;
private templateAndSolutionParticipationSubscription: Subscription;
- private profileInfoSubscription: Subscription;
private irisSettingsSubscription: Subscription;
- private submissionPolicySubscription: Subscription;
- private buildLogsSubscription: Subscription;
private exerciseStatisticsSubscription: Subscription;
private dialogErrorSource = new Subject();
@@ -117,24 +134,6 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
exerciseDetailSections: DetailOverviewSection[];
- // Icons
- faUndo = faUndo;
- faTrash = faTrash;
- faBook = faBook;
- faWrench = faWrench;
- faCheckDouble = faCheckDouble;
- faTable = faTable;
- faExclamationTriangle = faExclamationTriangle;
- faFileSignature = faFileSignature;
- faListAlt = faListAlt;
- faChartBar = faChartBar;
- faLightbulb = faLightbulb;
- faPencilAlt = faPencilAlt;
- faUsers = faUsers;
- faEye = faEye;
- faUserCheck = faUserCheck;
- faRobot = faRobot;
-
constructor(
private activatedRoute: ActivatedRoute,
private accountService: AccountService,
@@ -184,13 +183,15 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.templateAndSolutionParticipationSubscription = this.programmingExerciseService
.findWithTemplateAndSolutionParticipationAndLatestResults(programmingExercise.id!)
- .subscribe((updatedProgrammingExercise) => {
- this.programmingExercise = updatedProgrammingExercise.body!;
-
- this.setLatestCoveredLineRatio();
- this.loadingTemplateParticipationResults = false;
- this.loadingSolutionParticipationResults = false;
- this.profileInfoSubscription = this.profileService.getProfileInfo().subscribe(async (profileInfo) => {
+ .pipe(
+ tap((updatedProgrammingExercise) => {
+ this.programmingExercise = updatedProgrammingExercise.body!;
+ this.setLatestCoveredLineRatio();
+ this.loadingTemplateParticipationResults = false;
+ this.loadingSolutionParticipationResults = false;
+ }),
+ mergeMap(() => this.profileService.getProfileInfo()),
+ tap((profileInfo) => {
if (profileInfo) {
if (this.programmingExercise.projectKey && this.programmingExercise.templateParticipation?.buildPlanId) {
this.programmingExercise.templateParticipation.buildPlanUrl = createBuildPlanUrl(
@@ -215,38 +216,41 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
if (this.irisEnabled) {
this.irisSettingsSubscription = this.irisSettingsService.getCombinedCourseSettings(this.courseId).subscribe((settings) => {
this.irisChatEnabled = settings?.irisChatSettings?.enabled ?? false;
- this.exerciseDetailSections = this.getExerciseDetails();
});
}
}
+ }),
+ mergeMap(() => this.programmingExerciseSubmissionPolicyService.getSubmissionPolicyOfProgrammingExercise(exerciseId)),
+ tap((submissionPolicy) => {
+ this.programmingExercise.submissionPolicy = submissionPolicy;
+ }),
+ mergeMap(() => this.programmingExerciseService.getDiffReport(exerciseId)),
+ tap((gitDiffReport) => {
+ this.processGitDiffReport(gitDiffReport, false);
+ }),
+ mergeMap(() =>
+ this.programmingExercise.isAtLeastEditor ? this.programmingExerciseService.getBuildLogStatistics(exerciseId!) : of([] as BuildLogStatisticsDTO),
+ ),
+ tap((buildLogStatistics) => {
+ if (this.programmingExercise.isAtLeastEditor) {
+ this.programmingExercise.buildLogStatistics = buildLogStatistics;
+ }
+ }),
+ )
+ .subscribe({
+ next: () => {
+ this.setLatestCoveredLineRatio();
+ this.checkAndAlertInconsistencies();
+ this.plagiarismCheckSupported = this.programmingLanguageFeatureService.getProgrammingLanguageFeature(
+ programmingExercise.programmingLanguage,
+ ).plagiarismCheckSupported;
+
+ /** we make sure to await the results of the subscriptions (switchMap) to only call {@link getExerciseDetails} once */
this.exerciseDetailSections = this.getExerciseDetails();
- });
-
- this.submissionPolicySubscription = this.programmingExerciseSubmissionPolicyService
- .getSubmissionPolicyOfProgrammingExercise(exerciseId!)
- .subscribe((submissionPolicy) => {
- this.programmingExercise.submissionPolicy = submissionPolicy;
- this.exerciseDetailSections = this.getExerciseDetails();
- });
-
- this.loadGitDiffReport();
-
- // the build logs endpoint requires at least editor privileges
- if (this.programmingExercise.isAtLeastEditor) {
- this.buildLogsSubscription = this.programmingExerciseService
- .getBuildLogStatistics(exerciseId!)
- .subscribe((buildLogStatistics) => (this.programmingExercise.buildLogStatistics = buildLogStatistics));
- this.exerciseDetailSections = this.getExerciseDetails();
- }
-
- this.setLatestCoveredLineRatio();
-
- this.checkAndAlertInconsistencies();
-
- this.plagiarismCheckSupported = this.programmingLanguageFeatureService.getProgrammingLanguageFeature(
- programmingExercise.programmingLanguage,
- ).plagiarismCheckSupported;
- this.exerciseDetailSections = this.getExerciseDetails();
+ },
+ error: (error) => {
+ this.alertService.error(error.message);
+ },
});
this.exerciseStatisticsSubscription = this.statisticsService.getExerciseStatistics(exerciseId!).subscribe((statistics: ExerciseManagementStatisticsDto) => {
@@ -259,13 +263,17 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
this.dialogErrorSource.unsubscribe();
this.activatedRouteSubscription?.unsubscribe();
this.templateAndSolutionParticipationSubscription?.unsubscribe();
- this.profileInfoSubscription?.unsubscribe();
this.irisSettingsSubscription?.unsubscribe();
- this.submissionPolicySubscription?.unsubscribe();
- this.buildLogsSubscription?.unsubscribe();
this.exerciseStatisticsSubscription?.unsubscribe();
}
+ /**
+ * BE CAREFUL WHEN CALLING THIS METHOD!
+ * This method can cause child components to re-render, which can lead to re-initializations resulting
+ * in unnecessary requests putting load on the server.
+ *
+ * When adding a new call to this method, make sure that no duplicated and unnecessary requests are made.
+ */
getExerciseDetails(): DetailOverviewSection[] {
const exercise = this.programmingExercise;
exercise.buildConfig = this.programmingExerciseBuildConfig;
@@ -780,29 +788,37 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy {
return link;
}
- loadGitDiffReport() {
- this.programmingExerciseService.getDiffReport(this.programmingExercise.id!).subscribe((gitDiffReport) => {
- if (
- gitDiffReport &&
- (this.programmingExercise.gitDiffReport?.templateRepositoryCommitHash !== gitDiffReport.templateRepositoryCommitHash ||
- this.programmingExercise.gitDiffReport?.solutionRepositoryCommitHash !== gitDiffReport.solutionRepositoryCommitHash)
- ) {
- this.programmingExercise.gitDiffReport = gitDiffReport;
- gitDiffReport.programmingExercise = this.programmingExercise;
- this.addedLineCount =
- gitDiffReport.entries
- ?.map((entry) => entry.lineCount)
- .filter((lineCount) => lineCount)
- .map((lineCount) => lineCount!)
- .reduce((lineCount1, lineCount2) => lineCount1 + lineCount2, 0) ?? 0;
- this.removedLineCount =
- gitDiffReport.entries
- ?.map((entry) => entry.previousLineCount)
- .filter((lineCount) => lineCount)
- .map((lineCount) => lineCount!)
- .reduce((lineCount1, lineCount2) => lineCount1 + lineCount2, 0) ?? 0;
+ /**
+ *
+ * @param gitDiffReport
+ * @param updateDetailSections set to false when called from OnInit, as another method will take care to update the
+ * {@link exerciseDetailSections} to prevent unnecessary renderings and duplicated requests,
+ * see description of {@link getExerciseDetails}
+ */
+ private processGitDiffReport(gitDiffReport: ProgrammingExerciseGitDiffReport | undefined, updateDetailSections: boolean = true): void {
+ const isGitDiffReportUpdated =
+ gitDiffReport &&
+ (this.programmingExercise.gitDiffReport?.templateRepositoryCommitHash !== gitDiffReport.templateRepositoryCommitHash ||
+ this.programmingExercise.gitDiffReport?.solutionRepositoryCommitHash !== gitDiffReport.solutionRepositoryCommitHash);
+ if (isGitDiffReportUpdated) {
+ this.programmingExercise.gitDiffReport = gitDiffReport;
+ gitDiffReport.programmingExercise = this.programmingExercise;
+
+ const calculateLineCount = (entries: { lineCount?: number; previousLineCount?: number }[] = [], key: 'lineCount' | 'previousLineCount') =>
+ entries.map((entry) => entry[key] ?? 0).reduce((sum, count) => sum + count, 0);
+
+ this.addedLineCount = calculateLineCount(gitDiffReport.entries, 'lineCount');
+ this.removedLineCount = calculateLineCount(gitDiffReport.entries, 'previousLineCount');
+
+ if (updateDetailSections) {
this.exerciseDetailSections = this.getExerciseDetails();
}
+ }
+ }
+
+ loadGitDiffReport() {
+ this.programmingExerciseService.getDiffReport(this.programmingExercise.id!).subscribe((gitDiffReport) => {
+ this.processGitDiffReport(gitDiffReport);
});
}
diff --git a/src/test/javascript/spec/component/detail-overview-list.component.spec.ts b/src/test/javascript/spec/component/detail-overview-list.component.spec.ts
index 61e891b11e35..8d5998e3053a 100644
--- a/src/test/javascript/spec/component/detail-overview-list.component.spec.ts
+++ b/src/test/javascript/spec/component/detail-overview-list.component.spec.ts
@@ -56,7 +56,7 @@ describe('DetailOverviewList', () => {
});
it('should initialize and destroy', () => {
- component.sections = sections;
+ fixture.componentRef.setInput('sections', sections);
fixture.detectChanges();
expect(component.headlines).toStrictEqual([{ id: 'headline-1', translationKey: 'headline.1' }]);
expect(component.headlinesRecord).toStrictEqual({ 'headline.1': 'headline-1' });
@@ -67,7 +67,7 @@ describe('DetailOverviewList', () => {
});
it('should escape all falsy values', () => {
- component.sections = [
+ fixture.componentRef.setInput('sections', [
{
headline: 'some-section',
details: [
@@ -81,7 +81,7 @@ describe('DetailOverviewList', () => {
},
],
},
- ];
+ ]);
fixture.detectChanges();
const detailListTitleDOMElements = fixture.nativeElement.querySelectorAll('dt[id^=detail-title]');
expect(detailListTitleDOMElements).toHaveLength(1);
diff --git a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts
index 8563723b42b8..f355ba91a806 100644
--- a/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts
+++ b/src/test/javascript/spec/component/programming-exercise/programming-exercise-detail.component.spec.ts
@@ -22,9 +22,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service';
import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
import { MockProgrammingExerciseGradingService } from '../../helpers/mocks/service/mock-programming-exercise-grading.service';
-import { ProgrammingExerciseGitDiffReport } from 'app/entities/hestia/programming-exercise-git-diff-report.model';
import { ProgrammingExerciseSolutionEntry } from 'app/entities/hestia/programming-exercise-solution-entry.model';
-import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto';
import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model';
import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model';
import { HttpResponse } from '@angular/common/http';
@@ -34,17 +32,24 @@ import {
ProgrammingLanguageFeatureService,
} 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';
+import { SubmissionPolicyService } from '../../../../../main/webapp/app/exercises/programming/manage/services/submission-policy.service';
-describe('ProgrammingExercise Management Detail Component', () => {
+describe('ProgrammingExerciseDetailComponent', () => {
let comp: ProgrammingExerciseDetailComponent;
let fixture: ComponentFixture;
let statisticsService: StatisticsService;
let exerciseService: ProgrammingExerciseService;
let alertService: AlertService;
let profileService: ProfileService;
+ let submissionPolicyService: SubmissionPolicyService;
let programmingLanguageFeatureService: ProgrammingLanguageFeatureService;
let statisticsServiceStub: jest.SpyInstance;
let gitDiffReportStub: jest.SpyInstance;
+ let profileServiceStub: jest.SpyInstance;
+ let submissionPolicyServiceStub: jest.SpyInstance;
let buildLogStatisticsStub: jest.SpyInstance;
let findWithTemplateAndSolutionParticipationStub: jest.SpyInstance;
let router: Router;
@@ -59,6 +64,9 @@ describe('ProgrammingExercise Management Detail Component', () => {
solutionParticipation: {
id: 2,
} as SolutionProgrammingExerciseParticipation,
+ buildConfig: {
+ testwiseCoverageEnabled: true,
+ } as BuildConfig,
} as ProgrammingExercise;
const exerciseStatistics = {
@@ -128,6 +136,8 @@ describe('ProgrammingExercise Management Detail Component', () => {
alertService = fixture.debugElement.injector.get(AlertService);
exerciseService = fixture.debugElement.injector.get(ProgrammingExerciseService);
profileService = fixture.debugElement.injector.get(ProfileService);
+ submissionPolicyService = fixture.debugElement.injector.get(SubmissionPolicyService);
+
programmingLanguageFeatureService = fixture.debugElement.injector.get(ProgrammingLanguageFeatureService);
router = fixture.debugElement.injector.get(Router);
modalService = fixture.debugElement.injector.get(NgbModal);
@@ -136,6 +146,8 @@ describe('ProgrammingExercise Management Detail Component', () => {
.spyOn(exerciseService, 'findWithTemplateAndSolutionParticipationAndLatestResults')
.mockReturnValue(of(new HttpResponse({ body: mockProgrammingExercise })));
gitDiffReportStub = jest.spyOn(exerciseService, 'getDiffReport').mockReturnValue(of(gitDiffReport));
+ profileServiceStub = jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo));
+ submissionPolicyServiceStub = jest.spyOn(submissionPolicyService, 'getSubmissionPolicyOfProgrammingExercise').mockReturnValue(of(undefined));
buildLogStatisticsStub = jest.spyOn(exerciseService, 'getBuildLogStatistics').mockReturnValue(of(buildLogStatistics));
jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(profileInfo));
@@ -148,6 +160,19 @@ describe('ProgrammingExercise Management Detail Component', () => {
jest.restoreAllMocks();
});
+ it('should reload on participation change', fakeAsync(() => {
+ const loadDiffSpy = jest.spyOn(comp, 'loadGitDiffReport');
+ jest.spyOn(exerciseService, 'getLatestResult').mockReturnValue({ successful: true });
+ jest.spyOn(exerciseService, 'getLatestFullTestwiseCoverageReport').mockReturnValue(of({ coveredLineRatio: 0.5 }));
+ comp.programmingExercise = mockProgrammingExercise;
+ comp.programmingExerciseBuildConfig = mockProgrammingExercise.buildConfig;
+ comp.onParticipationChange();
+ tick();
+ expect(loadDiffSpy).toHaveBeenCalledOnce();
+ expect(gitDiffReportStub).toHaveBeenCalledOnce();
+ expect(comp.programmingExercise.coveredLinesRatio).toBe(0.5);
+ }));
+
describe('onInit for course exercise', () => {
const programmingExercise = new ProgrammingExercise(new Course(), undefined);
programmingExercise.id = 123;
@@ -163,6 +188,8 @@ describe('ProgrammingExercise Management Detail Component', () => {
// THEN
expect(findWithTemplateAndSolutionParticipationStub).toHaveBeenCalledOnce();
+ expect(profileServiceStub).toHaveBeenCalledTimes(2);
+ expect(submissionPolicyServiceStub).toHaveBeenCalledOnce();
expect(gitDiffReportStub).toHaveBeenCalledOnce();
expect(statisticsServiceStub).toHaveBeenCalledOnce();
await Promise.resolve();
@@ -285,18 +312,6 @@ describe('ProgrammingExercise Management Detail Component', () => {
expect(comp.isBuildPlanEditable).toBe(editable);
});
- it('should reload on participation change', fakeAsync(() => {
- const loadDiffSpy = jest.spyOn(comp, 'loadGitDiffReport');
- jest.spyOn(exerciseService, 'getLatestResult').mockReturnValue({ successful: true });
- jest.spyOn(exerciseService, 'getLatestFullTestwiseCoverageReport').mockReturnValue(of({ coveredLineRatio: 0.5 }));
- comp.programmingExercise = mockProgrammingExercise;
- comp.programmingExercise.buildConfig!.testwiseCoverageEnabled = true;
- comp.onParticipationChange();
- tick();
- expect(loadDiffSpy).toHaveBeenCalledOnce();
- expect(comp.programmingExercise.coveredLinesRatio).toBe(0.5);
- }));
-
it('should combine template commit', () => {
const combineCommitsSpy = jest.spyOn(exerciseService, 'combineTemplateRepositoryCommits').mockReturnValue(of(new HttpResponse({ body: null })));
const successSpy = jest.spyOn(alertService, 'success');