@testgorilla/tgo-coding-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 (163) hide show
  1. package/.eslintrc.json +45 -0
  2. package/README.md +257 -0
  3. package/jest.config.ts +21 -0
  4. package/ng-package.json +14 -0
  5. package/package.json +33 -0
  6. package/project.json +36 -0
  7. package/src/assets/i18n/en.json +124 -0
  8. package/src/index.ts +30 -0
  9. package/src/lib/components/.gitkeep +0 -0
  10. package/src/lib/components/code-editor/code-editor.component.html +10 -0
  11. package/src/lib/components/code-editor/code-editor.component.scss +21 -0
  12. package/src/lib/components/code-editor/code-editor.component.spec.ts +136 -0
  13. package/src/lib/components/code-editor/code-editor.component.ts +369 -0
  14. package/src/lib/components/code-editor/code-editor.mocks.ts +28 -0
  15. package/src/lib/components/code-editor/code-editor.service.spec.ts +160 -0
  16. package/src/lib/components/code-editor/code-editor.service.ts +94 -0
  17. package/src/lib/components/code-editor/helpers/c-helper.spec.ts +39 -0
  18. package/src/lib/components/code-editor/helpers/c-helper.ts +51 -0
  19. package/src/lib/components/code-editor/helpers/code-editor-helper.base.spec.ts +30 -0
  20. package/src/lib/components/code-editor/helpers/code-editor-helper.base.ts +16 -0
  21. package/src/lib/components/code-editor/helpers/code-editor-helper.mocks.ts +24 -0
  22. package/src/lib/components/code-editor/helpers/code-editor-helper.model.ts +67 -0
  23. package/src/lib/components/code-editor/helpers/cpp-helper.spec.ts +40 -0
  24. package/src/lib/components/code-editor/helpers/cpp-helper.ts +52 -0
  25. package/src/lib/components/code-editor/helpers/csharp-helper.spec.ts +42 -0
  26. package/src/lib/components/code-editor/helpers/csharp-helper.ts +55 -0
  27. package/src/lib/components/code-editor/helpers/go-helper.spec.ts +41 -0
  28. package/src/lib/components/code-editor/helpers/go-helper.ts +54 -0
  29. package/src/lib/components/code-editor/helpers/index.ts +15 -0
  30. package/src/lib/components/code-editor/helpers/java-helper.spec.ts +41 -0
  31. package/src/lib/components/code-editor/helpers/java-helper.ts +54 -0
  32. package/src/lib/components/code-editor/helpers/javascript-helper.spec.ts +39 -0
  33. package/src/lib/components/code-editor/helpers/javascript-helper.ts +32 -0
  34. package/src/lib/components/code-editor/helpers/kotlin-helper.spec.ts +41 -0
  35. package/src/lib/components/code-editor/helpers/kotlin-helper.ts +54 -0
  36. package/src/lib/components/code-editor/helpers/php-helper.spec.ts +39 -0
  37. package/src/lib/components/code-editor/helpers/php-helper.ts +32 -0
  38. package/src/lib/components/code-editor/helpers/python-helper.spec.ts +39 -0
  39. package/src/lib/components/code-editor/helpers/python-helper.ts +32 -0
  40. package/src/lib/components/code-editor/helpers/r-helper.spec.ts +39 -0
  41. package/src/lib/components/code-editor/helpers/r-helper.ts +32 -0
  42. package/src/lib/components/code-editor/helpers/ruby-helper.spec.ts +39 -0
  43. package/src/lib/components/code-editor/helpers/ruby-helper.ts +32 -0
  44. package/src/lib/components/code-editor/helpers/scala-helper.spec.ts +41 -0
  45. package/src/lib/components/code-editor/helpers/scala-helper.ts +53 -0
  46. package/src/lib/components/code-editor/helpers/sql-helper.spec.ts +87 -0
  47. package/src/lib/components/code-editor/helpers/sql-helper.ts +44 -0
  48. package/src/lib/components/code-editor/helpers/swift-helper.spec.ts +40 -0
  49. package/src/lib/components/code-editor/helpers/swift-helper.ts +51 -0
  50. package/src/lib/components/code-editor/helpers/typescript-helper.spec.ts +40 -0
  51. package/src/lib/components/code-editor/helpers/typescript-helper.ts +52 -0
  52. package/src/lib/components/code-editor/models/code-editor.model.ts +9 -0
  53. package/src/lib/components/code-editor/models/coding-snapshot.model.ts +4 -0
  54. package/src/lib/components/coding-question/coding-question.component.html +78 -0
  55. package/src/lib/components/coding-question/coding-question.component.scss +76 -0
  56. package/src/lib/components/coding-question/coding-question.component.spec.ts +85 -0
  57. package/src/lib/components/coding-question/coding-question.component.ts +102 -0
  58. package/src/lib/components/coding-section/coding-section.component.html +82 -0
  59. package/src/lib/components/coding-section/coding-section.component.scss +64 -0
  60. package/src/lib/components/coding-section/coding-section.component.spec.ts +257 -0
  61. package/src/lib/components/coding-section/coding-section.component.ts +187 -0
  62. package/src/lib/components/coding-test.module.ts +124 -0
  63. package/src/lib/components/common/truncated-text/truncated-text.component.html +6 -0
  64. package/src/lib/components/common/truncated-text/truncated-text.component.scss +18 -0
  65. package/src/lib/components/common/truncated-text/truncated-text.component.spec.ts +84 -0
  66. package/src/lib/components/common/truncated-text/truncated-text.component.ts +37 -0
  67. package/src/lib/components/configurations/configurations.component.html +57 -0
  68. package/src/lib/components/configurations/configurations.component.scss +42 -0
  69. package/src/lib/components/configurations/configurations.component.spec.ts +186 -0
  70. package/src/lib/components/configurations/configurations.component.ts +98 -0
  71. package/src/lib/components/instructions/instructions.component.html +41 -0
  72. package/src/lib/components/instructions/instructions.component.scss +167 -0
  73. package/src/lib/components/instructions/instructions.component.spec.ts +106 -0
  74. package/src/lib/components/instructions/instructions.component.ts +138 -0
  75. package/src/lib/components/panel/panel.component.html +19 -0
  76. package/src/lib/components/panel/panel.component.scss +41 -0
  77. package/src/lib/components/panel/panel.component.spec.ts +40 -0
  78. package/src/lib/components/panel/panel.component.ts +34 -0
  79. package/src/lib/components/runnable-editor/runnable-editor.component.html +75 -0
  80. package/src/lib/components/runnable-editor/runnable-editor.component.scss +55 -0
  81. package/src/lib/components/runnable-editor/runnable-editor.component.spec.ts +124 -0
  82. package/src/lib/components/runnable-editor/runnable-editor.component.ts +155 -0
  83. package/src/lib/components/tests/test-cases/test-cases.component.html +135 -0
  84. package/src/lib/components/tests/test-cases/test-cases.component.scss +220 -0
  85. package/src/lib/components/tests/test-cases/test-cases.component.spec.ts +401 -0
  86. package/src/lib/components/tests/test-cases/test-cases.component.ts +205 -0
  87. package/src/lib/components/tests/test-cases-content/test-cases-content.component.html +94 -0
  88. package/src/lib/components/tests/test-cases-content/test-cases-content.component.scss +103 -0
  89. package/src/lib/components/tests/test-cases-content/test-cases-content.component.spec.ts +122 -0
  90. package/src/lib/components/tests/test-cases-content/test-cases-content.component.ts +102 -0
  91. package/src/lib/components/tests/test-cases-status/test-cases-status.component.html +16 -0
  92. package/src/lib/components/tests/test-cases-status/test-cases-status.component.scss +49 -0
  93. package/src/lib/components/tests/test-cases-status/test-cases-status.component.spec.ts +22 -0
  94. package/src/lib/components/tests/test-cases-status/test-cases-status.component.ts +18 -0
  95. package/src/lib/components/tests/test-results.component.html +119 -0
  96. package/src/lib/components/tests/test-results.component.scss +189 -0
  97. package/src/lib/components/tests/test-results.component.spec.ts +140 -0
  98. package/src/lib/components/tests/test-results.component.ts +98 -0
  99. package/src/lib/components/tgo-coding-test/tgo-coding-test.component.html +96 -0
  100. package/src/lib/components/tgo-coding-test/tgo-coding-test.component.scss +6 -0
  101. package/src/lib/components/tgo-coding-test/tgo-coding-test.component.spec.ts +599 -0
  102. package/src/lib/components/tgo-coding-test/tgo-coding-test.component.ts +279 -0
  103. package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.html +36 -0
  104. package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.scss +183 -0
  105. package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.spec.ts +883 -0
  106. package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.ts +575 -0
  107. package/src/lib/config/index.ts +3 -0
  108. package/src/lib/config/tgo-coding-test.config.ts +26 -0
  109. package/src/lib/config/tgo-coding-test.provider.ts +38 -0
  110. package/src/lib/config/tgo-coding-test.token.ts +21 -0
  111. package/src/lib/models/.gitkeep +0 -0
  112. package/src/lib/models/auto-saved-data.ts +51 -0
  113. package/src/lib/models/code-event.ts +17 -0
  114. package/src/lib/models/coderunner-execution-results.ts +58 -0
  115. package/src/lib/models/coding-lib.mocks.ts +246 -0
  116. package/src/lib/models/configs.ts +18 -0
  117. package/src/lib/models/language-change-action.ts +4 -0
  118. package/src/lib/models/lat-languages.ts +12 -0
  119. package/src/lib/models/mixpanel-events.ts +3 -0
  120. package/src/lib/models/mode.ts +5 -0
  121. package/src/lib/models/paste-data.ts +4 -0
  122. package/src/lib/models/programming-language.ts +9 -0
  123. package/src/lib/models/test-cases.ts +74 -0
  124. package/src/lib/models/theme.ts +5 -0
  125. package/src/lib/models/translations.ts +1 -0
  126. package/src/lib/models/view-mode.ts +6 -0
  127. package/src/lib/pipes/memoize-func.pipe.ts +34 -0
  128. package/src/lib/services/.gitkeep +0 -0
  129. package/src/lib/services/candidate-coding-test-services/candidature-api.service.spec.ts +40 -0
  130. package/src/lib/services/candidate-coding-test-services/candidature-api.service.ts +15 -0
  131. package/src/lib/services/candidate-coding-test-services/coderunner-api.service.spec.ts +134 -0
  132. package/src/lib/services/candidate-coding-test-services/coderunner-api.service.ts +105 -0
  133. package/src/lib/services/candidate-coding-test-services/coding-test-tour.service.spec.ts +161 -0
  134. package/src/lib/services/candidate-coding-test-services/coding-test-tour.service.ts +100 -0
  135. package/src/lib/services/candidate-coding-test-services/coding-test.service.spec.ts +1524 -0
  136. package/src/lib/services/candidate-coding-test-services/coding-test.service.ts +843 -0
  137. package/src/lib/services/candidate-coding-test-services/index.ts +4 -0
  138. package/src/lib/services/coding-test-config.service.ts +48 -0
  139. package/src/lib/services/configurations.service.mocks.ts +77 -0
  140. package/src/lib/services/configurations.service.spec.ts +79 -0
  141. package/src/lib/services/configurations.service.ts +111 -0
  142. package/src/lib/services/index.ts +0 -0
  143. package/src/lib/services/lib-coding-test.service.spec.ts +265 -0
  144. package/src/lib/services/lib-coding-test.service.ts +157 -0
  145. package/src/lib/services/local-storage.service.mocks.ts +22 -0
  146. package/src/lib/services/storage.service.spec.ts +1120 -0
  147. package/src/lib/services/storage.service.ts +729 -0
  148. package/src/lib/services/test-cases.service.spec.ts +53 -0
  149. package/src/lib/services/test-cases.service.ts +29 -0
  150. package/src/lib/services/theme.service.spec.ts +76 -0
  151. package/src/lib/services/theme.service.ts +34 -0
  152. package/src/lib/styles/mixins.scss +86 -0
  153. package/src/lib/styles/styles.scss +112 -0
  154. package/src/lib/styles/variables.scss +105 -0
  155. package/src/lib/utils/.gitkeep +0 -0
  156. package/src/lib/utils/additional-languages/erlang.ts +115 -0
  157. package/src/lib/utils/resize-element.ts +15 -0
  158. package/src/lib/utils/time-to-ms.util.ts +10 -0
  159. package/src/test-setup.ts +1 -0
  160. package/tsconfig.json +16 -0
  161. package/tsconfig.lib.json +12 -0
  162. package/tsconfig.lib.prod.json +9 -0
  163. package/tsconfig.spec.json +13 -0
@@ -0,0 +1,575 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import {
3
+ Component,
4
+ EventEmitter,
5
+ HostBinding,
6
+ Input,
7
+ OnDestroy,
8
+ OnInit,
9
+ Output,
10
+ signal,
11
+ ViewEncapsulation,
12
+ computed,
13
+ inject,
14
+ } from '@angular/core';
15
+ import { ActivatedRoute, RouterModule } from '@angular/router';
16
+ import { translate, TRANSLOCO_SCOPE, TranslocoModule, TranslocoService } from '@ngneat/transloco';
17
+ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
18
+ import {
19
+ ApplicationTheme,
20
+ ConfirmDialogComponent,
21
+ DialogComponentModule,
22
+ DialogService,
23
+ SnackbarComponentModule,
24
+ } from '@testgorilla/tgo-ui';
25
+ import {
26
+ ApiService,
27
+ Assessment,
28
+ EnvironmentService,
29
+ getAvailableLangs,
30
+ IAntiCheatingState,
31
+ IConfigurationState,
32
+ INavigationButtonState,
33
+ IQuestionDataContract,
34
+ ISubmissionState,
35
+ MixpanelService,
36
+ Question,
37
+ TestResultRead,
38
+ ThemeService,
39
+ TranslocoLazyModuleUtils,
40
+ } from '@testgorilla/tgo-test-shared';
41
+ import { GuidedTourModule, GuidedTourService } from 'ngx-guided-tour';
42
+ import { interval, Observable, Subscription } from 'rxjs';
43
+ import { switchMap, take, tap } from 'rxjs/operators';
44
+
45
+ import { CodingTestsResultsModel } from '../../models/coderunner-execution-results';
46
+ import { PasteData } from '../../models/paste-data';
47
+ import {
48
+ ProgrammingLanguage,
49
+ ProgrammingLanguageVersioned,
50
+ } from '../../models/programming-language';
51
+ import { ExampleTestCase, SqlTestCase } from '../../models/test-cases';
52
+ import { ROOT_TRANSLATIONS_SCOPE } from '../../models/translations';
53
+ import {
54
+ CodingTestTourMode,
55
+ CodingTestTourService,
56
+ CodingTestService,
57
+ CandidatureApiService,
58
+ CoderunnerApiService,
59
+ } from '../../services/candidate-coding-test-services';
60
+ import { CodeEditorLanguages } from '../code-editor/helpers/code-editor-helper.model';
61
+ import { Modes } from '../../models/mode';
62
+ import { Themes } from '../../models/theme';
63
+ import { LATLanguages } from '../../models/lat-languages';
64
+ import { ViewMode } from '../../models/view-mode';
65
+ import { TgoCodingTestComponent } from '../tgo-coding-test/tgo-coding-test.component';
66
+ import { LibCodingTestService } from '../../services/lib-coding-test.service';
67
+ import { CodingTestConfigService } from '../../services/coding-test-config.service';
68
+
69
+ @UntilDestroy()
70
+ @Component({
71
+ standalone: true,
72
+ selector: 'tgo-coding-test-candidate-view',
73
+ templateUrl: './tgo-coding-test-candidate-view.component.html',
74
+ styleUrls: ['./tgo-coding-test-candidate-view.component.scss'],
75
+ encapsulation: ViewEncapsulation.None,
76
+ imports: [
77
+ TranslocoModule,
78
+ DialogComponentModule,
79
+ SnackbarComponentModule,
80
+ TgoCodingTestComponent,
81
+ GuidedTourModule,
82
+ CommonModule,
83
+ RouterModule,
84
+ ],
85
+ providers: [
86
+ TranslocoLazyModuleUtils.getScopeProvider(
87
+ 'tgo-coding-test',
88
+ getAvailableLangs(),
89
+ ROOT_TRANSLATIONS_SCOPE,
90
+ (lang: string) => import(`../../../assets/i18n/${lang}.json`)
91
+ ),
92
+ EnvironmentService,
93
+ CoderunnerApiService,
94
+ CodingTestService,
95
+ CodingTestTourService,
96
+ GuidedTourService,
97
+ ThemeService,
98
+ CandidatureApiService,
99
+ MixpanelService,
100
+
101
+ // use MockedApiService for local demo only
102
+ // { provide: ApiService, useClass: MockedApiService },
103
+ ApiService,
104
+ ],
105
+ })
106
+ export class TgoCodingTestCandidateViewComponent implements OnInit, IQuestionDataContract, OnDestroy {
107
+ private readonly dialogService = inject(DialogService);
108
+ private readonly translationScope = inject(TRANSLOCO_SCOPE);
109
+ private readonly candidatureApiService = inject(CandidatureApiService);
110
+ private readonly codingTestTourService = inject(CodingTestTourService);
111
+ private readonly libCodingTestService = inject(LibCodingTestService);
112
+ private readonly environmentService = inject(EnvironmentService);
113
+ private readonly codingTestService = inject(CodingTestService);
114
+ private readonly translocoService = inject(TranslocoService);
115
+ private readonly themeService = inject(ThemeService);
116
+ private readonly apiService = inject(ApiService);
117
+ private readonly route = inject(ActivatedRoute);
118
+ private readonly codingTestConfigService = inject(CodingTestConfigService);
119
+
120
+ @Input() assessment?: Assessment;
121
+ @Input({ required: true }) question!: Question;
122
+ @Input({ required: true }) test!: TestResultRead;
123
+ @Input() expirationObservable?: Observable<void>;
124
+ @Input() completeObservable?: Observable<void>;
125
+ @Input() isDemo?: boolean;
126
+
127
+ @Output() validationStatusChanged = new EventEmitter<any>(); // eslint-disable-line @typescript-eslint/no-explicit-any
128
+ @Output() antiCheatingConfigurationChanged = new EventEmitter<IAntiCheatingState | null>();
129
+ @Output() fullscreenChanged = new EventEmitter<boolean>();
130
+ @Output() themeChanged = new EventEmitter<ApplicationTheme>();
131
+ @Output() codingFullscreenChanged = new EventEmitter<boolean>();
132
+ @Output() navigationButtonStateChanged = new EventEmitter<INavigationButtonState | null>();
133
+ @Output() configurationStateChanged = new EventEmitter<IConfigurationState | null>();
134
+ @Output() submissionStateChanged = new EventEmitter<ISubmissionState | null>();
135
+ @Output() loadingStateChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
136
+
137
+ @HostBinding('class') hostClass = 'micro-question-code';
138
+
139
+ private readonly subscription: Subscription = new Subscription();
140
+
141
+ initCode = '';
142
+ viewModes = ViewMode;
143
+ mode: Modes = Modes.Running;
144
+ companyColor = this.themeService.getCompanyColor();
145
+ initCodesMap = new Map<CodeEditorLanguages, string>();
146
+ languages = signal<LATLanguages>([]);
147
+ codingLibTranslations = signal<{ [key: string]: string }>({});
148
+ rawLanguages: ProgrammingLanguage[] = [];
149
+ currentLanguage?: string;
150
+ isSQL = false;
151
+ isLAT = true;
152
+ code = '';
153
+ codingTestResults?: CodingTestsResultsModel;
154
+ testResultsLoading = computed(() => this.codingTestService.isTestResultsLoading());
155
+ exampleTestCases: ExampleTestCase[] = [];
156
+ activeGuidedTourId$ = this.codingTestTourService.activeGuidedTourId$;
157
+ firstTestRunComplete = false;
158
+ canopyUiTheme: ApplicationTheme = 'light';
159
+
160
+ private wasTourShown = false;
161
+
162
+ ngOnDestroy(): void {
163
+ if (!this.test?.is_preview_mode) {
164
+ this.antiCheatingConfigurationChanged.emit({
165
+ fullscreenTracking: false,
166
+ mouseEventTracking: false,
167
+ snapshotsTracking: false,
168
+ });
169
+ }
170
+
171
+ this.subscription.unsubscribe();
172
+ }
173
+
174
+ ngOnInit() {
175
+ this.setupState();
176
+ this.handleGuidedTour();
177
+
178
+ if (!this.test?.is_preview_mode) {
179
+ this.antiCheatingConfigurationChanged.emit({
180
+ fullscreenTracking: true,
181
+ mouseEventTracking: true,
182
+ snapshotsTracking: true,
183
+ });
184
+
185
+ this.setupAutosaveEvent();
186
+ }
187
+
188
+ const isNextQuestionDisabled = !this.test?.is_preview_mode;
189
+
190
+ this.configurationStateChanged.emit({
191
+ isSavingQuestionDisabled: isNextQuestionDisabled,
192
+ });
193
+
194
+ this.navigationButtonStateChanged.emit({
195
+ disabled: isNextQuestionDisabled,
196
+ });
197
+
198
+ this.subscription.add(
199
+ this.expirationObservable?.subscribe(() => {
200
+ this.clearSavedCode();
201
+ if (!this.test?.is_preview_mode) {
202
+ this.clearLastLanguageSelected();
203
+ this.submitCode();
204
+ this.enableNextButton();
205
+ }
206
+ })
207
+ );
208
+
209
+ this.subscription.add(
210
+ this.completeObservable?.subscribe(() => {
211
+ if (!this.test?.is_preview_mode) {
212
+ this.openSaveDialog();
213
+ } else {
214
+ this.clearSavedCode();
215
+ }
216
+ })
217
+ );
218
+
219
+ if (!this.isSQL) {
220
+ this.loadAvailableLanguages();
221
+ this.setTestCases();
222
+ } else {
223
+ this.setSqlTestCases();
224
+ this.setSqlLang();
225
+ this.setSqlInitCode();
226
+ this.loadingStateChanged.emit(false);
227
+ }
228
+ this.setListeners();
229
+ }
230
+
231
+ openSaveDialog(): void {
232
+ const dialogRef = this.dialogService.open(ConfirmDialogComponent, {
233
+ applicationTheme: this.canopyUiTheme,
234
+ size: 'small',
235
+ panelClass: '',
236
+ extraData: {
237
+ title: translate('CODING_QUESTION.MODALS.FINISH_TEST.TITLE') ?? '',
238
+ message: translate('CODING_QUESTION.MODALS.FINISH_TEST.MESSAGE') ?? '',
239
+ confirmButtonText: translate('CODING_QUESTION.MODALS.FINISH_TEST.SUBMIT') ?? '',
240
+ cancelButtonText: translate('CODING_QUESTION.MODALS.FINISH_TEST.CANCEL') ?? '',
241
+ color: this.companyColor,
242
+ },
243
+ });
244
+
245
+ dialogRef.afterClosed().subscribe(result => {
246
+ if (!result) {
247
+ return;
248
+ }
249
+
250
+ this.clearSavedCode();
251
+ this.clearLastLanguageSelected();
252
+ this.submitCode();
253
+ this.enableNextButton();
254
+ });
255
+ }
256
+
257
+ private clearLastLanguageSelected(): void {
258
+ this.libCodingTestService.clearLastLanguageSelected();
259
+ }
260
+
261
+ private clearSavedCode() {
262
+ this.libCodingTestService.triggerClearSavedCode();
263
+ }
264
+
265
+ private submitCode() {
266
+ if (this.code) {
267
+ this.submissionStateChanged?.emit({
268
+ text: this.code,
269
+ language: this.getVersionedLanguage(),
270
+ });
271
+ }
272
+ }
273
+
274
+ private getVersionedLanguage(): string | undefined {
275
+ const language = this.currentLanguage;
276
+
277
+ if (!language) {
278
+ return;
279
+ }
280
+
281
+ if (this.isSQL) {
282
+ return ProgrammingLanguageVersioned.SQLite;
283
+ }
284
+
285
+ return `${language}-${this.libCodingTestService.getVersion()}`;
286
+ }
287
+
288
+ private enableNextButton(): void {
289
+ this.configurationStateChanged.emit({
290
+ isSavingQuestionDisabled: false,
291
+ });
292
+ }
293
+
294
+ languageChange(selectedLangName: CodeEditorLanguages) {
295
+ const selectedLang = this.rawLanguages.filter(
296
+ language => language.name === selectedLangName
297
+ )[0];
298
+ this.codingTestService.handleLanguageChangeModal(
299
+ this.question.id,
300
+ selectedLang,
301
+ this.canopyUiTheme,
302
+ this.companyColor
303
+ );
304
+ }
305
+
306
+ updateCode(code: string): void {
307
+ this.code = code;
308
+ }
309
+
310
+ runTest(): void {
311
+ this.codingTestService.runTest(
312
+ this.question.id,
313
+ this.code,
314
+ this.isLAT,
315
+ this.isSQL,
316
+ false,
317
+ !!this.test.is_preview_mode,
318
+ this.question.question_id,
319
+ this.test.id,
320
+ this.test
321
+ );
322
+
323
+ if (!this.firstTestRunComplete) {
324
+ this.navigationButtonStateChanged.emit({
325
+ disabled: false,
326
+ });
327
+
328
+ this.firstTestRunComplete = true;
329
+ }
330
+ }
331
+
332
+ screenshotPaste(event: PasteData): void {
333
+ if (event.type !== 'paste') {
334
+ return;
335
+ }
336
+
337
+ this.codingTestService
338
+ .sendCopypasteEvent$(this.code, event.data, this.question.id)
339
+ .pipe(take(1))
340
+ .subscribe();
341
+ }
342
+
343
+ private setupAutosaveEvent(): void {
344
+ this.subscription.add(
345
+ interval(5000)
346
+ .pipe(
347
+ untilDestroyed(this),
348
+ switchMap(() => this.codingTestService.sendAutosaveEvent$(this.code, this.question.id))
349
+ )
350
+ .subscribe()
351
+ );
352
+ }
353
+
354
+ private setupState(): void {
355
+ this.setApiParams();
356
+ this.setCodingLibTranslations();
357
+ this.setIsSQL();
358
+ this.setIsLat();
359
+ this.setMode();
360
+ }
361
+
362
+ private setCodingLibTranslations() {
363
+ // TRANSLOCO_SCOPE is passed here to make sure that translation scope is resolved by the library
364
+ this.translocoService
365
+ .selectTranslateObject(`CODING_LIB`, {}, this.translationScope as string)
366
+ .pipe(take(1))
367
+ .subscribe(translations => {
368
+ this.codingLibTranslations.set(translations);
369
+ });
370
+ }
371
+
372
+ private setApiParams(): void {
373
+ const invitationUuid = this.route.snapshot.firstChild?.url[0].path || '';
374
+ const contentLanguage = this.assessment?.locale ?? 'en';
375
+ const apiUrl = this.codingTestConfigService.getApiUrl();
376
+
377
+ this.apiService.setParams(invitationUuid, contentLanguage, ROOT_TRANSLATIONS_SCOPE, apiUrl);
378
+ }
379
+
380
+ private setIsSQL(): void {
381
+ this.isSQL = this.question?.context.code_language === 'sql';
382
+ this.codingTestService.setIsSQLTest(this.isSQL);
383
+ }
384
+
385
+ private setSqlInitCode(): void {
386
+ this.initCode = this.question?.context.initial_code;
387
+ }
388
+
389
+ private setSqlLang(): void {
390
+ const sqlLanguage = this.question?.context.code_language as CodeEditorLanguages;
391
+ this.languages.set([
392
+ {
393
+ label: 'nonLAT',
394
+ value: sqlLanguage,
395
+ },
396
+ ]);
397
+ this.currentLanguage = sqlLanguage;
398
+ }
399
+
400
+ private setIsLat(): void {
401
+ // todo: add response field if possible ?
402
+ // this.isLAT = this.test?.is_language_agnostic_test ?? false;
403
+ this.isLAT = !this.isSQL;
404
+ }
405
+
406
+ private setMode(): void {
407
+ this.mode = this.test?.is_preview_mode ? Modes.Preview : Modes.Running;
408
+ }
409
+
410
+ private listenGuidedTourStart(): void {
411
+ this.libCodingTestService.startGuidedTour$.pipe(untilDestroyed(this)).subscribe(() => {
412
+ const guidedTourMode = this.test?.is_preview_mode
413
+ ? CodingTestTourMode.PracticeQuestion
414
+ : CodingTestTourMode.RealQuestion;
415
+
416
+ this.codingTestTourService.init(guidedTourMode);
417
+ });
418
+ }
419
+
420
+ private handleGuidedTour() {
421
+ if (!this.environmentService.isMobile && !this.wasTourShown && this.test?.is_preview_mode) {
422
+ const delayForGuidedTour = 1500;
423
+ setTimeout(() => {
424
+ this.codingTestTourService.init(CodingTestTourMode.PracticeQuestion);
425
+ this.wasTourShown = true;
426
+ }, delayForGuidedTour);
427
+ }
428
+ }
429
+
430
+ private getAllLanguages(): ProgrammingLanguage[] {
431
+ const boilerplates = this.question?.context.boilerplates ?? [];
432
+ return boilerplates.map(({ programming_language }) => programming_language);
433
+ }
434
+
435
+ private loadAvailableLanguages(): void {
436
+ this.candidatureApiService
437
+ .getAvailableLanguages$()
438
+ .pipe(
439
+ take(1),
440
+ tap(availableLangsIds => {
441
+ const languages = this.getAllLanguages();
442
+ const areAllLangsAvailable = availableLangsIds.length === 0;
443
+
444
+ this.rawLanguages = areAllLangsAvailable
445
+ ? languages
446
+ : languages.filter(({ id }) => id !== undefined && availableLangsIds.includes(id));
447
+ this.languages.set(
448
+ this.rawLanguages.map(language => ({
449
+ label:
450
+ typeof language.name === 'string' && language.name.length > 0
451
+ ? language.name.charAt(0).toUpperCase() + language.name.slice(1)
452
+ : 'Unknown',
453
+ value: language.name as CodeEditorLanguages,
454
+ version: language.version,
455
+ }))
456
+ );
457
+ }),
458
+ tap(() => {
459
+ this.loadingStateChanged.emit(false);
460
+ }),
461
+ tap(() => {
462
+ const boilerplates = this.question?.context.boilerplates ?? [];
463
+ boilerplates.forEach(boilerplate => {
464
+ if (boilerplate.programming_language.name) {
465
+ this.initCodesMap.set(
466
+ boilerplate.programming_language.name as CodeEditorLanguages,
467
+ boilerplate.code
468
+ );
469
+ }
470
+ });
471
+ }),
472
+ switchMap(() => this.libCodingTestService.currentLanguage$),
473
+ tap((language: CodeEditorLanguages) => {
474
+ const currentLang = this.rawLanguages.find(x => x.name === language);
475
+ if (currentLang) {
476
+ this.codingTestService.setCurrentLanguageLAT(currentLang);
477
+ }
478
+ const initCode = this.initCodesMap.get(language);
479
+ if (initCode) {
480
+ this.initCode = initCode;
481
+ this.updateCode(this.initCode);
482
+ }
483
+ })
484
+ )
485
+ .subscribe(() => {
486
+ this.libCodingTestService.setBoilerplateLoadingStatus(false);
487
+ });
488
+ }
489
+
490
+ private listenThemeChange(): void {
491
+ this.libCodingTestService.themeChanged$.pipe(untilDestroyed(this)).subscribe(theme => {
492
+ this.canopyUiTheme = theme === Themes.Dark ? 'dark' : 'light';
493
+ this.themeChanged.emit(this.canopyUiTheme);
494
+ });
495
+ }
496
+
497
+ private listenFullscreenChange(): void {
498
+ this.libCodingTestService.isFullscreen$
499
+ .pipe(untilDestroyed(this))
500
+ .subscribe(isFullscreen => this.fullscreenChanged.emit(isFullscreen));
501
+ }
502
+
503
+ private listenDeleteCustomTestCases(): void {
504
+ this.codingTestService
505
+ .listenDeleteCustomTestCases()
506
+ .pipe(untilDestroyed(this))
507
+ .subscribe(({ index }) => {
508
+ this.codingTestService.handleDeleteTestCaseModal(
509
+ index,
510
+ this.canopyUiTheme,
511
+ this.companyColor
512
+ );
513
+ });
514
+ }
515
+
516
+ private listenLanguageChange(): void {
517
+ this.libCodingTestService.currentLanguage$
518
+ .pipe(untilDestroyed(this))
519
+ .subscribe((language: CodeEditorLanguages) => {
520
+ this.currentLanguage = language;
521
+ const code = this.initCodesMap.get(language);
522
+ if (code) {
523
+ this.initCode = code;
524
+ } else {
525
+ this.initCode = '';
526
+ console.warn(`No initial code found for language: ${language}`);
527
+ }
528
+ });
529
+ }
530
+
531
+ private listenResetCode(): void {
532
+ this.codingTestService
533
+ .listenCodingTestChanges()
534
+ .pipe(untilDestroyed(this))
535
+ .subscribe(() => {
536
+ this.codingTestService.handleResetCodeModal(this.canopyUiTheme, this.companyColor);
537
+ });
538
+ }
539
+
540
+ private listenTestResults(): void {
541
+ this.codingTestService.codingTestResults$.pipe(untilDestroyed(this)).subscribe(results => {
542
+ this.codingTestResults = results;
543
+ });
544
+ }
545
+
546
+ private setSqlTestCases(): void {
547
+ const testCases = this.question?.context?.sql_testcases.map(testCase => ({
548
+ ...testCase,
549
+ post_sql: testCase.post_sql || '',
550
+ initial_sql: this.question?.context?.initial_sql || '',
551
+ })) as SqlTestCase[];
552
+ this.codingTestService.setSqlTestCases(testCases);
553
+ this.exampleTestCases = testCases.map(this.codingTestService.transformToExampleSQLTestCase);
554
+ }
555
+
556
+ private setTestCases(): void {
557
+ this.exampleTestCases = this.question?.context?.testcases?.map(
558
+ this.codingTestService.transformToExampleLATtestCase
559
+ );
560
+ }
561
+
562
+ private setListeners(): void {
563
+ this.listenGuidedTourStart();
564
+ this.listenFullscreenChange();
565
+ this.listenThemeChange();
566
+ this.listenResetCode();
567
+ this.codingTestService.trackConfigChanged(!!this.test?.is_preview_mode);
568
+ this.listenTestResults();
569
+ if (this.isLAT) {
570
+ this.listenDeleteCustomTestCases();
571
+ this.listenLanguageChange();
572
+ }
573
+ }
574
+ }
575
+
@@ -0,0 +1,3 @@
1
+ export { TgoCodingTestConfig } from './tgo-coding-test.config';
2
+ export { provideTgoCodingTest } from './tgo-coding-test.provider';
3
+
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Configuration interface for the TGO Coding Test library.
3
+ *
4
+ * This interface defines the runtime configuration that must be provided
5
+ * by the consuming application. The library does not hold environment
6
+ * variables itself; instead, it receives them from the host application
7
+ * through dependency injection.
8
+ */
9
+ export interface TgoCodingTestConfig {
10
+ /**
11
+ * The base URL for the main API endpoint.
12
+ * This is required for the library to communicate with the main API service.
13
+ *
14
+ * Example: 'https://api-talent-staging.testgorilla.com/api/'
15
+ */
16
+ apiUrl: string;
17
+
18
+ /**
19
+ * The base URL for the CodeRunner V2 API endpoint.
20
+ * This is required for the library to communicate with the code execution service.
21
+ *
22
+ * Example: 'https://cr-v2-staging.testgorilla.com'
23
+ */
24
+ coderunnerV2Endpoint: string;
25
+ }
26
+
@@ -0,0 +1,38 @@
1
+ import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
2
+ import { TGO_CODING_TEST_CONFIG } from './tgo-coding-test.token';
3
+ import { TgoCodingTestConfig } from './tgo-coding-test.config';
4
+
5
+ /**
6
+ * Provides the TGO Coding Test library configuration.
7
+ *
8
+ * This function should be called in the consuming application's
9
+ * app.config.ts (for standalone apps) or module providers.
10
+ *
11
+ * @param config - The configuration object containing required settings
12
+ * @returns EnvironmentProviders that can be added to the application providers
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { provideTgoCodingTest } from '@testgorilla/tgo-coding-test';
17
+ * import { environment } from './environments/environment';
18
+ *
19
+ * export const appConfig: ApplicationConfig = {
20
+ * providers: [
21
+ * provideTgoCodingTest({
22
+ * coderunnerV2Endpoint: environment.coderunnerV2Endpoint,
23
+ * }),
24
+ * ],
25
+ * };
26
+ * ```
27
+ */
28
+ export function provideTgoCodingTest(
29
+ config: TgoCodingTestConfig
30
+ ): EnvironmentProviders {
31
+ return makeEnvironmentProviders([
32
+ {
33
+ provide: TGO_CODING_TEST_CONFIG,
34
+ useValue: config,
35
+ },
36
+ ]);
37
+ }
38
+
@@ -0,0 +1,21 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ import { TgoCodingTestConfig } from './tgo-coding-test.config';
3
+
4
+ /**
5
+ * Injection token for the TGO Coding Test library configuration.
6
+ *
7
+ * This token is used to inject the runtime configuration provided
8
+ * by the consuming application into library services.
9
+ */
10
+ export const TGO_CODING_TEST_CONFIG = new InjectionToken<TgoCodingTestConfig>(
11
+ 'TGO_CODING_TEST_CONFIG',
12
+ {
13
+ providedIn: 'root',
14
+ factory: () => {
15
+ throw new Error(
16
+ 'TGO_CODING_TEST_CONFIG must be provided. Use provideTgoCodingTest() in your app.config.ts or component providers.'
17
+ );
18
+ },
19
+ }
20
+ );
21
+
File without changes