@testgorilla/tgo-ai-interview-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.
Files changed (29) hide show
  1. package/.eslintrc.json +46 -0
  2. package/README.md +91 -0
  3. package/jest.config.ts +29 -0
  4. package/ng-package.json +16 -0
  5. package/package.json +25 -0
  6. package/project.json +37 -0
  7. package/src/assets/i18n/en.json +19 -0
  8. package/src/index.ts +3 -0
  9. package/src/lib/components/ai-interview-test/ai-interview-test.component.html +42 -0
  10. package/src/lib/components/ai-interview-test/ai-interview-test.component.scss +167 -0
  11. package/src/lib/components/ai-interview-test/ai-interview-test.component.spec.ts +211 -0
  12. package/src/lib/components/ai-interview-test/ai-interview-test.component.ts +193 -0
  13. package/src/lib/components/index.ts +3 -0
  14. package/src/lib/components/interview-stream/interview-stream.component.html +9 -0
  15. package/src/lib/components/interview-stream/interview-stream.component.scss +5 -0
  16. package/src/lib/components/interview-stream/interview-stream.component.spec.ts +285 -0
  17. package/src/lib/components/interview-stream/interview-stream.component.ts +321 -0
  18. package/src/lib/components/interview-video/interview-video.component.html +8 -0
  19. package/src/lib/components/interview-video/interview-video.component.scss +7 -0
  20. package/src/lib/components/interview-video/interview-video.component.spec.ts +140 -0
  21. package/src/lib/components/interview-video/interview-video.component.ts +68 -0
  22. package/src/lib/models/index.ts +13 -0
  23. package/src/lib/models/question-component.ts +13 -0
  24. package/src/lib/models/translations.ts +3 -0
  25. package/src/test-setup.ts +28 -0
  26. package/tsconfig.json +17 -0
  27. package/tsconfig.lib.json +15 -0
  28. package/tsconfig.lib.prod.json +10 -0
  29. package/tsconfig.spec.json +13 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "extends": ["../../.eslintrc.json"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts"],
7
+ "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
8
+ "rules": {
9
+ "@angular-eslint/directive-selector": [
10
+ "error",
11
+ {
12
+ "type": "attribute",
13
+ "prefix": "tgo",
14
+ "style": "camelCase"
15
+ }
16
+ ],
17
+ "@angular-eslint/component-selector": [
18
+ "error",
19
+ {
20
+ "type": "element",
21
+ "prefix": "tgo",
22
+ "style": "kebab-case"
23
+ }
24
+ ]
25
+ }
26
+ },
27
+ {
28
+ "files": ["*.html"],
29
+ "extends": ["plugin:@nx/angular-template"],
30
+ "rules": {}
31
+ },
32
+ {
33
+ "files": ["*.json"],
34
+ "parser": "jsonc-eslint-parser",
35
+ "rules": {
36
+ "@nx/dependency-checks": [
37
+ "error",
38
+ {
39
+ "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"]
40
+ }
41
+ ]
42
+ }
43
+ }
44
+ ]
45
+ }
46
+
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @testgorilla/tgo-ai-interview-test
2
+
3
+ AI Interview component for TestGorilla assessments.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @testgorilla/tgo-ai-interview-test
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { AiInterviewTestComponent } from '@testgorilla/tgo-ai-interview-test';
15
+
16
+ @Component({
17
+ imports: [AiInterviewTestComponent],
18
+ template: `
19
+ <tgo-ai-interview-test
20
+ [question]="question"
21
+ [test]="test"
22
+ [isFirstQuestion]="false"
23
+ [conversationUrl]="conversationUrl"
24
+ [selectedMediaDevices]="selectedMediaDevices"
25
+ [mediaAccessChanged]="mediaAccessChanged"
26
+ (submissionStateChanged)="onSubmissionStateChanged($event)"
27
+ (loadingStateChanged)="onLoadingStateChanged($event)"
28
+ (requestMediaAccess)="onRequestMediaAccess()"
29
+ ></tgo-ai-interview-test>
30
+ `,
31
+ })
32
+ export class MyComponent {}
33
+ ```
34
+
35
+ ## API
36
+
37
+ ### Inputs
38
+
39
+ | Name | Type | Required | Default | Description |
40
+ | ------------------- | --------------------------------- | -------- | ------- | ------------------------------------------------ |
41
+ | question | Question | Yes | - | Question data containing text/media content |
42
+ | test | TestResultRead | Yes | - | Test configuration and metadata |
43
+ | isFirstQuestion | boolean | No | false | Whether this is the first question in the test |
44
+ | conversationUrl | string | No | - | Daily.co conversation URL for the AI interview |
45
+ | selectedMediaDevices| SelectedMediaDevices | No | - | Selected audio/video device IDs |
46
+ | mediaAccessChanged | Observable\<SelectedMediaDevices\>| No | - | Observable for media device changes |
47
+
48
+ ### Outputs
49
+
50
+ | Name | Type | Description |
51
+ | -------------------- | --------------------------- | ---------------------------------------------- |
52
+ | submissionStateChanged| EventEmitter\<ISubmissionState \| null\> | Emits submission state when answer is submitted |
53
+ | loadingStateChanged | EventEmitter\<boolean\> | Emits loading state changes |
54
+ | requestMediaAccess | EventEmitter\<void\> | Emits when media access permission is needed |
55
+
56
+ ## Peer Dependencies
57
+
58
+ This library requires the following peer dependencies:
59
+
60
+ - `@angular/common` ~18.2.13
61
+ - `@angular/core` ~18.2.13
62
+ - `@angular/animations` ~18.2.13
63
+ - `@angular/material` ~18.2.14 (for dialog support)
64
+ - `@ngneat/transloco` ~4.3.0
65
+ - `@testgorilla/tgo-ui` ~3.14.10
66
+ - `@daily-co/daily-js` ^0.79.0
67
+ - `rxjs` ~7.8.1
68
+
69
+ ## Internal Dependencies
70
+
71
+ All required services, models, and components are included within this library:
72
+
73
+ - `MediaService` - Handles audio/video recording and playback
74
+ - `ThemeService` - Provides theme/company color configuration
75
+ - `Question`, `TestResultRead`, `SelectedMediaDevices`, `ISubmissionState` - Type definitions
76
+ - `AudioAnimationComponent`, `VideoCountdownComponent`, `VimeoVideoComponent` - UI components
77
+ - `InterviewStreamComponent` - Daily.co video call integration component
78
+ - `InterviewVideoComponent` - Video display component
79
+ - `ReviewInstructionsDialogComponent` - Dialog component for review instructions
80
+ - `TranslocoLazyModuleUtils`, `getAvailableLangs` - Translation utilities
81
+
82
+ ## Features
83
+
84
+ - AI-powered interview via Daily.co video calls
85
+ - Candidate video recording
86
+ - Real-time video/audio stream handling
87
+ - Media device selection
88
+ - Translation support via Transloco
89
+ - Review instructions dialog
90
+ - Preview mode support
91
+
package/jest.config.ts ADDED
@@ -0,0 +1,29 @@
1
+ export default {
2
+ displayName: 'tgo-ai-interview-test',
3
+ preset: '../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ coverageDirectory: '../../coverage/packages/tgo-ai-interview-test',
6
+ testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/*.spec.[jt]s?(x)'],
7
+ testPathIgnorePatterns: ['/node_modules/', '/src/lib/models/'],
8
+ moduleNameMapper: {
9
+ '^@testgorilla/tgo-ui$': '<rootDir>/../tgo-test-shared/src/test-mocks/tgo-ui.mock.ts',
10
+ },
11
+ transform: {
12
+ '^.+\\.(ts|mjs|js|html)$': [
13
+ 'jest-preset-angular',
14
+ {
15
+ tsconfig: '<rootDir>/tsconfig.spec.json',
16
+ stringifyContentPathRegex: '\\.(html|svg)$',
17
+ },
18
+ ],
19
+ },
20
+ transformIgnorePatterns: [
21
+ 'node_modules/(?!.*\\.mjs$|lodash-es|@testgorilla|ng2-charts|chart\\.js|chartjs-plugin-datalabels|@daily-co)',
22
+ ],
23
+ snapshotSerializers: [
24
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
25
+ 'jest-preset-angular/build/serializers/ng-snapshot',
26
+ 'jest-preset-angular/build/serializers/html-comment',
27
+ ],
28
+ };
29
+
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/packages/tgo-ai-interview-test",
4
+ "assets": [
5
+ {
6
+ "glob": "**/*",
7
+ "input": "./src/assets",
8
+ "output": "./assets"
9
+ }
10
+ ],
11
+ "lib": {
12
+ "entryFile": "./src/index.ts"
13
+ },
14
+ "allowedNonPeerDependencies": []
15
+ }
16
+
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@testgorilla/tgo-ai-interview-test",
3
+ "version": "0.0.1",
4
+ "peerDependencies": {
5
+ "@angular/common": "~18.2.13",
6
+ "@angular/core": "~18.2.13",
7
+ "@angular/animations": "~18.2.13",
8
+ "@ngneat/transloco": "~4.3.0",
9
+ "@testgorilla/tgo-ui": "~3.14.10",
10
+ "@testgorilla/tgo-test-shared": "~0.0.1",
11
+ "@daily-co/daily-js": "^0.79.0",
12
+ "jest-preset-angular": "~14.2.4",
13
+ "rxjs": "~7.8.1"
14
+ },
15
+ "typings": "src/lib/types",
16
+ "sideEffects": false,
17
+ "license": "PROPRIETARY",
18
+ "publishConfig": {
19
+ "access": "restricted",
20
+ "registry": "https://registry.npmjs.org/"
21
+ },
22
+ "displayName": "AI Interview",
23
+ "description": "AI Interview component"
24
+ }
25
+
package/project.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "tgo-ai-interview-test",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/tgo-ai-interview-test/src",
5
+ "prefix": "tgo",
6
+ "projectType": "library",
7
+ "tags": [],
8
+ "targets": {
9
+ "build": {
10
+ "executor": "@nx/angular:package",
11
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}"],
12
+ "options": {
13
+ "project": "packages/tgo-ai-interview-test/ng-package.json"
14
+ },
15
+ "configurations": {
16
+ "production": {
17
+ "tsConfig": "packages/tgo-ai-interview-test/tsconfig.lib.prod.json"
18
+ },
19
+ "development": {
20
+ "tsConfig": "packages/tgo-ai-interview-test/tsconfig.lib.json"
21
+ }
22
+ },
23
+ "defaultConfiguration": "production"
24
+ },
25
+ "test": {
26
+ "executor": "@nx/jest:jest",
27
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
28
+ "options": {
29
+ "jestConfig": "packages/tgo-ai-interview-test/jest.config.ts"
30
+ }
31
+ },
32
+ "lint": {
33
+ "executor": "@nx/eslint:lint"
34
+ }
35
+ }
36
+ }
37
+
@@ -0,0 +1,19 @@
1
+ {
2
+ "TEST": {
3
+ "YOU": "You",
4
+ "RECORDING_STARTED": "Recording started",
5
+ "ANSWER_THE_QUESTION": "Answer the customer's question, then submit.",
6
+ "ANSWER_COMPLETED": "Submit answer",
7
+ "ANSWER": "Answer",
8
+ "CUSTOMER": "Customer",
9
+ "AUDIO_READY": "Your audio file is ready.",
10
+ "AUDIO_PREVIEW": "Audio is only available during practice questions.",
11
+ "REVIEW_INSTRUCTIONS": "Review Instructions",
12
+ "GET_READY": "Get ready to answer the customer's question.",
13
+ "TOOL_CALL": {
14
+ "ALL_FOR_TODAY": "That's all the questions for today. Thank you for your time.",
15
+ "NEXT_QUESTION": "Let's move to the next question."
16
+ }
17
+ }
18
+ }
19
+
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './lib/components';
2
+ export * from './lib/components/ai-interview-test/ai-interview-test.component';
3
+ export * from './lib/models';
@@ -0,0 +1,42 @@
1
+ <div class="ai-interview-test">
2
+ <div class="test-container">
3
+ <div
4
+ class="media-container"
5
+ [class.is-video-visible]="isInterviewInProgress()"
6
+ >
7
+ <div class="candidate-no-camera" *ngIf="!candidateVideoStreamReady()">
8
+ <h3>&nbsp;</h3>
9
+ <ui-icon name="User-profile-in-line" color="white" size="24"></ui-icon>
10
+ <h3 class="bold">{{ translations['YOU'] }}</h3>
11
+ </div>
12
+ <div class="candidate-camera" [hidden]="!candidateVideoStreamReady()">
13
+ <video
14
+ height
15
+ #video
16
+ id="video"
17
+ playsinline
18
+ (loadedmetadata)="onVideoLoad()"
19
+ ></video>
20
+ <h3 class="bold" *ngIf="candidateVideoStreamReady()">
21
+ {{ translations['YOU'] }}
22
+ </h3>
23
+ </div>
24
+ <tgo-audio-animation
25
+ *ngIf="isInterviewInProgress()"
26
+ [fakeData]="true"
27
+ ></tgo-audio-animation>
28
+ <div class="interview-stream-container">
29
+ <tgo-interview-stream
30
+ *ngIf="conversationUrl && hasMediaPermissions()"
31
+ [selectedMediaDevices]="selectedMediaDevices"
32
+ [conversationUrl]="conversationUrl"
33
+ (streamStart)="interviewStarted()"
34
+ (streamEnd)="interviewEnded()"
35
+ (checkMediaPermissions)="checkMediaPermissions()"
36
+ [translations]="translations"
37
+ ></tgo-interview-stream>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+
@@ -0,0 +1,167 @@
1
+ @import '@testgorilla/tgo-ui/projects/tgo-canopy-ui/theme/variables';
2
+
3
+ $border-radius: 10px;
4
+
5
+ .ai-interview-test {
6
+ background-color: $black;
7
+ padding: 24px;
8
+ display: flex;
9
+ justify-content: center;
10
+ min-height: calc(100vh - 80px);
11
+
12
+ h3 {
13
+ color: $tgo-white;
14
+ }
15
+
16
+ .test-container {
17
+ min-width: 360px;
18
+ width: 100%;
19
+ min-height: 300px;
20
+ height: min(725px, 100%, max(calc(100vw), calc(100vh), 200px));
21
+ max-width: calc(100vh * (16 / 9) - 80px - 48px);
22
+ display: flex;
23
+ flex-direction: column;
24
+ justify-content: space-between;
25
+ position: relative;
26
+ background-color: $black;
27
+ }
28
+
29
+ .media-container {
30
+ position: relative;
31
+ color: $tgo-white;
32
+ flex-grow: 1;
33
+
34
+ .interview-stream-container {
35
+ border-radius: $border-radius;
36
+ aspect-ratio: 16 / 9;
37
+ border: 4px solid $informative-40;
38
+ margin: 0 auto;
39
+ }
40
+
41
+ .candidate-no-camera {
42
+ position: absolute;
43
+ width: 200px;
44
+ aspect-ratio: 16 / 9;
45
+ z-index: 3;
46
+ top: 32px;
47
+ left: 32px;
48
+ border-radius: $border-radius;
49
+ background-color: #1a47aa;
50
+ display: flex;
51
+ justify-content: space-between;
52
+ flex-direction: column;
53
+ padding: 16px;
54
+
55
+ ui-icon {
56
+ margin: auto;
57
+ }
58
+ }
59
+
60
+ .candidate-camera {
61
+ position: absolute;
62
+ width: 200px;
63
+ top: 32px;
64
+ left: 32px;
65
+ z-index: 3;
66
+
67
+ video {
68
+ width: 200px;
69
+ border-radius: $border-radius;
70
+ }
71
+
72
+ h3 {
73
+ position: absolute;
74
+ bottom: 16px;
75
+ left: 16px;
76
+ }
77
+ }
78
+
79
+ tgo-audio-animation {
80
+ position: absolute;
81
+ top: 32px;
82
+ right: 32px;
83
+ z-index: 1;
84
+ }
85
+
86
+ tgo-vimeo-video {
87
+ display: none;
88
+ position: absolute;
89
+ height: 100%;
90
+ width: 100%;
91
+ }
92
+
93
+ &.is-video-visible {
94
+ tgo-vimeo-video {
95
+ display: block;
96
+ }
97
+ }
98
+
99
+ &.is-playing {
100
+ border: 4px solid $informative-40;
101
+ border-bottom-width: 3px;
102
+ }
103
+
104
+ &.is-answering {
105
+ .candidate-camera,
106
+ .candidate-no-camera {
107
+ tgo-audio-animation {
108
+ position: absolute;
109
+ top: 16px;
110
+ right: 16px;
111
+ }
112
+ }
113
+
114
+ .candidate-camera video,
115
+ .candidate-no-camera {
116
+ border: 3px solid $informative-40;
117
+ border-radius: $border-radius;
118
+ }
119
+
120
+ tgo-vimeo-video {
121
+ display: block;
122
+ }
123
+ }
124
+
125
+ .start,
126
+ .audio-info,
127
+ .overlay,
128
+ .answer {
129
+ display: flex;
130
+ justify-content: center;
131
+ align-items: center;
132
+ flex-direction: column;
133
+ gap: 40px;
134
+ height: 100%;
135
+ }
136
+
137
+ .preview {
138
+ height: 100%;
139
+ display: flex;
140
+ align-items: center;
141
+ flex-direction: column;
142
+ justify-content: flex-end;
143
+ padding: 24px;
144
+
145
+ p {
146
+ color: $grayscale-30;
147
+ margin: 8px 0;
148
+ }
149
+
150
+ audio {
151
+ margin: 16px 0;
152
+ }
153
+
154
+ &.hidden {
155
+ display: none;
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ @media screen and (max-width: 600px) {
162
+ .ai-interview-test {
163
+ padding: 0 24px;
164
+ margin-top: 16px;
165
+ }
166
+ }
167
+
@@ -0,0 +1,211 @@
1
+ import { ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, signal } from '@angular/core';
2
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
+ import { TranslocoService, TranslocoTestingModule } from '@ngneat/transloco';
5
+ import {
6
+ MediaService,
7
+ Question,
8
+ SelectedMediaDevices,
9
+ TestResultRead,
10
+ ThemeService,
11
+ } from '@testgorilla/tgo-test-shared';
12
+ import { ButtonComponentModule, IconComponentModule } from '@testgorilla/tgo-ui';
13
+ import { of, Subject } from 'rxjs';
14
+ import { AiInterviewTestComponent } from './ai-interview-test.component';
15
+
16
+ describe('AiInterviewTestComponent', () => {
17
+ let component: AiInterviewTestComponent;
18
+ let fixture: ComponentFixture<AiInterviewTestComponent>;
19
+ let transLocoServiceMock: Partial<jest.Mocked<TranslocoService>>;
20
+ let getMediaStreamSpy: jest.SpyInstance;
21
+ let submissionStateChangedSpy: jest.SpyInstance;
22
+ let mediaAccessChanged$: Subject<SelectedMediaDevices>;
23
+ let setSelectedMediaDevicesSpy: jest.SpyInstance;
24
+ let loadingStateChangedSpy: jest.SpyInstance;
25
+
26
+ const mockedTranslations = { testTranslationKey: 'test-translation' };
27
+ const mockStreamUrl = 'https://stream.com';
28
+
29
+ const mockedTest = {
30
+ is_preview_mode: false,
31
+ } as TestResultRead;
32
+
33
+ let hasMediaPermission = true;
34
+
35
+ class MediaServiceMock {
36
+ private recordSubject = new Subject();
37
+
38
+ isRecording = signal(false);
39
+
40
+ setSelectedMediaDevices(): void {
41
+ return;
42
+ }
43
+
44
+ getMediaStream() {
45
+ return Promise.resolve();
46
+ }
47
+
48
+ checkPermission() {
49
+ return Promise.resolve(hasMediaPermission);
50
+ }
51
+ }
52
+
53
+ beforeEach(async () => {
54
+ transLocoServiceMock = {
55
+ selectTranslateObject: jest.fn().mockReturnValue(of(mockedTranslations)),
56
+ };
57
+
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ const mockedMediaService = new MediaServiceMock() as any as MediaService;
60
+
61
+ await TestBed.configureTestingModule({
62
+ imports: [
63
+ TranslocoTestingModule.forRoot({
64
+ translocoConfig: {
65
+ availableLangs: ['en'],
66
+ defaultLang: 'en',
67
+ reRenderOnLangChange: true,
68
+ },
69
+ preloadLangs: true,
70
+ }),
71
+ IconComponentModule,
72
+ ButtonComponentModule,
73
+ NoopAnimationsModule,
74
+ AiInterviewTestComponent,
75
+ ],
76
+ schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
77
+ })
78
+ .overrideComponent(AiInterviewTestComponent, {
79
+ add: {
80
+ providers: [
81
+ { provide: TranslocoService, useValue: transLocoServiceMock },
82
+ { provide: MediaService, useValue: mockedMediaService },
83
+ {
84
+ provide: ChangeDetectorRef,
85
+ useValue: { markForCheck: jest.fn() },
86
+ },
87
+ {
88
+ provide: ThemeService,
89
+ useValue: {
90
+ uiTheme: 'dark',
91
+ getCompanyColor: jest.fn().mockReturnValue('#D410AA'),
92
+ },
93
+ },
94
+ ],
95
+ },
96
+ })
97
+ .compileComponents();
98
+
99
+ fixture = TestBed.createComponent(AiInterviewTestComponent);
100
+ component = fixture.componentInstance;
101
+
102
+ getMediaStreamSpy = jest.spyOn(mockedMediaService, 'getMediaStream');
103
+ setSelectedMediaDevicesSpy = jest.spyOn(mockedMediaService, 'setSelectedMediaDevices');
104
+ submissionStateChangedSpy = jest.spyOn(component.submissionStateChanged, 'emit');
105
+
106
+ loadingStateChangedSpy = jest.spyOn(component.loadingStateChanged, 'emit');
107
+
108
+ mediaAccessChanged$ = new Subject<SelectedMediaDevices>();
109
+ component.mediaAccessChanged = mediaAccessChanged$.asObservable();
110
+ hasMediaPermission = true;
111
+ });
112
+
113
+ afterEach(() => {
114
+ fixture.destroy();
115
+ jest.clearAllMocks();
116
+ });
117
+
118
+ describe('when component is initialized with and with question video content', () => {
119
+ beforeEach(() => {
120
+ component.question = {
121
+ text: ``,
122
+ } as Question;
123
+ component.test = mockedTest;
124
+ component.conversationUrl = mockStreamUrl;
125
+ component.ngOnInit();
126
+ fixture.detectChanges();
127
+ });
128
+
129
+ it('should set the translations', () => {
130
+ expect(component.translations).toEqual(mockedTranslations);
131
+ });
132
+
133
+ it('should init stream', () => {
134
+ expect(getMediaStreamSpy).toHaveBeenCalled();
135
+ });
136
+
137
+ it('should return stream content url', () => {
138
+ expect(component.conversationUrl).toBe(mockStreamUrl);
139
+ });
140
+
141
+ it('should has candidateStreamReady = false by default', () => {
142
+ expect(component.candidateVideoStreamReady()).toBe(false);
143
+ });
144
+
145
+ describe('when video is loaded', () => {
146
+ beforeEach(() => {
147
+ component.onVideoLoad();
148
+ fixture.detectChanges();
149
+ });
150
+
151
+ it('should set candidateStreamReady to true', () => {
152
+ expect(component.candidateVideoStreamReady()).toBe(true);
153
+ });
154
+ });
155
+
156
+ describe('when mediaAccessChange is called', () => {
157
+ const changedAudioDeviceId = 'test_audio_changed';
158
+ const changedVideoDeviceId = 'test_video_changed';
159
+ beforeEach(() => {
160
+ mediaAccessChanged$.next({
161
+ audioDeviceId: changedAudioDeviceId,
162
+ videoDeviceId: changedVideoDeviceId,
163
+ });
164
+ fixture.detectChanges();
165
+ });
166
+
167
+ it('should call mediaService.setSelectedMediaDevices() method with new devices Ids', () => {
168
+ expect(setSelectedMediaDevicesSpy).toHaveBeenCalledWith({
169
+ audioDeviceId: changedAudioDeviceId,
170
+ videoDeviceId: changedVideoDeviceId,
171
+ });
172
+ });
173
+
174
+ it('should reinitialize video stream', () => {
175
+ expect(getMediaStreamSpy).toHaveBeenCalled();
176
+ });
177
+ });
178
+
179
+ describe('when interviewStarted is called', () => {
180
+ beforeEach(() => {
181
+ component.interviewStarted();
182
+ fixture.detectChanges();
183
+ });
184
+
185
+ it('should set isInterviewInProgress to true', () => {
186
+ expect(component.isInterviewInProgress()).toBe(true);
187
+ });
188
+
189
+ it('should emit loadingStateChanged with false', () => {
190
+ expect(loadingStateChangedSpy).toHaveBeenCalledWith(false);
191
+ });
192
+
193
+ describe('when interviewEnded is called', () => {
194
+ beforeEach(() => {
195
+ component.interviewEnded();
196
+ fixture.detectChanges();
197
+ });
198
+
199
+ it('should set isInterviewInProgress to false', () => {
200
+ expect(component.isInterviewInProgress()).toBe(false);
201
+ });
202
+
203
+ it('should emit submissionStateChanged with empty text', () => {
204
+ expect(submissionStateChangedSpy).toHaveBeenCalledWith({
205
+ text: '',
206
+ });
207
+ });
208
+ });
209
+ });
210
+ });
211
+ });