diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.html b/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.html index 35ab48c1854a..958122437f2b 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.html +++ b/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.html @@ -1,6 +1,6 @@
-
+
(); + course = input.required(); courseId: number; examId: number; @@ -39,14 +49,6 @@ export class ExamScoresAverageScoresGraphComponent implements OnInit { xScaleMax = 100; lookup: NameToValueMap = {}; - constructor( - private navigationUtilService: ArtemisNavigationUtilService, - private activatedRoute: ActivatedRoute, - private service: StatisticsService, - private translateService: TranslateService, - private localeConversionService: LocaleConversionService, - ) {} - ngOnInit(): void { this.activatedRoute.params.subscribe((params) => { this.courseId = +params['courseId']; @@ -56,12 +58,12 @@ export class ExamScoresAverageScoresGraphComponent implements OnInit { } private initializeChart(): void { - this.lookup[this.averageScores.title] = { absoluteValue: this.averageScores.averagePoints! }; - const exerciseGroupAverage = this.averageScores.averagePercentage ? this.averageScores.averagePercentage : 0; - this.ngxData.push({ name: this.averageScores.title, value: exerciseGroupAverage }); + this.lookup[this.averageScores().title] = { absoluteValue: this.averageScores().averagePoints! }; + const exerciseGroupAverage = this.averageScores().averagePercentage ?? 0; + this.ngxData.push({ name: this.averageScores().title, value: exerciseGroupAverage }); this.ngxColor.domain.push(this.determineColor(true, exerciseGroupAverage)); this.xScaleMax = this.xScaleMax > exerciseGroupAverage ? this.xScaleMax : exerciseGroupAverage; - this.averageScores.exerciseResults.forEach((exercise) => { + this.averageScores().exerciseResults.forEach((exercise) => { const exerciseAverage = exercise.averagePercentage ?? 0; this.xScaleMax = this.xScaleMax > exerciseAverage ? this.xScaleMax : exerciseAverage; this.ngxData.push({ name: exercise.exerciseId + ' ' + exercise.title, value: exerciseAverage }); @@ -77,14 +79,14 @@ export class ExamScoresAverageScoresGraphComponent implements OnInit { } roundAndPerformLocalConversion(points: number | undefined) { - return this.localeConversionService.toLocaleString(roundValueSpecifiedByCourseSettings(points, this.course), this.course!.accuracyOfScores!); + return this.localeConversionService.toLocaleString(roundValueSpecifiedByCourseSettings(points, this.course()), this.course()!.accuracyOfScores!); } /** * We navigate to the exercise scores page when the user clicks on a data point */ navigateToExercise(exerciseId: number, exerciseType: ExerciseType) { - navigateToExamExercise(this.navigationUtilService, this.courseId, this.examId, this.averageScores.exerciseGroupId, exerciseType, exerciseId, 'scores'); + navigateToExamExercise(this.navigationUtilService, this.courseId, this.examId, this.averageScores().exerciseGroupId, exerciseType, exerciseId, 'scores'); } /** diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts b/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts index 94a6f375b3b7..3c09d79156e4 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts @@ -1,8 +1,8 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Subscription, forkJoin, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { SortService } from 'app/shared/service/sort.service'; import { download, generateCsv, mkConfig } from 'export-to-csv'; import { @@ -58,6 +58,14 @@ import { USERNAME_KEY, } from 'app/shared/export/export-constants'; import { BonusStrategy } from 'app/entities/bonus.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../../shared/language/translate.directive'; +import { ExportModule } from '../../shared/export/export.module'; +import { NgStyle } from '@angular/common'; +import { ArtemisParticipantScoresModule } from '../../shared/participant-scores/participant-scores.module'; +import { ExamScoresAverageScoresGraphComponent } from './exam-scores-average-scores-graph.component'; +import { ArtemisSharedComponentModule } from '../../shared/components/shared-component.module'; +import { ArtemisSharedCommonModule } from '../../shared/shared-common.module'; export enum MedianType { PASSED, @@ -70,8 +78,31 @@ export enum MedianType { templateUrl: './exam-scores.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./exam-scores.component.scss', '../../shared/chart/vertical-bar-chart.scss'], + standalone: true, + imports: [ + FaIconComponent, + TranslateDirective, + ExportModule, + RouterLink, + NgStyle, + ArtemisParticipantScoresModule, + ExamScoresAverageScoresGraphComponent, + ArtemisSharedComponentModule, + ArtemisSharedCommonModule, + ], }) export class ExamScoresComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private examService = inject(ExamManagementService); + private sortService = inject(SortService); + private alertService = inject(AlertService); + private changeDetector = inject(ChangeDetectorRef); + private languageHelper = inject(JhiLanguageHelper); + private localeConversionService = inject(LocaleConversionService); + private participantScoresService = inject(ParticipantScoresService); + private gradingSystemService = inject(GradingSystemService); + private courseManagementService = inject(CourseManagementService); + public examScoreDTO: ExamScoreDTO; public exerciseGroups: ExerciseGroup[]; public studentResults: StudentResult[]; @@ -130,18 +161,6 @@ export class ExamScoresComponent implements OnInit, OnDestroy { faExclamationTriangle = faExclamationTriangle; private languageChangeSubscription?: Subscription; - constructor( - private route: ActivatedRoute, - private examService: ExamManagementService, - private sortService: SortService, - private alertService: AlertService, - private changeDetector: ChangeDetectorRef, - private languageHelper: JhiLanguageHelper, - private localeConversionService: LocaleConversionService, - private participantScoresService: ParticipantScoresService, - private gradingSystemService: GradingSystemService, - private courseManagementService: CourseManagementService, - ) {} ngOnInit() { this.route.params.subscribe((params) => { diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores.module.ts b/src/main/webapp/app/exam/exam-scores/exam-scores.module.ts index e439030cb776..fc4d3df9b992 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores.module.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-scores.module.ts @@ -12,7 +12,6 @@ import { ArtemisParticipantScoresModule } from 'app/shared/participant-scores/pa import { ExportModule } from 'app/shared/export/export.module'; @NgModule({ - declarations: [ExamScoresComponent, ExamScoresAverageScoresGraphComponent], imports: [ ArtemisSharedModule, ArtemisExamScoresRoutingModule, @@ -23,6 +22,8 @@ import { ExportModule } from 'app/shared/export/export.module'; BarChartModule, ArtemisParticipantScoresModule, ExportModule, + ExamScoresComponent, + ExamScoresAverageScoresGraphComponent, ], }) export class ArtemisExamScoresModule {} diff --git a/src/test/javascript/spec/component/exam/exam-scores/exam-scores-average-scores-graph.component.spec.ts b/src/test/javascript/spec/component/exam/exam-scores/exam-scores-average-scores-graph.component.spec.ts index f962626e8e37..49da1a162d28 100644 --- a/src/test/javascript/spec/component/exam/exam-scores/exam-scores-average-scores-graph.component.spec.ts +++ b/src/test/javascript/spec/component/exam/exam-scores/exam-scores-average-scores-graph.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; -import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { MockDirective, MockModule, MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { ExamScoresAverageScoresGraphComponent } from 'app/exam/exam-scores/exam-scores-average-scores-graph.component'; @@ -9,7 +9,6 @@ import { MockTranslateService } from '../../../helpers/mocks/service/mock-transl import { AggregatedExerciseGroupResult, AggregatedExerciseResult } from 'app/exam/exam-scores/exam-score-dtos.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { BarChartModule } from '@swimlane/ngx-charts'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { GraphColors } from 'app/entities/statistics.model'; import { NgxChartsSingleSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { ExerciseType } from 'app/entities/exercise.model'; @@ -56,7 +55,7 @@ describe('ExamScoresAverageScoresGraphComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, MockModule(BarChartModule), RouterModule.forRoot([])], - declarations: [ExamScoresAverageScoresGraphComponent, MockPipe(ArtemisTranslatePipe), MockDirective(TranslateDirective)], + declarations: [ExamScoresAverageScoresGraphComponent, MockDirective(TranslateDirective)], providers: [ MockProvider(CourseManagementService, { find: () => { @@ -70,16 +69,14 @@ describe('ExamScoresAverageScoresGraphComponent', () => { }), { provide: TranslateService, useClass: MockTranslateService }, ], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExamScoresAverageScoresGraphComponent); - component = fixture.componentInstance; - navigateToExerciseMock = jest.spyOn(component, 'navigateToExercise').mockImplementation(); - - component.averageScores = returnValue; - fixture.detectChanges(); - }); + }).compileComponents(); + + fixture = TestBed.createComponent(ExamScoresAverageScoresGraphComponent); + component = fixture.componentInstance; + navigateToExerciseMock = jest.spyOn(component, 'navigateToExercise').mockImplementation(); + + fixture.componentRef.setInput('averageScores', returnValue); + fixture.detectChanges(); }); it('should set ngx data objects and bar colors correctly', () => { @@ -99,8 +96,9 @@ describe('ExamScoresAverageScoresGraphComponent', () => { }); const adaptExpectedData = (averagePoints: number, newColor: string, expectedColorDomain: string[], expectedData: NgxChartsSingleSeriesDataEntry[]) => { - component.averageScores.averagePoints = averagePoints; - component.averageScores.averagePercentage = averagePoints * 10; + component.averageScores().averagePoints = averagePoints; + component.averageScores().averagePercentage = averagePoints * 10; + expectedColorDomain[0] = newColor; expectedData[0].value = averagePoints * 10; component.ngxColor.domain = []; @@ -146,7 +144,10 @@ describe('ExamScoresAverageScoresGraphComponent', () => { it('should look up absolute value', () => { const roundAndPerformLocalConversionSpy = jest.spyOn(component, 'roundAndPerformLocalConversion'); - component.course = { accuracyOfScores: 2 }; + const updatedCourse = { + accuracyOfScores: 2, + }; + fixture.componentRef.setInput('course', updatedCourse); component.lookup['test'] = { absoluteValue: 40 }; const result = component.lookupAbsoluteValue('test'); diff --git a/src/test/javascript/spec/component/exam/exam-scores/exam-scores.component.spec.ts b/src/test/javascript/spec/component/exam/exam-scores/exam-scores.component.spec.ts index df9ddaa86480..3d82fa3ba673 100644 --- a/src/test/javascript/spec/component/exam/exam-scores/exam-scores.component.spec.ts +++ b/src/test/javascript/spec/component/exam/exam-scores/exam-scores.component.spec.ts @@ -1,5 +1,6 @@ -import { HttpResponse } from '@angular/common/http'; +import { HttpResponse, provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { TranslateService } from '@ngx-translate/core'; @@ -14,15 +15,14 @@ import { ExerciseResult, StudentResult, } from 'app/exam/exam-scores/exam-score-dtos.model'; +import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ExamScoresComponent, MedianType } from 'app/exam/exam-scores/exam-scores.component'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { HelpIconComponent } from 'app/shared/components/help-icon.component'; -import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; import { ParticipantScoresService, ScoresDTO } from 'app/shared/participant-scores/participant-scores.service'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { SortService } from 'app/shared/service/sort.service'; import { cloneDeep } from 'lodash-es'; -import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { EMPTY, of } from 'rxjs'; import { GradingSystemService } from 'app/grading-system/grading-system.service'; import { GradingScale } from 'app/entities/grading-scale.model'; @@ -35,7 +35,6 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { MockRouter } from '../../../helpers/mocks/mock-router'; import { AccountService } from 'app/core/auth/account.service'; import { MockRouterLinkDirective } from '../../../helpers/mocks/directive/mock-router-link.directive'; -import { ParticipantScoresDistributionComponent } from 'app/shared/participant-scores/participant-scores-distribution/participant-scores-distribution.component'; import { LocaleConversionService } from 'app/shared/service/locale-conversion.service'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import { CsvDecimalSeparator, CsvExportOptions, CsvFieldSeparator, CsvQuoteStrings } from 'app/shared/export/export-modal.component'; @@ -55,9 +54,9 @@ import { REGISTRATION_NUMBER_KEY, USERNAME_KEY, } from 'app/shared/export/export-constants'; -import { ExportButtonComponent } from 'app/shared/export/export-button.component'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; import { BonusStrategy } from 'app/entities/bonus.model'; +import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; describe('ExamScoresComponent', () => { let fixture: ComponentFixture; @@ -276,30 +275,28 @@ describe('ExamScoresComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ + imports: [MockModule(BrowserAnimationsModule)], declarations: [ ExamScoresComponent, MockPipe(ArtemisTranslatePipe), MockComponent(FaIconComponent), - MockComponent(HelpIconComponent), - MockComponent(ExportButtonComponent), MockDirective(TranslateDirective), MockDirective(SortByDirective), MockDirective(SortDirective), - MockDirective(DeleteButtonDirective), MockComponent(ExamScoresAverageScoresGraphComponent), MockRouterLinkDirective, - MockComponent(ParticipantScoresDistributionComponent), ], providers: [ { provide: ActivatedRoute, useValue: { params: of({ courseId: 1, examId: 1 }) } }, { provide: Router, useClass: MockRouter }, + { provide: TranslateService, useClass: MockTranslateService }, + provideHttpClient(), + provideHttpClientTesting(), MockProvider(AccountService), MockProvider(ArtemisNavigationUtilService), - MockProvider(TranslateService), MockProvider(ExamManagementService), MockProvider(SortService), MockProvider(AlertService), - MockProvider(ParticipantScoresService), MockProvider(GradingSystemService, { findGradingScaleForExam: () => { return of( @@ -328,18 +325,16 @@ describe('ExamScoresComponent', () => { }, }), ], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExamScoresComponent); - comp = fixture.componentInstance; - examService = fixture.debugElement.injector.get(ExamManagementService); - gradingSystemService = fixture.debugElement.injector.get(GradingSystemService); - const participationScoreService = fixture.debugElement.injector.get(ParticipantScoresService); - findExamScoresSpy = jest - .spyOn(participationScoreService, 'findExamScores') - .mockReturnValue(of(new HttpResponse({ body: [examScoreStudent1, examScoreStudent2, examScoreStudent3] }))); - }); + }).compileComponents(); + + fixture = TestBed.createComponent(ExamScoresComponent); + comp = fixture.componentInstance; + examService = fixture.debugElement.injector.get(ExamManagementService); + gradingSystemService = fixture.debugElement.injector.get(GradingSystemService); + const participationScoreService = fixture.debugElement.injector.get(ParticipantScoresService); + findExamScoresSpy = jest + .spyOn(participationScoreService, 'findExamScores') + .mockReturnValue(of(new HttpResponse({ body: [examScoreStudent1, examScoreStudent2, examScoreStudent3] }))); }); afterEach(() => {