@testgorilla/tgo-typing-test 1.0.0 → 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.
Files changed (134) hide show
  1. package/.eslintrc.json +46 -0
  2. package/jest.config.ts +21 -0
  3. package/ng-package.json +15 -0
  4. package/package.json +7 -22
  5. package/project.json +36 -0
  6. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.html +30 -0
  7. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.spec.ts +250 -0
  8. package/src/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.ts +47 -0
  9. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.html +72 -0
  10. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.spec.ts +699 -0
  11. package/src/lib/components/tgo-typing-test/tgo-typing-test.component.ts +287 -0
  12. package/src/lib/helpers/config.ts +28 -0
  13. package/src/lib/helpers/constants/default-config.ts +103 -0
  14. package/src/lib/helpers/controllers/input-controller.ts +710 -0
  15. package/src/lib/helpers/controllers/quotes-controller.ts +183 -0
  16. package/src/lib/helpers/observables/banner-event.ts +18 -0
  17. package/src/lib/helpers/observables/config-event.ts +31 -0
  18. package/src/lib/helpers/observables/timer-event.ts +18 -0
  19. package/src/lib/helpers/states/active-page.ts +9 -0
  20. package/src/lib/helpers/states/composition.ts +29 -0
  21. package/src/lib/helpers/states/page-transition.ts +9 -0
  22. package/src/lib/helpers/states/slow-timer.ts +16 -0
  23. package/src/lib/helpers/states/test-active.ts +9 -0
  24. package/src/lib/helpers/states/time.ts +13 -0
  25. package/src/lib/helpers/test/caps-warning.ts +50 -0
  26. package/src/lib/helpers/test/caret.ts +92 -0
  27. package/src/lib/helpers/test/custom-text.ts +73 -0
  28. package/src/lib/helpers/test/english-punctuation.ts +38 -0
  29. package/src/lib/helpers/test/focus.ts +39 -0
  30. package/src/lib/helpers/test/manual-restart-tracker.ts +13 -0
  31. package/src/lib/helpers/test/out-of-focus.ts +19 -0
  32. package/src/lib/helpers/test/replay.ts +265 -0
  33. package/src/lib/helpers/test/test-input.ts +320 -0
  34. package/src/lib/helpers/test/test-logic.ts +1039 -0
  35. package/src/lib/helpers/test/test-state.ts +17 -0
  36. package/src/lib/helpers/test/test-stats.ts +442 -0
  37. package/src/lib/helpers/test/test-timer.ts +209 -0
  38. package/src/lib/helpers/test/test-ui.ts +370 -0
  39. package/src/lib/helpers/test/test-words.ts +72 -0
  40. package/src/lib/helpers/test/timer-progress.ts +16 -0
  41. package/src/lib/helpers/test/tts.ts +42 -0
  42. package/src/lib/helpers/test/weak-spot.ts +74 -0
  43. package/src/lib/helpers/test/wordset.ts +109 -0
  44. package/src/lib/styles/animations.scss +101 -0
  45. package/src/lib/styles/caret.scss +108 -0
  46. package/src/lib/styles/core.scss +498 -0
  47. package/src/lib/styles/index.scss +19 -0
  48. package/src/lib/styles/inputs.scss +290 -0
  49. package/src/lib/styles/popups.scss +1311 -0
  50. package/src/lib/styles/test.scss +1008 -0
  51. package/src/lib/styles/z_media-queries.scss +848 -0
  52. package/src/lib/types/types.d.ts +731 -0
  53. package/src/lib/utils/misc.ts +776 -0
  54. package/src/test-setup.ts +20 -0
  55. package/tsconfig.json +16 -0
  56. package/tsconfig.lib.json +14 -0
  57. package/tsconfig.lib.prod.json +9 -0
  58. package/tsconfig.spec.json +11 -0
  59. package/esm2022/index.mjs +0 -3
  60. package/esm2022/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.mjs +0 -45
  61. package/esm2022/lib/components/tgo-typing-test/tgo-typing-test.component.mjs +0 -299
  62. package/esm2022/lib/helpers/config.mjs +0 -24
  63. package/esm2022/lib/helpers/constants/default-config.mjs +0 -103
  64. package/esm2022/lib/helpers/controllers/input-controller.mjs +0 -586
  65. package/esm2022/lib/helpers/controllers/quotes-controller.mjs +0 -118
  66. package/esm2022/lib/helpers/observables/config-event.mjs +0 -16
  67. package/esm2022/lib/helpers/observables/timer-event.mjs +0 -16
  68. package/esm2022/lib/helpers/states/active-page.mjs +0 -8
  69. package/esm2022/lib/helpers/states/composition.mjs +0 -20
  70. package/esm2022/lib/helpers/states/page-transition.mjs +0 -8
  71. package/esm2022/lib/helpers/states/slow-timer.mjs +0 -15
  72. package/esm2022/lib/helpers/states/test-active.mjs +0 -8
  73. package/esm2022/lib/helpers/states/time.mjs +0 -11
  74. package/esm2022/lib/helpers/test/caps-warning.mjs +0 -50
  75. package/esm2022/lib/helpers/test/caret.mjs +0 -80
  76. package/esm2022/lib/helpers/test/custom-text.mjs +0 -59
  77. package/esm2022/lib/helpers/test/english-punctuation.mjs +0 -29
  78. package/esm2022/lib/helpers/test/focus.mjs +0 -35
  79. package/esm2022/lib/helpers/test/manual-restart-tracker.mjs +0 -11
  80. package/esm2022/lib/helpers/test/out-of-focus.mjs +0 -14
  81. package/esm2022/lib/helpers/test/replay.mjs +0 -217
  82. package/esm2022/lib/helpers/test/test-input.mjs +0 -264
  83. package/esm2022/lib/helpers/test/test-logic.mjs +0 -927
  84. package/esm2022/lib/helpers/test/test-state.mjs +0 -13
  85. package/esm2022/lib/helpers/test/test-stats.mjs +0 -342
  86. package/esm2022/lib/helpers/test/test-timer.mjs +0 -207
  87. package/esm2022/lib/helpers/test/test-ui.mjs +0 -341
  88. package/esm2022/lib/helpers/test/test-words.mjs +0 -69
  89. package/esm2022/lib/helpers/test/timer-progress.mjs +0 -15
  90. package/esm2022/lib/helpers/test/weak-spot.mjs +0 -65
  91. package/esm2022/lib/helpers/test/wordset.mjs +0 -100
  92. package/esm2022/lib/utils/misc.mjs +0 -673
  93. package/esm2022/testgorilla-tgo-typing-test.mjs +0 -5
  94. package/fesm2022/testgorilla-tgo-typing-test.mjs +0 -4707
  95. package/fesm2022/testgorilla-tgo-typing-test.mjs.map +0 -1
  96. package/lib/components/tgo-typing-replay-input/tgo-typing-replay-input.component.d.ts +0 -14
  97. package/lib/components/tgo-typing-test/tgo-typing-test.component.d.ts +0 -54
  98. package/lib/helpers/config.d.ts +0 -98
  99. package/lib/helpers/constants/default-config.d.ts +0 -3
  100. package/lib/helpers/controllers/input-controller.d.ts +0 -16
  101. package/lib/helpers/controllers/quotes-controller.d.ts +0 -20
  102. package/lib/helpers/observables/config-event.d.ts +0 -5
  103. package/lib/helpers/observables/timer-event.d.ts +0 -4
  104. package/lib/helpers/states/active-page.d.ts +0 -2
  105. package/lib/helpers/states/composition.d.ts +0 -10
  106. package/lib/helpers/states/page-transition.d.ts +0 -2
  107. package/lib/helpers/states/slow-timer.d.ts +0 -3
  108. package/lib/helpers/states/test-active.d.ts +0 -2
  109. package/lib/helpers/states/time.d.ts +0 -3
  110. package/lib/helpers/test/caps-warning.d.ts +0 -5
  111. package/lib/helpers/test/caret.d.ts +0 -11
  112. package/lib/helpers/test/custom-text.d.ts +0 -16
  113. package/lib/helpers/test/english-punctuation.d.ts +0 -3
  114. package/lib/helpers/test/focus.d.ts +0 -7
  115. package/lib/helpers/test/manual-restart-tracker.d.ts +0 -3
  116. package/lib/helpers/test/out-of-focus.d.ts +0 -4
  117. package/lib/helpers/test/replay.d.ts +0 -20
  118. package/lib/helpers/test/test-input.d.ts +0 -86
  119. package/lib/helpers/test/test-logic.d.ts +0 -25
  120. package/lib/helpers/test/test-state.d.ts +0 -7
  121. package/lib/helpers/test/test-stats.d.ts +0 -92
  122. package/lib/helpers/test/test-timer.d.ts +0 -6
  123. package/lib/helpers/test/test-ui.d.ts +0 -27
  124. package/lib/helpers/test/test-words.d.ts +0 -23
  125. package/lib/helpers/test/timer-progress.d.ts +0 -3
  126. package/lib/helpers/test/weak-spot.d.ts +0 -3
  127. package/lib/helpers/test/wordset.d.ts +0 -7
  128. package/lib/utils/misc.d.ts +0 -81
  129. /package/{assets → src/assets}/typing-test-languages/english.json +0 -0
  130. /package/{assets → src/assets}/typing-test-languages/english_punctuation.json +0 -0
  131. /package/{assets → src/assets}/typing-test-languages/quotes/english_version_1.json +0 -0
  132. /package/{assets → src/assets}/typing-test-languages/quotes/english_version_2.json +0 -0
  133. /package/{assets → src/assets}/typing-test-languages/quotes/filtered_sources.json +0 -0
  134. /package/{index.d.ts → src/index.ts} +0 -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
+ "@angular-eslint/prefer-standalone": "off"
26
+ }
27
+ },
28
+ {
29
+ "files": ["*.html"],
30
+ "extends": ["plugin:@nx/angular-template"],
31
+ "rules": {}
32
+ },
33
+ {
34
+ "files": ["*.json"],
35
+ "parser": "jsonc-eslint-parser",
36
+ "rules": {
37
+ "@nx/dependency-checks": [
38
+ "error",
39
+ {
40
+ "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"]
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ ]
46
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,21 @@
1
+ export default {
2
+ displayName: 'tgo-typing-test',
3
+ preset: '../../jest.preset.js',
4
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5
+ coverageDirectory: '../../coverage/packages/tgo-typing-test',
6
+ transform: {
7
+ '^.+\\.(ts|mjs|js|html)$': [
8
+ 'jest-preset-angular',
9
+ {
10
+ tsconfig: '<rootDir>/tsconfig.spec.json',
11
+ stringifyContentPathRegex: '\\.(html|svg)$',
12
+ },
13
+ ],
14
+ },
15
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16
+ snapshotSerializers: [
17
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
18
+ 'jest-preset-angular/build/serializers/ng-snapshot',
19
+ 'jest-preset-angular/build/serializers/html-comment',
20
+ ],
21
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/packages/tgo-typing-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
+ }
package/package.json CHANGED
@@ -1,31 +1,16 @@
1
1
  {
2
2
  "name": "@testgorilla/tgo-typing-test",
3
- "version": "1.0.0",
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
- "jest-preset-angular": "~14.2.4",
5
+ "@angular/common": "~19.2.17",
6
+ "@angular/core": "~19.2.17",
7
+ "@angular/animations": "~19.2.17",
8
+ "jest-preset-angular": "14.4.2",
9
9
  "rxjs": "~7.8.1"
10
10
  },
11
- "typings": "index.d.ts",
11
+ "typings": "src/lib/types",
12
12
  "sideEffects": false,
13
13
  "license": "PROPRIETARY",
14
14
  "displayName": "Typing Test",
15
- "description": "Typing Test component",
16
- "module": "fesm2022/testgorilla-tgo-typing-test.mjs",
17
- "exports": {
18
- "./package.json": {
19
- "default": "./package.json"
20
- },
21
- ".": {
22
- "types": "./index.d.ts",
23
- "esm2022": "./esm2022/testgorilla-tgo-typing-test.mjs",
24
- "esm": "./esm2022/testgorilla-tgo-typing-test.mjs",
25
- "default": "./fesm2022/testgorilla-tgo-typing-test.mjs"
26
- }
27
- },
28
- "dependencies": {
29
- "tslib": "^2.3.0"
30
- }
15
+ "description": "Typing Test component"
31
16
  }
package/project.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "tgo-typing-test",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/tgo-typing-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-typing-test/ng-package.json"
14
+ },
15
+ "configurations": {
16
+ "production": {
17
+ "tsConfig": "packages/tgo-typing-test/tsconfig.lib.prod.json"
18
+ },
19
+ "development": {
20
+ "tsConfig": "packages/tgo-typing-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-typing-test/jest.config.ts"
30
+ }
31
+ },
32
+ "lint": {
33
+ "executor": "@nx/eslint:lint"
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,30 @@
1
+ <div id="centerContent">
2
+ <div id="middle" #middle>
3
+ <div class="page pageTest active">
4
+ <div id="result" tabindex="0">
5
+ <div class="bottom" style="grid-column: 1/3">
6
+ <div id="resultReplay">
7
+ <div class="title">
8
+ Watch replay
9
+ <span
10
+ id="playpauseReplayButton"
11
+ class="textButton"
12
+ aria-label="Start replay"
13
+ data-balloon-pos="up"
14
+ style="display: inline-block"
15
+ (click)="playpauseReplayButtonClick()"
16
+ #toggleButton
17
+ >
18
+ <div id="key" class="Play" #playpauseelem>{{ playpauseelem.className }}</div>
19
+ </span>
20
+ <p id="replayStopwatch" #replayStopwatch>0s</p>
21
+ </div>
22
+ <div id="wordsWrapper">
23
+ <div id="replayWords" class="words"></div>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
@@ -0,0 +1,250 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { ElementRef } from '@angular/core';
3
+ import { TgoTypingReplayInputComponent } from './tgo-typing-replay-input.component';
4
+ import * as config from '../../helpers/config';
5
+ import * as replay from '../../helpers/test/replay';
6
+
7
+ jest.mock('../../helpers/config');
8
+ jest.mock('../../helpers/test/replay');
9
+
10
+ describe('TgoTypingReplayInputComponent', () => {
11
+ let component: TgoTypingReplayInputComponent;
12
+ let fixture: ComponentFixture<TgoTypingReplayInputComponent>;
13
+
14
+ const mockReplayExport = JSON.stringify({
15
+ wordsList: ['word1', 'word2', 'word3'],
16
+ replayData: [
17
+ { timestamp: 0, char: 'w' },
18
+ { timestamp: 100, char: 'o' },
19
+ ],
20
+ });
21
+
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+
25
+ TestBed.configureTestingModule({
26
+ imports: [TgoTypingReplayInputComponent],
27
+ });
28
+
29
+ fixture = TestBed.createComponent(TgoTypingReplayInputComponent);
30
+ component = fixture.componentInstance;
31
+ });
32
+
33
+ describe('when initialized', () => {
34
+ it('should create the component', () => {
35
+ expect(component).toBeDefined();
36
+ });
37
+
38
+ it('should have default funbox value', () => {
39
+ expect(component.funbox).toBe('none');
40
+ });
41
+ });
42
+
43
+ describe('#ngOnInit', () => {
44
+ beforeEach(() => {
45
+ component.replayExport = mockReplayExport;
46
+ });
47
+
48
+ it('should parse the replayExport JSON string', () => {
49
+ component.ngOnInit();
50
+
51
+ expect(component.replayExportData).toEqual({
52
+ wordsList: ['word1', 'word2', 'word3'],
53
+ replayData: [
54
+ { timestamp: 0, char: 'w' },
55
+ { timestamp: 100, char: 'o' },
56
+ ],
57
+ });
58
+ });
59
+
60
+ it('should call setFunbox with the funbox input value', () => {
61
+ component.funbox = 'custom-funbox';
62
+
63
+ component.ngOnInit();
64
+
65
+ expect(config.setFunbox).toHaveBeenCalledWith('custom-funbox');
66
+ expect(config.setFunbox).toHaveBeenCalledTimes(1);
67
+ });
68
+
69
+ it('should call replayGetWordsList with parsed wordsList', () => {
70
+ component.ngOnInit();
71
+
72
+ expect(replay.replayGetWordsList).toHaveBeenCalledWith(['word1', 'word2', 'word3']);
73
+ expect(replay.replayGetWordsList).toHaveBeenCalledTimes(1);
74
+ });
75
+
76
+ it('should call setReplayData with parsed replayData', () => {
77
+ component.ngOnInit();
78
+
79
+ expect(replay.setReplayData).toHaveBeenCalledWith([
80
+ { timestamp: 0, char: 'w' },
81
+ { timestamp: 100, char: 'o' },
82
+ ]);
83
+ expect(replay.setReplayData).toHaveBeenCalledTimes(1);
84
+ });
85
+
86
+ describe('when replayExport contains different data', () => {
87
+ it('should handle empty wordsList and replayData', () => {
88
+ component.replayExport = JSON.stringify({
89
+ wordsList: [],
90
+ replayData: [],
91
+ });
92
+
93
+ component.ngOnInit();
94
+
95
+ expect(replay.replayGetWordsList).toHaveBeenCalledWith([]);
96
+ expect(replay.setReplayData).toHaveBeenCalledWith([]);
97
+ });
98
+
99
+ it('should handle large wordsList', () => {
100
+ const largeWordsList = Array.from({ length: 100 }, (_, i) => `word${i}`);
101
+ component.replayExport = JSON.stringify({
102
+ wordsList: largeWordsList,
103
+ replayData: [],
104
+ });
105
+
106
+ component.ngOnInit();
107
+
108
+ expect(replay.replayGetWordsList).toHaveBeenCalledWith(largeWordsList);
109
+ });
110
+ });
111
+ });
112
+
113
+ describe('#ngAfterViewInit', () => {
114
+ let mockToggleButtonElement: HTMLElement;
115
+ let mockReplayStopwatchElement: HTMLElement;
116
+
117
+ beforeEach(() => {
118
+ component.replayExport = mockReplayExport;
119
+ component.ngOnInit();
120
+
121
+ // Create mock elements
122
+ mockToggleButtonElement = document.createElement('button');
123
+ mockReplayStopwatchElement = document.createElement('div');
124
+
125
+ // Mock ViewChild ElementRef
126
+ component.toggleButton = {
127
+ nativeElement: mockToggleButtonElement,
128
+ } as ElementRef;
129
+ component.replayStopwatch = {
130
+ nativeElement: mockReplayStopwatchElement,
131
+ } as ElementRef;
132
+ });
133
+
134
+ it('should call setToggleButton with toggleButton native element', () => {
135
+ component.ngAfterViewInit();
136
+
137
+ expect(replay.setToggleButton).toHaveBeenCalledWith(mockToggleButtonElement);
138
+ expect(replay.setToggleButton).toHaveBeenCalledTimes(1);
139
+ });
140
+
141
+ it('should call setReplayStopwatch with replayStopwatch native element', () => {
142
+ component.ngAfterViewInit();
143
+
144
+ expect(replay.setReplayStopwatch).toHaveBeenCalledWith(mockReplayStopwatchElement);
145
+ expect(replay.setReplayStopwatch).toHaveBeenCalledTimes(1);
146
+ });
147
+
148
+ it('should call initializeReplayPrompt', () => {
149
+ component.ngAfterViewInit();
150
+
151
+ expect(replay.initializeReplayPrompt).toHaveBeenCalledTimes(1);
152
+ });
153
+
154
+ it('should call loadOldReplay', () => {
155
+ component.ngAfterViewInit();
156
+
157
+ expect(replay.loadOldReplay).toHaveBeenCalledTimes(1);
158
+ });
159
+
160
+ it('should call playReplay', () => {
161
+ component.ngAfterViewInit();
162
+
163
+ expect(replay.playReplay).toHaveBeenCalledTimes(1);
164
+ });
165
+
166
+ it('should call replay initialization functions in correct order', () => {
167
+ const callOrder: string[] = [];
168
+
169
+ jest.mocked(replay.setToggleButton).mockImplementation(() => {
170
+ callOrder.push('setToggleButton');
171
+ });
172
+ jest.mocked(replay.setReplayStopwatch).mockImplementation(() => {
173
+ callOrder.push('setReplayStopwatch');
174
+ });
175
+ jest.mocked(replay.initializeReplayPrompt).mockImplementation(() => {
176
+ callOrder.push('initializeReplayPrompt');
177
+ });
178
+ jest.mocked(replay.loadOldReplay).mockImplementation(() => {
179
+ callOrder.push('loadOldReplay');
180
+ return 0;
181
+ });
182
+ jest.mocked(replay.playReplay).mockImplementation(() => {
183
+ callOrder.push('playReplay');
184
+ });
185
+
186
+ component.ngAfterViewInit();
187
+
188
+ expect(callOrder).toEqual([
189
+ 'setToggleButton',
190
+ 'setReplayStopwatch',
191
+ 'initializeReplayPrompt',
192
+ 'loadOldReplay',
193
+ 'playReplay',
194
+ ]);
195
+ });
196
+ });
197
+
198
+ describe('#playpauseReplayButtonClick', () => {
199
+ it('should call the imported playpauseReplayButtonClick function', () => {
200
+ component.playpauseReplayButtonClick();
201
+
202
+ expect(replay.playpauseReplayButtonClick).toHaveBeenCalledTimes(1);
203
+ });
204
+
205
+ it('should call playpauseReplayButtonClick on multiple invocations', () => {
206
+ component.playpauseReplayButtonClick();
207
+ component.playpauseReplayButtonClick();
208
+ component.playpauseReplayButtonClick();
209
+
210
+ expect(replay.playpauseReplayButtonClick).toHaveBeenCalledTimes(3);
211
+ });
212
+ });
213
+
214
+ describe('when component lifecycle is complete', () => {
215
+ let mockToggleButtonElement: HTMLElement;
216
+ let mockReplayStopwatchElement: HTMLElement;
217
+
218
+ beforeEach(() => {
219
+ component.replayExport = mockReplayExport;
220
+ component.funbox = 'test-funbox';
221
+
222
+ mockToggleButtonElement = document.createElement('button');
223
+ mockReplayStopwatchElement = document.createElement('div');
224
+
225
+ component.toggleButton = {
226
+ nativeElement: mockToggleButtonElement,
227
+ } as ElementRef;
228
+ component.replayStopwatch = {
229
+ nativeElement: mockReplayStopwatchElement,
230
+ } as ElementRef;
231
+ });
232
+
233
+ it('should initialize and setup replay completely', () => {
234
+ // Simulate full lifecycle
235
+ component.ngOnInit();
236
+ component.ngAfterViewInit();
237
+
238
+ // Verify all initialization steps were called
239
+ expect(config.setFunbox).toHaveBeenCalledWith('test-funbox');
240
+ expect(replay.replayGetWordsList).toHaveBeenCalled();
241
+ expect(replay.setReplayData).toHaveBeenCalled();
242
+ expect(replay.setToggleButton).toHaveBeenCalledWith(mockToggleButtonElement);
243
+ expect(replay.setReplayStopwatch).toHaveBeenCalledWith(mockReplayStopwatchElement);
244
+ expect(replay.initializeReplayPrompt).toHaveBeenCalled();
245
+ expect(replay.loadOldReplay).toHaveBeenCalled();
246
+ expect(replay.playReplay).toHaveBeenCalled();
247
+ });
248
+ });
249
+ });
250
+
@@ -0,0 +1,47 @@
1
+ import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
2
+ import { setFunbox } from '../../helpers/config';
3
+ import {
4
+ initializeReplayPrompt,
5
+ loadOldReplay,
6
+ playpauseReplayButtonClick,
7
+ playReplay,
8
+ replayGetWordsList,
9
+ setReplayData,
10
+ setReplayStopwatch,
11
+ setToggleButton,
12
+ } from '../../helpers/test/replay';
13
+ import { CommonModule } from '@angular/common';
14
+
15
+ @Component({
16
+ selector: 'tgo-typing-replay-input',
17
+ imports: [CommonModule],
18
+ templateUrl: './tgo-typing-replay-input.component.html',
19
+ styleUrls: ['../../styles/index.scss']
20
+ })
21
+ export class TgoTypingReplayInputComponent implements OnInit, AfterViewInit {
22
+ @Input() replayExport: any;
23
+ @Input() funbox = 'none';
24
+ replayExportData: any;
25
+
26
+ @ViewChild('toggleButton') toggleButton!: ElementRef;
27
+ @ViewChild('replayStopwatch') replayStopwatch!: ElementRef;
28
+
29
+ ngOnInit(): void {
30
+ this.replayExportData = JSON.parse(this.replayExport);
31
+ setFunbox(this.funbox);
32
+ replayGetWordsList(this.replayExportData['wordsList']);
33
+ setReplayData(this.replayExportData['replayData']);
34
+ }
35
+
36
+ ngAfterViewInit() {
37
+ setToggleButton(this.toggleButton.nativeElement);
38
+ setReplayStopwatch(this.replayStopwatch.nativeElement);
39
+ initializeReplayPrompt();
40
+ loadOldReplay();
41
+ playReplay();
42
+ }
43
+
44
+ playpauseReplayButtonClick() {
45
+ playpauseReplayButtonClick();
46
+ }
47
+ }
@@ -0,0 +1,72 @@
1
+ <div id="centerContent">
2
+ <div id="middle" #middle>
3
+ <div class="page pageTest active">
4
+ <div id="typingTest">
5
+ <div id="capsWarning" [ngClass]="{ hidden: (showCaps$ | async) === false }" #capsWarning>
6
+ Caps Lock
7
+ </div>
8
+ <div id="memoryTimer">Time left to memorise all words: 0s</div>
9
+ <div id="testModesNotice"></div>
10
+ <input
11
+ id="wordsInput"
12
+ class=""
13
+ tabindex="0"
14
+ type="text"
15
+ autocomplete="off"
16
+ autocapitalize="off"
17
+ autocorrect="off"
18
+ data-gramm="false"
19
+ data-gramm_editor="false"
20
+ data-enable-grammarly="false"
21
+ list="autocompleteOff"
22
+ (keyup)="wordsInputKeyup($event)"
23
+ (beforeinput)="wordsInputBeforeinput($event)"
24
+ (input)="wordsInputInput($event)"
25
+ (focus)="wordsInputFocus($event)"
26
+ (focusout)="wordsInputFocusOut()"
27
+ (copy)="wordsInputCopy($event)"
28
+ (paste)="wordsInputPaste($event)"
29
+ (compositionstart)="wordsInputCompositionstart()"
30
+ (compositionend)="wordsInputCompositionend()"
31
+ #wordsInput
32
+ />
33
+ @if (showMiniTimerAndLiveWpm) {
34
+ <div id="miniTimerAndLiveWpm" class="timerMain size2">
35
+ <div class="time" [@fadeIn]="(showTimer$ | async) ? 'show' : 'hide'">
36
+ {{ timeLeft$ | async }}
37
+ </div>
38
+ <div class="wpm hidden">60</div>
39
+ <div class="acc hidden">100%</div>
40
+ <div class="burst hidden">1</div>
41
+ </div>
42
+ }
43
+
44
+ @if (outOfFocusShow$ | async) {
45
+ <div class="outOfFocusWarning">
46
+ Click or press any key to focus
47
+ </div>
48
+ }
49
+
50
+ @if (errorEvent | async; as errorMessage) {
51
+ <div class="errorWarning">
52
+ {{ errorMessage }} Reload the page or <a href="https://candidates.testgorilla.com/hc/en-us/requests/new" target="_blank">contact support</a>.
53
+ </div>
54
+ }
55
+
56
+ <div id="wordsWrapper" translate="no" #wordsWrapper (click)="wordsWrapperClick()">
57
+ <div id="paceCaret" class="default size2 hidden"></div>
58
+ @if (showCaret$ | async) {
59
+ <div id="caret" [@updatePositionCaret]="{
60
+ value: state,
61
+ params: { caretTopPos: caretTop, caretLeftPos: caretLeft }
62
+ }"
63
+ [ngStyle]="{ 'animation-name': (caretAnimation$ | async) }" class="default size2">
64
+ </div>
65
+ }
66
+ <div id="words" class="size2" #words [ngClass]="{ blurred: (outOfFocusShow$ | async) }"></div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+