@testgorilla/tgo-immersive-test 0.0.1 → 2.0.0

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 CHANGED
@@ -21,7 +21,8 @@
21
21
  "prefix": "tgo",
22
22
  "style": "kebab-case"
23
23
  }
24
- ]
24
+ ],
25
+ "@angular-eslint/prefer-standalone": "off"
25
26
  }
26
27
  },
27
28
  {
package/README.md CHANGED
@@ -32,6 +32,38 @@ import { ImmersiveTestComponent } from '@testgorilla/tgo-immersive-test';
32
32
  export class MyComponent {}
33
33
  ```
34
34
 
35
+ ## Assets (translations)
36
+
37
+ This package ships its translations under `assets/i18n`. Consumer apps must copy these assets into their build output so Transloco can load them at runtime.
38
+
39
+ - **Angular CLI / Nx**: add an `assets` entry pointing at the package:
40
+
41
+ ```json
42
+ {
43
+ "assets": [
44
+ "src/favicon.ico",
45
+ "src/assets",
46
+ {
47
+ "glob": "**/*",
48
+ "input": "node_modules/@testgorilla/tgo-immersive-test/assets",
49
+ "output": "assets/tgo-immersive-test"
50
+ }
51
+ ]
52
+ }
53
+ ```
54
+
55
+ If you develop inside this repo (consuming the workspace sources), also include the local path:
56
+
57
+ ```json
58
+ {
59
+ "glob": "**/*",
60
+ "input": "packages/tgo-immersive-test/src/assets",
61
+ "output": "assets/tgo-immersive-test"
62
+ }
63
+ ```
64
+
65
+ After adding the asset entries, rebuild/re-serve your app so `/assets/tgo-immersive-test/i18n/en.json` is available.
66
+
35
67
  ## API
36
68
 
37
69
  ### Inputs
package/jest.config.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Detect CI environment
2
+ const isCI = process.env.CI === 'true' || process.env.CIRCLECI === 'true';
3
+
1
4
  export default {
2
5
  displayName: 'tgo-immersive-test',
3
6
  preset: '../../jest.preset.js',
@@ -5,8 +8,11 @@ export default {
5
8
  coverageDirectory: '../../coverage/packages/tgo-immersive-test',
6
9
  testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/*.spec.[jt]s?(x)'],
7
10
  testPathIgnorePatterns: ['/node_modules/', '/src/lib/models/'],
11
+ // Run tests sequentially in CI to prevent OOM with media-related tests
12
+ ...(isCI && { maxWorkers: 1 }),
8
13
  moduleNameMapper: {
9
- '^@testgorilla/tgo-ui$': '<rootDir>/../tgo-test-shared/src/test-mocks/tgo-ui.mock.ts',
14
+ '^@testgorilla/tgo-ui$': '<rootDir>/../shared/src/test-mocks/tgo-ui.mock.ts',
15
+ '^@testgorilla/tgo-test-shared$': '<rootDir>/src/shared/index.ts',
10
16
  },
11
17
  transform: {
12
18
  '^.+\\.(ts|mjs|js|html)$': [
package/package.json CHANGED
@@ -1,25 +1,20 @@
1
1
  {
2
2
  "name": "@testgorilla/tgo-immersive-test",
3
- "version": "0.0.1",
3
+ "version": "2.0.0",
4
4
  "peerDependencies": {
5
- "@angular/common": "~18.2.13",
6
- "@angular/core": "~18.2.13",
7
- "@angular/animations": "~18.2.13",
8
- "@angular/material": "~18.2.14",
5
+ "@angular/common": "~19.2.17",
6
+ "@angular/core": "~19.2.17",
7
+ "@angular/animations": "~19.2.17",
8
+ "@angular/material": "~19.2.19",
9
9
  "@ngneat/transloco": "~4.3.0",
10
- "@testgorilla/tgo-ui": "~3.14.10",
11
- "@testgorilla/tgo-test-shared": "~0.0.1",
12
- "jest-preset-angular": "~14.2.4",
13
- "ngx-quill": "^26.0.10",
10
+ "@testgorilla/tgo-ui": "~4.0.0",
11
+ "jest-preset-angular": "14.4.2",
12
+ "ngx-quill": "^27.1.2",
14
13
  "rxjs": "~7.8.1"
15
14
  },
16
15
  "typings": "src/lib/types",
17
16
  "sideEffects": false,
18
17
  "license": "PROPRIETARY",
19
- "publishConfig": {
20
- "access": "restricted",
21
- "registry": "https://registry.npmjs.org/"
22
- },
23
18
  "displayName": "Immersive Test",
24
19
  "description": "Immersive Test component"
25
20
  }
package/project.json CHANGED
@@ -7,6 +7,18 @@
7
7
  "tags": [],
8
8
  "targets": {
9
9
  "build": {
10
+ "executor": "nx:run-commands",
11
+ "outputs": ["{workspaceRoot}/dist/packages/tgo-immersive-test"],
12
+ "options": {
13
+ "commands": [
14
+ "nx run tgo-immersive-test:package",
15
+ "node scripts/replace-shared-alias.js dist/packages/tgo-immersive-test @testgorilla/tgo-test-shared"
16
+ ],
17
+ "parallel": false,
18
+ "forwardAllArgs": false
19
+ }
20
+ },
21
+ "package": {
10
22
  "executor": "@nx/angular:package",
11
23
  "outputs": ["{workspaceRoot}/dist/{projectRoot}"],
12
24
  "options": {
@@ -34,4 +46,3 @@
34
46
  }
35
47
  }
36
48
  }
37
-
@@ -74,7 +74,7 @@
74
74
  {{ translations['GET_READY'] }}
75
75
  </h1>
76
76
  </div>
77
- <tgo-video-countdown (finish)="startRecordAnswer()"></tgo-video-countdown>
77
+ <tgo-video-countdown (finished)="startRecordAnswer()"></tgo-video-countdown>
78
78
  <div>&nbsp;</div>
79
79
  </div>
80
80
  <div class="preview" [class.hidden]="!audioUrl()" *ngIf="test.is_preview_mode">
@@ -10,7 +10,7 @@ import {
10
10
  TestResultRead,
11
11
  ThemeService,
12
12
  } from '@testgorilla/tgo-test-shared';
13
- import { DialogService } from '@testgorilla/tgo-ui';
13
+ import { ButtonComponentModule, DialogService, IconComponentModule } from '@testgorilla/tgo-ui';
14
14
  import { of, Subject } from 'rxjs';
15
15
  import { ImmersiveTestComponent } from './immersive-test.component';
16
16
 
@@ -143,6 +143,8 @@ describe('ImmersiveTestComponent', () => {
143
143
  }),
144
144
  NoopAnimationsModule,
145
145
  ImmersiveTestComponent,
146
+ ButtonComponentModule,
147
+ IconComponentModule,
146
148
  ],
147
149
  schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
148
150
  })
@@ -49,40 +49,39 @@ import {
49
49
  } from '@ngneat/transloco';
50
50
 
51
51
  @Component({
52
- selector: 'tgo-immersive-test',
53
- templateUrl: './immersive-test.component.html',
54
- styleUrl: './immersive-test.component.scss',
55
- animations: [
56
- trigger('fadeInFadeOut', [
57
- transition(':enter', [
58
- style({ opacity: 0 }),
59
- animate('600ms', style({ opacity: 1 })),
60
- ]),
61
- transition(':leave', [animate('600ms', style({ opacity: 0 }))]),
62
- ]),
63
- ],
64
- standalone: true,
65
- imports: [
66
- TranslocoModule,
67
- ButtonComponentModule,
68
- IconComponentModule,
69
- CommonModule,
70
- VimeoVideoComponent,
71
- VideoCountdownComponent,
72
- RingingPhoneAnimationComponent,
73
- AudioAnimationComponent,
74
- ReviewInstructionsDialogComponent,
75
- ],
76
- providers: [
77
- TranslocoLazyModuleUtils.getScopeProvider(
78
- 'tgo-immersive-test',
79
- getAvailableLangs(),
80
- ROOT_TRANSLATIONS_SCOPE,
81
- (lang: string) => import(`../../../assets/i18n/${lang}.json`)
82
- ),
83
- DialogService,
84
- ThemeService,
85
- ],
52
+ selector: 'tgo-immersive-test',
53
+ templateUrl: './immersive-test.component.html',
54
+ styleUrl: './immersive-test.component.scss',
55
+ animations: [
56
+ trigger('fadeInFadeOut', [
57
+ transition(':enter', [
58
+ style({ opacity: 0 }),
59
+ animate('600ms', style({ opacity: 1 })),
60
+ ]),
61
+ transition(':leave', [animate('600ms', style({ opacity: 0 }))]),
62
+ ]),
63
+ ],
64
+ imports: [
65
+ TranslocoModule,
66
+ ButtonComponentModule,
67
+ IconComponentModule,
68
+ CommonModule,
69
+ VimeoVideoComponent,
70
+ VideoCountdownComponent,
71
+ RingingPhoneAnimationComponent,
72
+ AudioAnimationComponent,
73
+ ReviewInstructionsDialogComponent,
74
+ ],
75
+ providers: [
76
+ TranslocoLazyModuleUtils.getScopeProvider('tgo-immersive-test', getAvailableLangs(), ROOT_TRANSLATIONS_SCOPE, (lang: string) => {
77
+ // Fetch from app assets; demo app copies the library assets to
78
+ // /assets/tgo-immersive-test via project.json.
79
+ const url = new URL(`assets/tgo-immersive-test/i18n/${lang}.json`, document.baseURI).href;
80
+ return fetch(url).then((res) => res.json());
81
+ }),
82
+ DialogService,
83
+ ThemeService,
84
+ ]
86
85
  })
87
86
  export class ImmersiveTestComponent
88
87
  implements OnInit, OnDestroy, IQuestionDataContract
@@ -1,4 +1,4 @@
1
- // Library-specific components only (shared components are in @testgorilla/tgo-test-shared)
1
+ // Library-specific components only (shared components are bundled from packages/shared)
2
2
  export * from './immersive-test/immersive-test.component';
3
3
  export * from './review-instructions-dialog';
4
4
  export * from './ringing-phone-animation';
@@ -25,26 +25,25 @@ export interface ReviewInstructionsDialogData {
25
25
  }
26
26
 
27
27
  @Component({
28
- selector: 'tgo-review-instructions-dialog',
29
- templateUrl: './review-instructions-dialog.component.html',
30
- styleUrls: ['./review-instructions-dialog.component.scss'],
31
- providers: [
32
- TranslocoLazyModuleUtils.getScopeProvider(
33
- 'tgo-immersive-test-review-instructions',
34
- getAvailableLangs(),
35
- 'INSTRUCTIONS_MODAL',
36
- (lang: string) => import(`../../../assets/i18n/${lang}.json`)
37
- ),
38
- ],
39
- changeDetection: ChangeDetectionStrategy.OnPush,
40
- standalone: true,
41
- imports: [
42
- CommonModule,
43
- TranslocoModule,
44
- DialogComponentModule,
45
- CardComponentModule,
46
- QuillViewComponent,
47
- ],
28
+ selector: 'tgo-review-instructions-dialog',
29
+ templateUrl: './review-instructions-dialog.component.html',
30
+ styleUrls: ['./review-instructions-dialog.component.scss'],
31
+ providers: [
32
+ TranslocoLazyModuleUtils.getScopeProvider('tgo-immersive-test-review-instructions', getAvailableLangs(), 'INSTRUCTIONS_MODAL', (lang: string) => {
33
+ // Fetch from app assets; demo app copies the library assets to
34
+ // /assets/tgo-immersive-test via project.json.
35
+ const url = new URL(`assets/tgo-immersive-test/i18n/${lang}.json`, document.baseURI).href;
36
+ return fetch(url).then((res) => res.json());
37
+ }),
38
+ ],
39
+ changeDetection: ChangeDetectionStrategy.OnPush,
40
+ imports: [
41
+ CommonModule,
42
+ TranslocoModule,
43
+ DialogComponentModule,
44
+ CardComponentModule,
45
+ QuillViewComponent,
46
+ ]
48
47
  })
49
48
  export class ReviewInstructionsDialogComponent implements OnInit {
50
49
  translations: { [key: string]: string } = {};
@@ -56,10 +56,14 @@ describe('RingingPhoneAnimationComponent', () => {
56
56
  });
57
57
 
58
58
  it('should call "playSpy" if "isRinging" changes to true', () => {
59
+ // First set it to false
60
+ component.isRinging = false;
61
+ component.ringingSignal.set(false);
62
+
59
63
  const changes: SimpleChanges = {
60
64
  isRinging: {
61
- previousValue: true,
62
- currentValue: false,
65
+ previousValue: false,
66
+ currentValue: true,
63
67
  firstChange: false,
64
68
  isFirstChange: () => false,
65
69
  },
@@ -75,10 +79,15 @@ describe('RingingPhoneAnimationComponent', () => {
75
79
  });
76
80
 
77
81
  it('should call "pauseSpy" if isRinging changes to false', () => {
82
+ // First set it to true and start ringing
83
+ component.isRinging = true;
84
+ component.ringingSignal.set(true);
85
+ component.audio.play();
86
+
78
87
  const changes: SimpleChanges = {
79
88
  isRinging: {
80
- previousValue: false,
81
- currentValue: true,
89
+ previousValue: true,
90
+ currentValue: false,
82
91
  firstChange: false,
83
92
  isFirstChange: () => false,
84
93
  },
@@ -11,11 +11,10 @@ import { CommonModule } from '@angular/common';
11
11
  import { ringingPhoneSound } from './ringing-phone-animation.sound';
12
12
 
13
13
  @Component({
14
- selector: 'tgo-ringing-phone-animation',
15
- templateUrl: './ringing-phone-animation.component.html',
16
- styleUrls: ['./ringing-phone-animation.component.scss'],
17
- standalone: true,
18
- imports: [CommonModule],
14
+ selector: 'tgo-ringing-phone-animation',
15
+ templateUrl: './ringing-phone-animation.component.html',
16
+ styleUrls: ['./ringing-phone-animation.component.scss'],
17
+ imports: [CommonModule]
19
18
  })
20
19
  export class RingingPhoneAnimationComponent implements OnChanges, OnDestroy {
21
20
  @Input() isRinging = true;
@@ -1,5 +1,5 @@
1
- import { NoopAnimationsModule } from '@angular/platform-browser/animations';
2
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3
3
  import { VideoCountdownComponent } from './video-countdown.component';
4
4
 
5
5
  describe('VideoCountdownComponent', () => {
@@ -34,11 +34,11 @@ describe('VideoCountdownComponent', () => {
34
34
  expect(component.isActive).toBe(true);
35
35
  });
36
36
 
37
- it('should count down from startFrom to endAt', (done) => {
37
+ it('should count down from startFrom to endAt', done => {
38
38
  component.startFrom = 3;
39
39
  component.endAt = 0;
40
40
  component.interval = 100;
41
- const finishSpy = jest.spyOn(component.finish, 'emit');
41
+ const finishSpy = jest.spyOn(component.finished, 'emit');
42
42
 
43
43
  component.start();
44
44
 
@@ -56,4 +56,3 @@ describe('VideoCountdownComponent', () => {
56
56
  expect(clearTimeoutSpy).toHaveBeenCalledWith(component.timeoutId);
57
57
  });
58
58
  });
59
-
@@ -30,14 +30,13 @@ import {
30
30
  ]),
31
31
  ],
32
32
  changeDetection: ChangeDetectionStrategy.OnPush,
33
- standalone: true,
34
33
  imports: [CommonModule],
35
34
  })
36
35
  export class VideoCountdownComponent implements OnInit, OnDestroy {
37
36
  @Input() startFrom = 5;
38
37
  @Input() endAt = 0;
39
38
  @Input() interval = 1325;
40
- @Output() finish: EventEmitter<void> = new EventEmitter<void>();
39
+ @Output() finished: EventEmitter<void> = new EventEmitter<void>();
41
40
 
42
41
  isActive = false;
43
42
  displayNum: { key: number; value: number } = {
@@ -80,7 +79,7 @@ export class VideoCountdownComponent implements OnInit, OnDestroy {
80
79
  this.cdr.markForCheck();
81
80
 
82
81
  if (this.displayNum && this.displayNum.value === this.endAt) {
83
- this.finish.emit();
82
+ this.finished.emit();
84
83
  this.isActive = false;
85
84
  return;
86
85
  }
@@ -99,4 +98,3 @@ export class VideoCountdownComponent implements OnInit, OnDestroy {
99
98
  }
100
99
  }
101
100
  }
102
-
package/src/test-setup.ts CHANGED
@@ -20,3 +20,43 @@ jest.mock('@vimeo/player', () => {
20
20
  };
21
21
  });
22
22
 
23
+ // Global cleanup for media elements after each test
24
+ afterEach(() => {
25
+ // Clean up all audio and video elements in the DOM
26
+ const audioElements = Array.from(document.querySelectorAll('audio'));
27
+ const videoElements = Array.from(document.querySelectorAll('video'));
28
+
29
+ [...audioElements, ...videoElements].forEach((element) => {
30
+ const mediaElement = element as HTMLMediaElement;
31
+ try {
32
+ mediaElement.pause();
33
+ mediaElement.src = '';
34
+ mediaElement.srcObject = null;
35
+ // Remove event listeners by cloning
36
+ const newElement = mediaElement.cloneNode(false);
37
+ mediaElement.parentNode?.replaceChild(newElement, mediaElement);
38
+ } catch (error) {
39
+ // Ignore errors during cleanup
40
+ }
41
+ });
42
+
43
+ // Clean up MediaStream tracks
44
+ if (window.MediaStream) {
45
+ // Stop any active MediaStream tracks
46
+ const streams = Array.from(document.querySelectorAll('video, audio'));
47
+ streams.forEach((element) => {
48
+ const mediaElement = element as HTMLMediaElement;
49
+ if (mediaElement.srcObject instanceof MediaStream) {
50
+ mediaElement.srcObject.getTracks().forEach((track) => {
51
+ track.stop();
52
+ });
53
+ mediaElement.srcObject = null;
54
+ }
55
+ });
56
+ }
57
+
58
+ // Revoke any object URLs that might have been created
59
+ // Note: This is a best-effort cleanup since we can't track all created URLs
60
+ // Components should clean up their own object URLs in ngOnDestroy
61
+ });
62
+
package/tsconfig.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "es2022"
3
+ "target": "es2022",
4
+ "paths": {
5
+ "@testgorilla/tgo-test-shared": ["./src/shared/index.ts"]
6
+ }
4
7
  },
5
8
  "files": [],
6
9
  "include": [],
package/tsconfig.lib.json CHANGED
@@ -5,11 +5,16 @@
5
5
  "declaration": true,
6
6
  "declarationMap": true,
7
7
  "inlineSources": true,
8
- "types": [
9
- "node",
10
- ]
8
+ "baseUrl": "../../",
9
+ "moduleResolution": "node",
10
+ "paths": {
11
+ "@testgorilla/tgo-test-shared": ["packages/tgo-immersive-test/src/shared/index.ts"]
12
+ }
13
+ },
14
+ "angularCompilerOptions": {
15
+ "preserveWhitespaces": false
11
16
  },
12
17
  "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"],
13
- "include": ["src/**/*.ts"]
18
+ "include": ["src/**/*.ts", "src/shared/**/*.ts"]
14
19
  }
15
20
 
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "extends": "./tsconfig.lib.json",
3
3
  "compilerOptions": {
4
- "declarationMap": false
4
+ "declarationMap": false,
5
+ "rootDir": "../../"
5
6
  },
6
7
  "angularCompilerOptions": {
7
8
  "compilationMode": "partial"
@@ -4,10 +4,14 @@
4
4
  "outDir": "../../dist/out-tsc",
5
5
  "module": "commonjs",
6
6
  "target": "es2016",
7
- "types": ["jest", "node"]
7
+ "types": ["jest", "node"],
8
+ "baseUrl": "../../",
9
+ "paths": {
10
+ "@testgorilla/tgo-test-shared": ["packages/tgo-immersive-test/src/shared/index.ts"]
11
+ }
8
12
  },
9
13
  "files": ["src/test-setup.ts"],
10
- "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"],
14
+ "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts", "src/shared/**/*.ts"],
11
15
  "exclude": ["src/lib/models/test.ts"]
12
16
  }
13
17