@testgorilla/tgo-immersive-test 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +46 -0
- package/README.md +89 -0
- package/jest.config.ts +28 -0
- package/ng-package.json +16 -0
- package/package.json +25 -0
- package/project.json +37 -0
- package/src/assets/i18n/en.json +18 -0
- package/src/index.ts +4 -0
- package/src/lib/components/immersive-test/immersive-test.component.html +100 -0
- package/src/lib/components/immersive-test/immersive-test.component.scss +247 -0
- package/src/lib/components/immersive-test/immersive-test.component.spec.ts +581 -0
- package/src/lib/components/immersive-test/immersive-test.component.ts +279 -0
- package/src/lib/components/index.ts +6 -0
- package/src/lib/components/review-instructions-dialog/index.ts +1 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.html +39 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.scss +160 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.spec.ts +81 -0
- package/src/lib/components/review-instructions-dialog/review-instructions-dialog.component.ts +80 -0
- package/src/lib/components/ringing-phone-animation/index.ts +1 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.html +16 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.scss +79 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.spec.ts +95 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.component.ts +54 -0
- package/src/lib/components/ringing-phone-animation/ringing-phone-animation.sound.ts +2 -0
- package/src/lib/components/video-countdown/index.ts +1 -0
- package/src/lib/components/video-countdown/video-countdown.component.html +10 -0
- package/src/lib/components/video-countdown/video-countdown.component.scss +16 -0
- package/src/lib/components/video-countdown/video-countdown.component.spec.ts +59 -0
- package/src/lib/components/video-countdown/video-countdown.component.ts +102 -0
- package/src/lib/models/index.ts +9 -0
- package/src/lib/models/translations.ts +3 -0
- package/src/lib/services/index.ts +7 -0
- package/src/test-setup.ts +22 -0
- package/tsconfig.json +17 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +13 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, signal } from '@angular/core';
|
|
2
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
3
|
+
import { By } from '@angular/platform-browser';
|
|
4
|
+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
5
|
+
import { TranslocoService, TranslocoTestingModule } from '@ngneat/transloco';
|
|
6
|
+
import {
|
|
7
|
+
MediaService,
|
|
8
|
+
Question,
|
|
9
|
+
SelectedMediaDevices,
|
|
10
|
+
TestResultRead,
|
|
11
|
+
ThemeService,
|
|
12
|
+
} from '@testgorilla/tgo-test-shared';
|
|
13
|
+
import { DialogService } from '@testgorilla/tgo-ui';
|
|
14
|
+
import { of, Subject } from 'rxjs';
|
|
15
|
+
import { ImmersiveTestComponent } from './immersive-test.component';
|
|
16
|
+
|
|
17
|
+
describe('ImmersiveTestComponent', () => {
|
|
18
|
+
let component: ImmersiveTestComponent;
|
|
19
|
+
let fixture: ComponentFixture<ImmersiveTestComponent>;
|
|
20
|
+
let transLocoServiceMock: Partial<jest.Mocked<TranslocoService>>;
|
|
21
|
+
let getMediaStreamSpy: jest.SpyInstance;
|
|
22
|
+
let checkMediaPermissionSpy: jest.SpyInstance;
|
|
23
|
+
let recordAudioSpy: jest.SpyInstance;
|
|
24
|
+
let stopRecordingSpy: jest.SpyInstance;
|
|
25
|
+
let playAudioSpy: jest.SpyInstance;
|
|
26
|
+
let submissionStateChangedSpy: jest.SpyInstance;
|
|
27
|
+
let requestMediaAccessSpy: jest.SpyInstance;
|
|
28
|
+
let openDialogSpy: jest.SpyInstance;
|
|
29
|
+
let expirationSubject$: Subject<void>;
|
|
30
|
+
let mediaAccessChanged$: Subject<SelectedMediaDevices>;
|
|
31
|
+
let setSelectedMediaDevicesSpy: jest.SpyInstance;
|
|
32
|
+
|
|
33
|
+
const mockedTranslations = { testTranslationKey: 'test-translation' };
|
|
34
|
+
const mockVideoUrl = 'https://player.vimeo.com/video/1234';
|
|
35
|
+
const mockedQuestionWithVideo = {
|
|
36
|
+
text: `<p>mock</p><iframe src="${mockVideoUrl}"></iframe>`,
|
|
37
|
+
} as Question;
|
|
38
|
+
const mockAudioUrl = 'https://test.com/audio.mp3';
|
|
39
|
+
const mockedQuestionWithAudio = {
|
|
40
|
+
text: `<p>mock</p><iframe src="${mockAudioUrl}"></iframe>`,
|
|
41
|
+
} as Question;
|
|
42
|
+
const mockedTest = {
|
|
43
|
+
is_preview_mode: false,
|
|
44
|
+
} as TestResultRead;
|
|
45
|
+
const mockedObjectUrl = 'objectUrl';
|
|
46
|
+
|
|
47
|
+
const selectors = {
|
|
48
|
+
getStartQuestionBtn: () =>
|
|
49
|
+
fixture.debugElement.query(By.css('[data-testid="immersive-test.start-question-btn"]')),
|
|
50
|
+
getStopRecordingBtn: () =>
|
|
51
|
+
fixture.debugElement.query(By.css('[data-testid="immersive-test.stop-recording-btn"]')),
|
|
52
|
+
getReviewInstructionsBtn: () =>
|
|
53
|
+
fixture.debugElement.query(By.css('[data-testid="immersive-test.review-instructions-btn"]')),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const DialogServiceMock = {
|
|
57
|
+
open: jest.fn(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mockedVolumeValue = 0.5;
|
|
61
|
+
const mockedFile = new Blob();
|
|
62
|
+
|
|
63
|
+
let hasMediaPermission = true;
|
|
64
|
+
|
|
65
|
+
class MediaServiceMock {
|
|
66
|
+
private recordSubject = new Subject();
|
|
67
|
+
|
|
68
|
+
isRecording = signal(false);
|
|
69
|
+
|
|
70
|
+
setSelectedMediaDevices() {
|
|
71
|
+
// Mock implementation - no-op for testing
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getMediaStream() {
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
recordAudio() {
|
|
79
|
+
this.isRecording.set(true);
|
|
80
|
+
if (hasMediaPermission) {
|
|
81
|
+
window.setTimeout(() => {
|
|
82
|
+
this.recordSubject.next({ type: 'volume', value: mockedVolumeValue });
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
this.recordSubject.error('Permission denied');
|
|
86
|
+
this.recordSubject.complete();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this.recordSubject.asObservable();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
checkPermission() {
|
|
93
|
+
return Promise.resolve(hasMediaPermission);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
stopRecording() {
|
|
97
|
+
this.isRecording.set(false);
|
|
98
|
+
this.recordSubject.next({ type: 'complete', file: mockedFile });
|
|
99
|
+
this.recordSubject.complete();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
playAudio() {
|
|
103
|
+
return Promise.resolve();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
beforeEach(async () => {
|
|
108
|
+
transLocoServiceMock = {
|
|
109
|
+
selectTranslateObject: jest.fn().mockReturnValue(of(mockedTranslations)),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (!navigator.mediaDevices) {
|
|
113
|
+
Object.defineProperty(navigator, 'mediaDevices', {
|
|
114
|
+
value: {},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!navigator.mediaDevices.getUserMedia) {
|
|
119
|
+
Object.defineProperty(navigator.mediaDevices, 'getUserMedia', {
|
|
120
|
+
value: jest.fn(),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!window.URL.createObjectURL) {
|
|
125
|
+
Object.defineProperty(URL, 'createObjectURL', {
|
|
126
|
+
value: jest.fn().mockReturnValue(mockedObjectUrl),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
const mockedMediaService = new MediaServiceMock() as any as MediaService;
|
|
132
|
+
|
|
133
|
+
await TestBed.configureTestingModule({
|
|
134
|
+
declarations: [],
|
|
135
|
+
imports: [
|
|
136
|
+
TranslocoTestingModule.forRoot({
|
|
137
|
+
translocoConfig: {
|
|
138
|
+
availableLangs: ['en'],
|
|
139
|
+
defaultLang: 'en',
|
|
140
|
+
reRenderOnLangChange: true,
|
|
141
|
+
},
|
|
142
|
+
preloadLangs: true,
|
|
143
|
+
}),
|
|
144
|
+
NoopAnimationsModule,
|
|
145
|
+
ImmersiveTestComponent,
|
|
146
|
+
],
|
|
147
|
+
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
|
|
148
|
+
})
|
|
149
|
+
.overrideComponent(ImmersiveTestComponent, {
|
|
150
|
+
add: {
|
|
151
|
+
providers: [
|
|
152
|
+
{ provide: TranslocoService, useValue: transLocoServiceMock },
|
|
153
|
+
{ provide: MediaService, useValue: mockedMediaService },
|
|
154
|
+
{
|
|
155
|
+
provide: ChangeDetectorRef,
|
|
156
|
+
useValue: { markForCheck: jest.fn() },
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
provide: DialogService,
|
|
160
|
+
useValue: DialogServiceMock,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
provide: ThemeService,
|
|
164
|
+
useValue: {
|
|
165
|
+
uiTheme: 'dark',
|
|
166
|
+
getCompanyColor: jest.fn().mockReturnValue('#D410AA'),
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
.compileComponents();
|
|
173
|
+
|
|
174
|
+
fixture = TestBed.createComponent(ImmersiveTestComponent);
|
|
175
|
+
component = fixture.componentInstance;
|
|
176
|
+
|
|
177
|
+
getMediaStreamSpy = jest.spyOn(mockedMediaService, 'getMediaStream');
|
|
178
|
+
checkMediaPermissionSpy = jest.spyOn(mockedMediaService, 'checkPermission');
|
|
179
|
+
recordAudioSpy = jest.spyOn(mockedMediaService, 'recordAudio');
|
|
180
|
+
playAudioSpy = jest.spyOn(mockedMediaService, 'playAudio');
|
|
181
|
+
stopRecordingSpy = jest.spyOn(mockedMediaService, 'stopRecording');
|
|
182
|
+
openDialogSpy = jest.spyOn(DialogServiceMock, 'open');
|
|
183
|
+
setSelectedMediaDevicesSpy = jest.spyOn(mockedMediaService, 'setSelectedMediaDevices');
|
|
184
|
+
submissionStateChangedSpy = jest.spyOn(component.submissionStateChanged, 'emit');
|
|
185
|
+
|
|
186
|
+
requestMediaAccessSpy = jest.spyOn(component.requestMediaAccess, 'emit');
|
|
187
|
+
|
|
188
|
+
expirationSubject$ = new Subject();
|
|
189
|
+
mediaAccessChanged$ = new Subject<SelectedMediaDevices>();
|
|
190
|
+
component.expirationObservable = expirationSubject$.asObservable();
|
|
191
|
+
component.mediaAccessChanged = mediaAccessChanged$.asObservable();
|
|
192
|
+
hasMediaPermission = true;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
afterEach(() => {
|
|
196
|
+
fixture.destroy();
|
|
197
|
+
jest.clearAllMocks();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('when component is initialized with isFirstQuestion = false and with question video content', () => {
|
|
201
|
+
beforeEach(() => {
|
|
202
|
+
component.question = mockedQuestionWithVideo;
|
|
203
|
+
component.test = mockedTest;
|
|
204
|
+
component.isFirstQuestion = false;
|
|
205
|
+
component.ngOnInit();
|
|
206
|
+
fixture.detectChanges();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should set the translations', () => {
|
|
210
|
+
expect(component.translations).toEqual(mockedTranslations);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should start playing first video', () => {
|
|
214
|
+
expect(component.isQuestionPlaying()).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should not show start question btn', () => {
|
|
218
|
+
expect(selectors.getStartQuestionBtn()).toBeFalsy();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should init stream', () => {
|
|
222
|
+
expect(getMediaStreamSpy).toHaveBeenCalled();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should return video content type and url', () => {
|
|
226
|
+
expect(component.isVideo).toBe(true);
|
|
227
|
+
expect(component.fileUrl).toBe(mockVideoUrl);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should has candidateStreamReady = false by default', () => {
|
|
231
|
+
expect(component.candidateVideoStreamReady()).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('when video is loaded', () => {
|
|
235
|
+
beforeEach(() => {
|
|
236
|
+
component.onVideoLoad();
|
|
237
|
+
fixture.detectChanges();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should set candidateStreamReady to true', () => {
|
|
241
|
+
expect(component.candidateVideoStreamReady()).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('When review instruction btn is clicked', () => {
|
|
246
|
+
beforeEach(() => {
|
|
247
|
+
selectors.getReviewInstructionsBtn()?.nativeElement.click();
|
|
248
|
+
fixture.detectChanges();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should open review instruction dialog', () => {
|
|
252
|
+
expect(openDialogSpy).toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('when component is initialized with isFirstQuestion = true and with question video content', () => {
|
|
258
|
+
beforeEach(() => {
|
|
259
|
+
component.question = mockedQuestionWithVideo;
|
|
260
|
+
component.test = mockedTest;
|
|
261
|
+
component.isFirstQuestion = true;
|
|
262
|
+
component.ngOnInit();
|
|
263
|
+
fixture.detectChanges();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should not start playing first video', () => {
|
|
267
|
+
expect(component.isQuestionPlaying()).toBe(false);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should show start question btn', () => {
|
|
271
|
+
expect(selectors.getStartQuestionBtn()).toBeTruthy();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('when start question btn is clicked ', () => {
|
|
275
|
+
beforeEach(() => {
|
|
276
|
+
selectors.getStartQuestionBtn()?.nativeElement.click();
|
|
277
|
+
fixture.detectChanges();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should start playing first video', () => {
|
|
281
|
+
expect(component.isQuestionPlaying()).toBe(true);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should not show start question btn', () => {
|
|
285
|
+
expect(selectors.getStartQuestionBtn()).toBeFalsy();
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('when component is initialized with isFirstQuestion = false and with question audio content', () => {
|
|
291
|
+
beforeEach(() => {
|
|
292
|
+
component.question = mockedQuestionWithAudio;
|
|
293
|
+
component.test = mockedTest;
|
|
294
|
+
component.isFirstQuestion = false;
|
|
295
|
+
component.ngOnInit();
|
|
296
|
+
fixture.detectChanges();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should start playing first audio', () => {
|
|
300
|
+
expect(component.isQuestionPlaying()).toBe(true);
|
|
301
|
+
expect(playAudioSpy).toHaveBeenCalledWith(mockAudioUrl);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should not show start question btn', () => {
|
|
305
|
+
expect(selectors.getStartQuestionBtn()).toBeFalsy();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should call initStream', () => {
|
|
309
|
+
expect(getMediaStreamSpy).toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should return audio content type and url', () => {
|
|
313
|
+
expect(component.isVideo).toBe(false);
|
|
314
|
+
expect(component.fileUrl).toBe(mockAudioUrl);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should not show stop recording btn', () => {
|
|
318
|
+
expect(selectors.getStopRecordingBtn()).toBeFalsy();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe('when question finishes playing', () => {
|
|
322
|
+
beforeEach(() => {
|
|
323
|
+
// for playAudio() Promise is resolved immediately
|
|
324
|
+
fixture.detectChanges();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should check permission', () => {
|
|
328
|
+
expect(checkMediaPermissionSpy).toHaveBeenCalled();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should start countdown', () => {
|
|
332
|
+
expect(component.isCountingDown()).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('when recording started', () => {
|
|
336
|
+
beforeEach(() => {
|
|
337
|
+
component.startRecordAnswer();
|
|
338
|
+
fixture.detectChanges();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should update volume value', async () => {
|
|
342
|
+
fixture.detectChanges();
|
|
343
|
+
await fixture.whenStable();
|
|
344
|
+
// Wait for setTimeout in MediaServiceMock.recordAudio()
|
|
345
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
346
|
+
fixture.detectChanges();
|
|
347
|
+
|
|
348
|
+
expect(component.volume()).toBe(mockedVolumeValue);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should show stop recording btn', () => {
|
|
352
|
+
expect(selectors.getStopRecordingBtn()).toBeTruthy();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe('when stop Recording button is clicked', () => {
|
|
356
|
+
beforeEach(() => {
|
|
357
|
+
selectors.getStopRecordingBtn()?.nativeElement.click();
|
|
358
|
+
fixture.detectChanges();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should call mediaService.stopRecording() method', () => {
|
|
362
|
+
expect(stopRecordingSpy).toHaveBeenCalled();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should change isAnswering value to false', () => {
|
|
366
|
+
expect(component.isAnswering()).toBe(false);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should submit answer', () => {
|
|
370
|
+
expect(submissionStateChangedSpy).toHaveBeenCalledWith({
|
|
371
|
+
file: mockedFile,
|
|
372
|
+
text: '',
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('when expirationObservable is called', () => {
|
|
378
|
+
beforeEach(() => {
|
|
379
|
+
expirationSubject$.next();
|
|
380
|
+
fixture.detectChanges();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should call mediaService.stopRecording() method', () => {
|
|
384
|
+
expect(stopRecordingSpy).toHaveBeenCalled();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should change isAnswering value to false', () => {
|
|
388
|
+
expect(component.isAnswering()).toBe(false);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should submit answer', () => {
|
|
392
|
+
expect(submissionStateChangedSpy).toHaveBeenCalledWith({
|
|
393
|
+
file: mockedFile,
|
|
394
|
+
text: '',
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
describe('when mediaAccessChange is called', () => {
|
|
400
|
+
const changedAudioDeviceId = 'test_audio_changed';
|
|
401
|
+
const changedVideoDeviceId = 'test_video_changed';
|
|
402
|
+
beforeEach(() => {
|
|
403
|
+
mediaAccessChanged$.next({
|
|
404
|
+
audioDeviceId: changedAudioDeviceId,
|
|
405
|
+
videoDeviceId: changedVideoDeviceId,
|
|
406
|
+
});
|
|
407
|
+
fixture.detectChanges();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should call mediaService.setSelectedMediaDevices() method with new devices Ids', () => {
|
|
411
|
+
expect(setSelectedMediaDevicesSpy).toHaveBeenCalledWith({
|
|
412
|
+
audioDeviceId: changedAudioDeviceId,
|
|
413
|
+
videoDeviceId: changedVideoDeviceId,
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('should reinitialize video stream', () => {
|
|
418
|
+
expect(getMediaStreamSpy).toHaveBeenCalled();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should reinitialize countdown', () => {
|
|
422
|
+
expect(component.isCountingDown()).toBe(true);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
describe('when recording started with no media permissions', () => {
|
|
428
|
+
beforeEach(() => {
|
|
429
|
+
hasMediaPermission = false;
|
|
430
|
+
component.startRecordAnswer();
|
|
431
|
+
fixture.detectChanges();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should emit requestMediaAccess event', () => {
|
|
435
|
+
expect(requestMediaAccessSpy).toHaveBeenCalled();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it('should set isAnswering to false', () => {
|
|
439
|
+
expect(component.isAnswering()).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('when expirationObservable is called before question play finish', () => {
|
|
444
|
+
beforeEach(() => {
|
|
445
|
+
expirationSubject$.next();
|
|
446
|
+
fixture.detectChanges();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should submit empty answer', () => {
|
|
450
|
+
expect(submissionStateChangedSpy).toHaveBeenCalledWith({
|
|
451
|
+
file: undefined,
|
|
452
|
+
text: '',
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
describe('when component is initialized with isFirstQuestion = false and with question audio content and there is no media permission', () => {
|
|
460
|
+
beforeEach(() => {
|
|
461
|
+
component.question = mockedQuestionWithAudio;
|
|
462
|
+
component.test = {
|
|
463
|
+
is_preview_mode: false,
|
|
464
|
+
} as TestResultRead;
|
|
465
|
+
component.isFirstQuestion = false;
|
|
466
|
+
hasMediaPermission = false;
|
|
467
|
+
component.ngOnInit();
|
|
468
|
+
fixture.detectChanges();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe('when question finishes playing', () => {
|
|
472
|
+
beforeEach(() => {
|
|
473
|
+
// for playAudio() Promise is resolved immediately
|
|
474
|
+
fixture.detectChanges();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should check permission', () => {
|
|
478
|
+
expect(checkMediaPermissionSpy).toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should not start countdown', () => {
|
|
482
|
+
expect(component.isCountingDown()).toBe(false);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('should emit requestMediaAccess event', () => {
|
|
486
|
+
expect(requestMediaAccessSpy).toHaveBeenCalled();
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe('when component is initialized with isFirstQuestion = true and with question audio content', () => {
|
|
492
|
+
beforeEach(() => {
|
|
493
|
+
component.question = mockedQuestionWithAudio;
|
|
494
|
+
component.test = mockedTest;
|
|
495
|
+
component.isFirstQuestion = true;
|
|
496
|
+
component.ngOnInit();
|
|
497
|
+
fixture.detectChanges();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('should not start playing first audio', () => {
|
|
501
|
+
expect(component.isQuestionPlaying()).toBe(false);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('should show start question btn', () => {
|
|
505
|
+
expect(selectors.getStartQuestionBtn()).toBeTruthy();
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
describe('when start question btn is clicked ', () => {
|
|
509
|
+
beforeEach(() => {
|
|
510
|
+
selectors.getStartQuestionBtn()?.nativeElement.click();
|
|
511
|
+
fixture.detectChanges();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('should start playing first video', () => {
|
|
515
|
+
expect(component.isQuestionPlaying()).toBe(true);
|
|
516
|
+
expect(playAudioSpy).toHaveBeenCalledWith(mockAudioUrl);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should not show start question btn', () => {
|
|
520
|
+
expect(selectors.getStartQuestionBtn()).toBeFalsy();
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
describe('when component is initialized with isFirstQuestion = false and with question audio content in preview mode', () => {
|
|
526
|
+
beforeEach(() => {
|
|
527
|
+
component.question = mockedQuestionWithAudio;
|
|
528
|
+
component.test = {
|
|
529
|
+
is_preview_mode: true,
|
|
530
|
+
} as TestResultRead;
|
|
531
|
+
component.isFirstQuestion = false;
|
|
532
|
+
component.ngOnInit();
|
|
533
|
+
fixture.detectChanges();
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
describe('when question finishes playing', () => {
|
|
537
|
+
beforeEach(() => {
|
|
538
|
+
// for playAudio() Promise is resolved immediately
|
|
539
|
+
fixture.detectChanges();
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should check permission', () => {
|
|
543
|
+
expect(checkMediaPermissionSpy).toHaveBeenCalled();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('should start countdown', () => {
|
|
547
|
+
expect(component.isCountingDown()).toBe(true);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
describe('when recording started', () => {
|
|
551
|
+
beforeEach(() => {
|
|
552
|
+
component.startRecordAnswer();
|
|
553
|
+
fixture.detectChanges();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('should start recording answer', () => {
|
|
557
|
+
expect(recordAudioSpy).toHaveBeenCalled();
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should has empty audioUrl', () => {
|
|
561
|
+
expect(component.audioUrl()).toBe('');
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
describe('when stop Recording button is clicked', () => {
|
|
565
|
+
beforeEach(() => {
|
|
566
|
+
selectors.getStopRecordingBtn()?.nativeElement.click();
|
|
567
|
+
fixture.detectChanges();
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should call mediaService.stopRecording() method', () => {
|
|
571
|
+
expect(stopRecordingSpy).toHaveBeenCalled();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should set audioUrl', () => {
|
|
575
|
+
expect(component.audioUrl()).toBe(mockedObjectUrl);
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
});
|