import {fakeAsync, TestBed, tick} from '@angular/core/testing'; import {MicroLessonMetricsService} from './micro-lesson-metrics.service'; import {PostMessageBridgeFactory} from 'ngx-post-message'; import {GameActionsService} from '../game-actions.service'; import {MicroLessonInfoOx, TimerOxService, UserInfoOx} from 'ox-core'; import {AppInfoOxService} from '../app-info-ox.service'; import {SubLevelService} from '../sub-level.service'; import {ChallengeService} from '../challenge.service'; import {ExerciseOx} from '../game.service'; import {BehaviorSubject} from 'rxjs'; import {ScoreStarsService} from './score-stars.service'; import {AnswerService} from '../answer.service'; import {ExerciseMetricsBonusTime} from 'ox-core'; import {MicroLessonLevelConfiguration} from '../../../../../ox-core/src/lib/models/micro-lesson-info-ox'; import {EventEmitter} from '@angular/core'; describe('MicroLessonMetricsService', () => { let metricsService: MicroLessonMetricsService; let gameActionsService: GameActionsService; const starServiceSpy = jasmine.createSpyObj>('ScoreStarsService', ['calculateStars', 'calculateScore']); const sublevelServiceSpy = jasmine.createSpyObj('ScoreStarsService', ['getMaxSubLevel']); sublevelServiceSpy.currentSublevel = new BehaviorSubject(1); starServiceSpy.calculateScore.and.callFake((a) => a.score = 5000); starServiceSpy.calculateStars.and.callFake((a) => a.stars = 3); const challengeServiceSpy = jasmine.createSpyObj>('ChallengeService', ['nextChallenge']); challengeServiceSpy.currentExercise = new BehaviorSubject>(new ExerciseOx(1, 1, new ExerciseMetricsBonusTime(10, 20), [])); const microlessonConfig = new MicroLessonLevelConfiguration(); microlessonConfig.exercisesToUpSubLevel = [1, 1, 1, 1]; const microLessonInfo = new MicroLessonInfoOx( [microlessonConfig], 1, 1, '', [], undefined); const appInfoSpy = jasmine.createSpyObj('AppInfoOxService', ['getMetricsTable']); appInfoSpy.microLessonInfo = microLessonInfo; appInfoSpy.userInfo = new UserInfoOx('', false, 'a test token'); beforeEach(() => { TestBed.configureTestingModule({ providers: [ ChallengeService, TimerOxService, GameActionsService, SubLevelService, AnswerService, PostMessageBridgeFactory, MicroLessonMetricsService, {provide: SubLevelService, useValue: sublevelServiceSpy}, {provide: AppInfoOxService, useValue: appInfoSpy}, {provide: ChallengeService, useValue: challengeServiceSpy}, {provide: ScoreStarsService, useValue: starServiceSpy} ] }); metricsService = TestBed.get(MicroLessonMetricsService); gameActionsService = TestBed.get(GameActionsService); }); it('should be created', () => { const service: MicroLessonMetricsService = TestBed.get(MicroLessonMetricsService); expect(service).toBeTruthy(); }); it('metrics should start after start game', () => { gameActionsService.startGame.emit({info: undefined, level: 1}); expect(metricsService.currentMetrics.level).toBe(1); expect(metricsService.currentMetrics.microLessonId).toBe(''); }); it('metrics should update lastSublevelReached', () => { const service: SubLevelService = TestBed.get(SubLevelService); mockStartMetrics(); service.currentSublevel.next(2); expect(metricsService.currentMetrics.lastSubLevelReached).toBe(2); }); it('metrics should clear values after restart', () => { mockStartMetrics(); metricsService.currentMetrics.lastSubLevelReached = 4; metricsService.currentMetrics.exerciseMetrics = [undefined, undefined, undefined]; metricsService.currentMetrics.level = 10; gameActionsService.restartGame.emit({info: microLessonInfo, level: 1}); expect(metricsService.currentMetrics.lastSubLevelReached).toBe(1); expect(metricsService.currentMetrics.level).toBe(1); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(0); }); it('metrics should have one exercise after being started and show next challenge event send', () => { mockStartMetrics(); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(0); gameActionsService.showNextChallenge.emit(); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(1); }); it('metrics should have one exercise after being started and show next challenge event send', () => { mockStartMetrics(); gameActionsService.showNextChallenge.emit(); const starSurrenderCounter = metricsService.currentMetrics.surrender; gameActionsService.surrender.emit(); expect(metricsService.currentMetrics.surrender).toBe(starSurrenderCounter + 1); expect((metricsService['getLastExerciseMetric']().surrender)).toBeTruthy(); }); it('Metrics should finish, calculate score and time after microLessonCompleted', fakeAsync((done) => { mockStartMetrics(); gameActionsService.showNextChallenge.emit(); // Time does not start to count till first challenge is at screen. tick(10000); spyOn(metricsService.finishedMetrics, 'emit'); gameActionsService.microLessonCompleted.emit(); expect(metricsService.finishedMetrics.emit).toHaveBeenCalledWith(metricsService.currentMetrics); expect(metricsService.currentMetrics.stars).toBe(3); expect(metricsService.currentMetrics.score).toBe(5000); expect(metricsService.currentMetrics.netTime).toBe(10); })); it('Metrics should add one raise hint count and add one hint metric after hint event is triggered', () => { mockStartMetrics(); gameActionsService.showNextChallenge.emit(); const originalHints = metricsService.currentMetrics.hints; const originalHintMetrics = metricsService.currentMetrics.exerciseMetrics[0].hints.length; gameActionsService.showHint.emit(); expect(metricsService.currentMetrics.hints).toBe(originalHints + 1); gameActionsService.showHint.emit(); expect(metricsService.currentMetrics.exerciseMetrics[0].hints.length).toBe(originalHintMetrics + 2); }); it('After show next challenge is triggered metrics should add one more exercise metric to array', () => { mockStartMetrics(); const originalCount = metricsService.currentMetrics.exerciseMetrics.length; gameActionsService.showNextChallenge.emit(); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(originalCount + 1); gameActionsService.showNextChallenge.emit(); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(originalCount + 2); gameActionsService.showNextChallenge.emit(); expect(metricsService.currentMetrics.exerciseMetrics.length).toBe(originalCount + 3); }); it('After try answer a answer metric should be added with correctness indeterminate', () => { mockStartMetrics(); gameActionsService.showNextChallenge.emit(); // for a challenge to be "set" const answerService: AnswerService = TestBed.get(AnswerService); expect(metricsService['getLastExerciseMetric']().answers.length).toBe(0); answerService.tryAnswer.emit(1); expect(metricsService['getLastExerciseMetric']().answers.length).toBe(1); expect(metricsService['getLastExerciseMetric']().answers[0].correctness).toBe('indeterminate'); }); it('After checkedAnswer should update correct/wrong count and set correctness to last answer. Also send update metrics event.', () => { mockStartMetrics(); const startRightAnswers = metricsService.currentMetrics.rightAnswers; const startWroAnswers = metricsService.currentMetrics.wrongAnswers; gameActionsService.showNextChallenge.emit(); // for a challenge to be "set" spyOn(metricsService.updateMetrics, 'emit'); const answerService: AnswerService = TestBed.get(AnswerService); answerService.tryAnswer.emit(1); gameActionsService.checkedAnswer.emit(true); expect(metricsService['getLastExerciseMetric']().answers[metricsService['getLastExerciseMetric']().answers.length - 1].correctness) .toBe('right'); expect(metricsService.currentMetrics.rightAnswers).toBe(startRightAnswers + 1); expect(metricsService.currentMetrics.wrongAnswers).toBe(startWroAnswers); expect(metricsService.updateMetrics.emit).toHaveBeenCalledWith(metricsService.currentMetrics); answerService.tryAnswer.emit(1); gameActionsService.checkedAnswer.emit(false); expect(metricsService['getLastExerciseMetric']().answers[metricsService['getLastExerciseMetric']().answers.length - 1].correctness) .toBe('wrong'); expect(metricsService.currentMetrics.rightAnswers).toBe(startRightAnswers + 1); expect(metricsService.currentMetrics.wrongAnswers).toBe(startWroAnswers + 1); expect(metricsService.updateMetrics.emit).toHaveBeenCalledWith(metricsService.currentMetrics); }); it('Timer doesnt start to take time till first challenge is on screen', fakeAsync(() => { mockStartMetrics(); const timer = TestBed.get(TimerOxService); tick(1000); expect(timer['_seconds']).toBe(undefined); gameActionsService.showNextChallenge.emit(); expect(timer['_seconds']).toBe(0); tick(2000); expect(timer['_seconds']).toBe(2); gameActionsService.microLessonCompleted.emit(); // to stop timer expect(metricsService.currentMetrics.netTime).toBe(2); })); it('Timer doesn t take into account time between try button - check answer', fakeAsync(() => { mockStartMetrics(); const answerService: AnswerService = TestBed.get(AnswerService); const timer = TestBed.get(TimerOxService); gameActionsService.showNextChallenge.emit(); expect(timer['_seconds']).toBe(0); tick(2000); expect(timer['_seconds']).toBe(2); answerService.tryAnswer.emit(1); tick(5000); expect(timer['_seconds']).toBe(2); gameActionsService.microLessonCompleted.emit(); // to stop timer expect(metricsService.currentMetrics.netTime).toBe(2); })); it('Timer doesn t take into account time between after correct answer/ surrender but yes after wrong answer', fakeAsync(() => { mockStartMetrics(); const answerService: AnswerService = TestBed.get(AnswerService); const timer = TestBed.get(TimerOxService); gameActionsService.showNextChallenge.emit(); expect(timer['_seconds']).toBe(0); answerService.tryAnswer.emit(1); tick(5000); // not taken gameActionsService.checkedAnswer.emit(false); tick(2000); expect(timer['_seconds']).toBe(2); answerService.tryAnswer.emit(1); gameActionsService.checkedAnswer.emit(true); tick(2000); expect(timer['_seconds']).toBe(2); answerService.tryAnswer.emit(1); gameActionsService.surrender.emit(); tick(2000); expect(timer['_seconds']).toBe(2); gameActionsService.microLessonCompleted.emit(); // to stop timer expect(metricsService.currentMetrics.netTime).toBe(2); })); it('Metrics time is 0 after restart game', fakeAsync(() => { mockStartMetrics(); const timer = TestBed.get(TimerOxService); gameActionsService.showNextChallenge.emit(); tick(5000); // not taken expect(timer['_seconds']).toBe(5); gameActionsService.restartGame.emit({level: 1, info: undefined}); console.log(timer['_seconds']); expect(timer['_seconds']).toBe(0); expect(metricsService.currentMetrics.netTime).toBe(0); gameActionsService.microLessonCompleted.emit(); // to stop timer })); it('Metrics time net time is working correctly', fakeAsync(() => { mockStartMetrics(); TestBed.get(ChallengeService).currentExercise = new EventEmitter(); TestBed.get(ChallengeService).currentExercise.emit({myOB: 1}); tick(5000); // not taken gameActionsService.checkedAnswer.emit(true); tick(5000); // not taken expect(metricsService.currentMetrics.netTime).toBe(5); // gameActionsService.restartGame.emit({level: 1, info: undefined}); // console.log(timer['_seconds']); // expect(timer['_seconds']).toBe(0); // expect(metricsService.currentMetrics.netTime).toBe(0); // gameActionsService.microLessonCompleted.emit(); // to stop timer })); function mockStartMetrics() { metricsService.gameStart(microLessonInfo, 1); } afterEach(() => { metricsService.currentMetrics = undefined; }); });