@testgorilla/tgo-coding-test 1.0.0 → 2.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/shared/index.mjs +834 -0
- package/fesm2022/shared/index.mjs.map +1 -0
- package/fesm2022/testgorilla-tgo-coding-test.mjs +96 -96
- package/fesm2022/testgorilla-tgo-coding-test.mjs.map +1 -1
- package/lib/components/configurations/configurations.component.d.ts +1 -1
- package/package.json +15 -17
- package/esm2022/index.mjs +0 -12
- package/esm2022/lib/components/code-editor/code-editor.component.mjs +0 -335
- package/esm2022/lib/components/code-editor/code-editor.service.mjs +0 -72
- package/esm2022/lib/components/code-editor/helpers/c-helper.mjs +0 -40
- package/esm2022/lib/components/code-editor/helpers/code-editor-helper.base.mjs +0 -11
- package/esm2022/lib/components/code-editor/helpers/code-editor-helper.model.mjs +0 -2
- package/esm2022/lib/components/code-editor/helpers/cpp-helper.mjs +0 -41
- package/esm2022/lib/components/code-editor/helpers/csharp-helper.mjs +0 -42
- package/esm2022/lib/components/code-editor/helpers/go-helper.mjs +0 -42
- package/esm2022/lib/components/code-editor/helpers/index.mjs +0 -16
- package/esm2022/lib/components/code-editor/helpers/java-helper.mjs +0 -42
- package/esm2022/lib/components/code-editor/helpers/javascript-helper.mjs +0 -26
- package/esm2022/lib/components/code-editor/helpers/kotlin-helper.mjs +0 -42
- package/esm2022/lib/components/code-editor/helpers/php-helper.mjs +0 -26
- package/esm2022/lib/components/code-editor/helpers/python-helper.mjs +0 -26
- package/esm2022/lib/components/code-editor/helpers/r-helper.mjs +0 -26
- package/esm2022/lib/components/code-editor/helpers/ruby-helper.mjs +0 -26
- package/esm2022/lib/components/code-editor/helpers/scala-helper.mjs +0 -41
- package/esm2022/lib/components/code-editor/helpers/sql-helper.mjs +0 -34
- package/esm2022/lib/components/code-editor/helpers/swift-helper.mjs +0 -40
- package/esm2022/lib/components/code-editor/helpers/typescript-helper.mjs +0 -41
- package/esm2022/lib/components/code-editor/models/code-editor.model.mjs +0 -2
- package/esm2022/lib/components/code-editor/models/coding-snapshot.model.mjs +0 -2
- package/esm2022/lib/components/coding-question/coding-question.component.mjs +0 -126
- package/esm2022/lib/components/coding-section/coding-section.component.mjs +0 -188
- package/esm2022/lib/components/common/truncated-text/truncated-text.component.mjs +0 -38
- package/esm2022/lib/components/configurations/configurations.component.mjs +0 -97
- package/esm2022/lib/components/instructions/instructions.component.mjs +0 -139
- package/esm2022/lib/components/panel/panel.component.mjs +0 -34
- package/esm2022/lib/components/runnable-editor/runnable-editor.component.mjs +0 -169
- package/esm2022/lib/components/tests/test-cases/test-cases.component.mjs +0 -198
- package/esm2022/lib/components/tests/test-cases-content/test-cases-content.component.mjs +0 -96
- package/esm2022/lib/components/tests/test-cases-status/test-cases-status.component.mjs +0 -21
- package/esm2022/lib/components/tests/test-results.component.mjs +0 -127
- package/esm2022/lib/components/tgo-coding-test/tgo-coding-test.component.mjs +0 -280
- package/esm2022/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.mjs +0 -476
- package/esm2022/lib/config/index.mjs +0 -2
- package/esm2022/lib/config/tgo-coding-test.config.mjs +0 -2
- package/esm2022/lib/config/tgo-coding-test.provider.mjs +0 -34
- package/esm2022/lib/config/tgo-coding-test.token.mjs +0 -14
- package/esm2022/lib/models/auto-saved-data.mjs +0 -2
- package/esm2022/lib/models/code-event.mjs +0 -2
- package/esm2022/lib/models/coderunner-execution-results.mjs +0 -2
- package/esm2022/lib/models/configs.mjs +0 -2
- package/esm2022/lib/models/language-change-action.mjs +0 -2
- package/esm2022/lib/models/lat-languages.mjs +0 -3
- package/esm2022/lib/models/mixpanel-events.mjs +0 -2
- package/esm2022/lib/models/mode.mjs +0 -2
- package/esm2022/lib/models/paste-data.mjs +0 -2
- package/esm2022/lib/models/programming-language.mjs +0 -2
- package/esm2022/lib/models/test-cases.mjs +0 -7
- package/esm2022/lib/models/theme.mjs +0 -2
- package/esm2022/lib/models/translations.mjs +0 -2
- package/esm2022/lib/models/view-mode.mjs +0 -8
- package/esm2022/lib/pipes/memoize-func.pipe.mjs +0 -39
- package/esm2022/lib/services/candidate-coding-test-services/candidature-api.service.mjs +0 -19
- package/esm2022/lib/services/candidate-coding-test-services/coderunner-api.service.mjs +0 -58
- package/esm2022/lib/services/candidate-coding-test-services/coding-test-tour.service.mjs +0 -89
- package/esm2022/lib/services/candidate-coding-test-services/coding-test.service.mjs +0 -490
- package/esm2022/lib/services/candidate-coding-test-services/index.mjs +0 -5
- package/esm2022/lib/services/coding-test-config.service.mjs +0 -51
- package/esm2022/lib/services/configurations.service.mjs +0 -89
- package/esm2022/lib/services/lib-coding-test.service.mjs +0 -106
- package/esm2022/lib/services/storage.service.mjs +0 -624
- package/esm2022/lib/services/test-cases.service.mjs +0 -30
- package/esm2022/lib/services/theme.service.mjs +0 -36
- package/esm2022/lib/utils/additional-languages/erlang.mjs +0 -103
- package/esm2022/lib/utils/resize-element.mjs +0 -13
- package/esm2022/lib/utils/time-to-ms.util.mjs +0 -11
- package/esm2022/shared/index.mjs +0 -5
- package/esm2022/shared/lib/components/audio-animation/audio-animation.component.mjs +0 -114
- package/esm2022/shared/lib/components/audio-animation/index.mjs +0 -2
- package/esm2022/shared/lib/components/index.mjs +0 -3
- package/esm2022/shared/lib/components/vimeo-video/index.mjs +0 -2
- package/esm2022/shared/lib/components/vimeo-video/vimeo-video.component.mjs +0 -101
- package/esm2022/shared/lib/models/answer.mjs +0 -2
- package/esm2022/shared/lib/models/assessment.mjs +0 -2
- package/esm2022/shared/lib/models/environment.mjs +0 -2
- package/esm2022/shared/lib/models/index.mjs +0 -9
- package/esm2022/shared/lib/models/question-component.mjs +0 -2
- package/esm2022/shared/lib/models/question.mjs +0 -2
- package/esm2022/shared/lib/models/test.mjs +0 -2
- package/esm2022/shared/lib/models/translations.mjs +0 -2
- package/esm2022/shared/lib/models/window.mjs +0 -2
- package/esm2022/shared/lib/services/api/api.service.mjs +0 -97
- package/esm2022/shared/lib/services/api/mocked-api.service.mjs +0 -131
- package/esm2022/shared/lib/services/environment/environment.service.mjs +0 -13
- package/esm2022/shared/lib/services/index.mjs +0 -10
- package/esm2022/shared/lib/services/localization/languages.model.mjs +0 -19
- package/esm2022/shared/lib/services/localization/transloco-lazy-module-utils.mjs +0 -27
- package/esm2022/shared/lib/services/localization/transloco-testing.module.mjs +0 -11
- package/esm2022/shared/lib/services/media/media.service.mjs +0 -129
- package/esm2022/shared/lib/services/mixpanel/mixpanel.service.mjs +0 -30
- package/esm2022/shared/lib/services/theme/theme.service.mjs +0 -24
- package/esm2022/shared/test-mocks/assessment-test.mock.mjs +0 -112
- package/esm2022/shared/test-mocks/index.mjs +0 -3
- package/esm2022/shared/test-mocks/tgo-ui.mock.mjs +0 -39
- package/esm2022/testgorilla-tgo-coding-test.mjs +0 -5
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { signal, computed, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, EventEmitter, inject, ElementRef, Output, HostBinding, Injectable, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import Player from '@vimeo/player';
|
|
6
|
+
import 'mixpanel-browser';
|
|
7
|
+
import { Observable, throwError, EMPTY, of, delay } from 'rxjs';
|
|
8
|
+
import { TRANSLOCO_SCOPE, TranslocoTestingModule, translate } from '@ngneat/transloco';
|
|
9
|
+
import * as i1$1 from '@angular/common/http';
|
|
10
|
+
import * as i2 from '@testgorilla/tgo-ui';
|
|
11
|
+
import { catchError } from 'rxjs/operators';
|
|
12
|
+
|
|
13
|
+
class AudioAnimationComponent {
|
|
14
|
+
volume = 0;
|
|
15
|
+
fakeData = false;
|
|
16
|
+
isAnimationRunning = false;
|
|
17
|
+
animationFrameId = null;
|
|
18
|
+
lastAnimationUpdate = performance.now();
|
|
19
|
+
volumeSignal = signal(this.volume);
|
|
20
|
+
barHeightsSignal = computed(() => this.getBarHeights(this.volumeSignal()));
|
|
21
|
+
constructor() {
|
|
22
|
+
this.startAnimation();
|
|
23
|
+
}
|
|
24
|
+
ngOnChanges() {
|
|
25
|
+
this.volumeSignal.set(this.normalizeRealVolume(this.volume));
|
|
26
|
+
if (this.fakeData) {
|
|
27
|
+
this.startAnimation();
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.stopAnimation();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
normalizeRealVolume(originalVolume) {
|
|
34
|
+
return Math.min(1, originalVolume * 10);
|
|
35
|
+
}
|
|
36
|
+
startAnimation() {
|
|
37
|
+
if (this.isAnimationRunning) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.isAnimationRunning = true;
|
|
41
|
+
const animate = (timestamp) => {
|
|
42
|
+
if (!this.isAnimationRunning) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (timestamp - this.lastAnimationUpdate > 200) {
|
|
46
|
+
this.lastAnimationUpdate = timestamp;
|
|
47
|
+
const newVolume = Math.random() * 0.8 + 0.2;
|
|
48
|
+
this.volumeSignal.set(newVolume);
|
|
49
|
+
}
|
|
50
|
+
this.animationFrameId = requestAnimationFrame(animate);
|
|
51
|
+
};
|
|
52
|
+
this.animationFrameId = requestAnimationFrame(animate);
|
|
53
|
+
}
|
|
54
|
+
stopAnimation() {
|
|
55
|
+
this.isAnimationRunning = false;
|
|
56
|
+
if (this.animationFrameId) {
|
|
57
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
58
|
+
this.animationFrameId = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getBarHeights(volume) {
|
|
62
|
+
if (!this.fakeData && volume < 0.15) {
|
|
63
|
+
return {
|
|
64
|
+
'first-bar': `42%`,
|
|
65
|
+
'second-bar': `42%`,
|
|
66
|
+
'third-bar': `42%`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
let range;
|
|
70
|
+
this.fakeData
|
|
71
|
+
? (range = this.getFakeVolumeRange(volume))
|
|
72
|
+
: (range = this.getRealVolumeRange(volume));
|
|
73
|
+
const randomHeight = this.getRandomHeight(range.min, range.max);
|
|
74
|
+
return {
|
|
75
|
+
'first-bar': `${randomHeight}%`,
|
|
76
|
+
'second-bar': `${randomHeight + 30}%`,
|
|
77
|
+
'third-bar': `${randomHeight}%`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
getFakeVolumeRange(volume) {
|
|
81
|
+
if (volume < 0.33) {
|
|
82
|
+
return { min: 60, max: 90 };
|
|
83
|
+
}
|
|
84
|
+
else if (volume < 0.66) {
|
|
85
|
+
return { min: 90, max: 120 };
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
return { min: 120, max: 140 };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
getRealVolumeRange(volume) {
|
|
92
|
+
if (volume < 0.45) {
|
|
93
|
+
return { min: 90, max: 90 };
|
|
94
|
+
}
|
|
95
|
+
else if (volume < 0.6) {
|
|
96
|
+
return { min: 110, max: 110 };
|
|
97
|
+
}
|
|
98
|
+
else if (volume < 0.75) {
|
|
99
|
+
return { min: 125, max: 125 };
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return { min: 140, max: 140 };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
getRandomHeight(min, max) {
|
|
106
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
107
|
+
}
|
|
108
|
+
ngOnDestroy() {
|
|
109
|
+
this.stopAnimation();
|
|
110
|
+
}
|
|
111
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AudioAnimationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
112
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: AudioAnimationComponent, isStandalone: true, selector: "tgo-audio-animation", inputs: { volume: "volume", fakeData: "fakeData" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"audio-animation-wrapper\">\n <section class=\"audio-animation-container\">\n <div\n class=\"sound-bar first-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['first-bar'] }\"\n ></div>\n <div\n class=\"sound-bar second-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['second-bar'] }\"\n ></div>\n <div\n class=\"sound-bar third-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['third-bar'] }\"\n ></div>\n </section>\n</div>\n\n", styles: [".audio-animation-wrapper{display:flex;justify-content:center;align-items:center}.audio-animation-wrapper .audio-animation-container{display:flex;justify-content:space-between;align-items:center;width:32px;height:24px;background-color:#4285f4;border-radius:25px;padding:5px 6.5px;box-shadow:0 4px 10px #0000001a}.audio-animation-wrapper .audio-animation-container .sound-bar{width:4px;background-color:#fff;height:20%;transition:height .2s ease-in-out;clip-path:ellipse(45% 30% at 50% 50%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
113
|
+
}
|
|
114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: AudioAnimationComponent, decorators: [{
|
|
115
|
+
type: Component,
|
|
116
|
+
args: [{ selector: 'tgo-audio-animation', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CommonModule], template: "<div class=\"audio-animation-wrapper\">\n <section class=\"audio-animation-container\">\n <div\n class=\"sound-bar first-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['first-bar'] }\"\n ></div>\n <div\n class=\"sound-bar second-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['second-bar'] }\"\n ></div>\n <div\n class=\"sound-bar third-bar\"\n [ngStyle]=\"{ height: barHeightsSignal()['third-bar'] }\"\n ></div>\n </section>\n</div>\n\n", styles: [".audio-animation-wrapper{display:flex;justify-content:center;align-items:center}.audio-animation-wrapper .audio-animation-container{display:flex;justify-content:space-between;align-items:center;width:32px;height:24px;background-color:#4285f4;border-radius:25px;padding:5px 6.5px;box-shadow:0 4px 10px #0000001a}.audio-animation-wrapper .audio-animation-container .sound-bar{width:4px;background-color:#fff;height:20%;transition:height .2s ease-in-out;clip-path:ellipse(45% 30% at 50% 50%)}\n"] }]
|
|
117
|
+
}], ctorParameters: () => [], propDecorators: { volume: [{
|
|
118
|
+
type: Input
|
|
119
|
+
}], fakeData: [{
|
|
120
|
+
type: Input
|
|
121
|
+
}] } });
|
|
122
|
+
|
|
123
|
+
class VimeoVideoComponent {
|
|
124
|
+
class = 'tgo-vimeo-video';
|
|
125
|
+
videoUrl = '';
|
|
126
|
+
isPlaying = false;
|
|
127
|
+
options = {
|
|
128
|
+
controls: false,
|
|
129
|
+
autoplay: false,
|
|
130
|
+
loop: false,
|
|
131
|
+
speed: true,
|
|
132
|
+
title: false,
|
|
133
|
+
byline: false,
|
|
134
|
+
quality: 'auto',
|
|
135
|
+
autopause: false,
|
|
136
|
+
};
|
|
137
|
+
videoEnded = new EventEmitter();
|
|
138
|
+
videoStarted = new EventEmitter();
|
|
139
|
+
vimeoPlayer;
|
|
140
|
+
elRef = inject(ElementRef);
|
|
141
|
+
async ngOnInit() {
|
|
142
|
+
await this.configureVimeoPlayer();
|
|
143
|
+
}
|
|
144
|
+
async ngOnDestroy() {
|
|
145
|
+
await this.removeVimeoPlayer();
|
|
146
|
+
}
|
|
147
|
+
async ngOnChanges(simpleChanges) {
|
|
148
|
+
if (this.vimeoPlayer) {
|
|
149
|
+
if (simpleChanges['videoUrl'] && this.videoUrl) {
|
|
150
|
+
await this.vimeoPlayer.loadVideo(this.videoUrl);
|
|
151
|
+
await this.playVideo();
|
|
152
|
+
}
|
|
153
|
+
if (simpleChanges['isPlaying']) {
|
|
154
|
+
if (this.isPlaying) {
|
|
155
|
+
await this.vimeoPlayer.play();
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
await this.vimeoPlayer.pause();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async removeVimeoPlayer() {
|
|
164
|
+
if (this.vimeoPlayer) {
|
|
165
|
+
await this.vimeoPlayer.destroy();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async configureVimeoPlayer() {
|
|
169
|
+
const options = {
|
|
170
|
+
...this.options,
|
|
171
|
+
url: this.videoUrl,
|
|
172
|
+
};
|
|
173
|
+
try {
|
|
174
|
+
await this.removeVimeoPlayer();
|
|
175
|
+
this.vimeoPlayer = new Player(this.elRef.nativeElement, options);
|
|
176
|
+
this.vimeoPlayer.on('ended', () => {
|
|
177
|
+
this.videoEnded.emit();
|
|
178
|
+
this.isPlaying = false;
|
|
179
|
+
this.vimeoPlayer?.pause();
|
|
180
|
+
});
|
|
181
|
+
this.vimeoPlayer.on('playing', () => {
|
|
182
|
+
this.videoStarted.emit();
|
|
183
|
+
});
|
|
184
|
+
this.vimeoPlayer.on('pause', (event) => {
|
|
185
|
+
if (this.isPlaying && event.percent < 1) {
|
|
186
|
+
this.vimeoPlayer?.play();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
await this.playVideo();
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
await this.removeVimeoPlayer();
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async playVideo() {
|
|
197
|
+
if (this.vimeoPlayer && this.isPlaying) {
|
|
198
|
+
await this.vimeoPlayer.play();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: VimeoVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
202
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: VimeoVideoComponent, isStandalone: true, selector: "tgo-vimeo-video", inputs: { videoUrl: "videoUrl", isPlaying: "isPlaying", options: "options" }, outputs: { videoEnded: "videoEnded", videoStarted: "videoStarted" }, host: { properties: { "class": "this.class" } }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, styles: [".tgo-vimeo-video iframe{position:absolute;top:0;left:0;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
203
|
+
}
|
|
204
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: VimeoVideoComponent, decorators: [{
|
|
205
|
+
type: Component,
|
|
206
|
+
args: [{ selector: 'tgo-vimeo-video', template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, styles: [".tgo-vimeo-video iframe{position:absolute;top:0;left:0;width:100%;height:100%}\n"] }]
|
|
207
|
+
}], propDecorators: { class: [{
|
|
208
|
+
type: HostBinding
|
|
209
|
+
}], videoUrl: [{
|
|
210
|
+
type: Input
|
|
211
|
+
}], isPlaying: [{
|
|
212
|
+
type: Input
|
|
213
|
+
}], options: [{
|
|
214
|
+
type: Input
|
|
215
|
+
}], videoEnded: [{
|
|
216
|
+
type: Output
|
|
217
|
+
}], videoStarted: [{
|
|
218
|
+
type: Output
|
|
219
|
+
}] } });
|
|
220
|
+
|
|
221
|
+
const ROOT_TRANSLATIONS_SCOPE = 'TEST';
|
|
222
|
+
|
|
223
|
+
class MediaService {
|
|
224
|
+
mediaRecorder = null;
|
|
225
|
+
selectedMediaDevices;
|
|
226
|
+
isRecording = signal(false);
|
|
227
|
+
stopRecording() {
|
|
228
|
+
this.mediaRecorder?.stop();
|
|
229
|
+
}
|
|
230
|
+
getMediaStream(data) {
|
|
231
|
+
return navigator.mediaDevices.getUserMedia(this.getMediaStreamConstraints(data));
|
|
232
|
+
}
|
|
233
|
+
recordAudio() {
|
|
234
|
+
let checkPermissionPromise;
|
|
235
|
+
return new Observable((observer) => {
|
|
236
|
+
void this.getMediaStream({ video: false, audio: true })
|
|
237
|
+
.then((stream) => {
|
|
238
|
+
this.mediaRecorder = new MediaRecorder(stream);
|
|
239
|
+
this.mediaRecorder.start();
|
|
240
|
+
this.isRecording.set(true);
|
|
241
|
+
const audioChunks = [];
|
|
242
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
243
|
+
audioChunks.push(event.data);
|
|
244
|
+
};
|
|
245
|
+
this.mediaRecorder.addEventListener('stop', () => {
|
|
246
|
+
const audioBlob = new Blob(audioChunks, {
|
|
247
|
+
type: 'audio/mp3',
|
|
248
|
+
});
|
|
249
|
+
observer.next({ type: 'complete', file: audioBlob });
|
|
250
|
+
this.isRecording.set(false);
|
|
251
|
+
observer.complete();
|
|
252
|
+
});
|
|
253
|
+
this.mediaRecorder.onerror = (error) => {
|
|
254
|
+
observer.error(error);
|
|
255
|
+
observer.complete();
|
|
256
|
+
};
|
|
257
|
+
const audioContext = new AudioContext();
|
|
258
|
+
const mediaStreamAudioSourceNode = audioContext.createMediaStreamSource(stream);
|
|
259
|
+
const analyserNode = audioContext.createAnalyser();
|
|
260
|
+
mediaStreamAudioSourceNode.connect(analyserNode);
|
|
261
|
+
const pcmData = new Float32Array(analyserNode.fftSize);
|
|
262
|
+
const onFrame = () => {
|
|
263
|
+
analyserNode.getFloatTimeDomainData(pcmData);
|
|
264
|
+
let sumSquares = 0.0;
|
|
265
|
+
for (const amplitude of pcmData) {
|
|
266
|
+
sumSquares += amplitude * amplitude;
|
|
267
|
+
}
|
|
268
|
+
if (sumSquares > 0) {
|
|
269
|
+
observer.next({
|
|
270
|
+
type: 'volume',
|
|
271
|
+
value: Math.sqrt(sumSquares / pcmData.length),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
if (!checkPermissionPromise) {
|
|
276
|
+
checkPermissionPromise = this.checkPermission({
|
|
277
|
+
video: false,
|
|
278
|
+
audio: true,
|
|
279
|
+
});
|
|
280
|
+
checkPermissionPromise
|
|
281
|
+
.then((hasPermission) => {
|
|
282
|
+
if (!hasPermission) {
|
|
283
|
+
observer.error(new Error('Permission denied'));
|
|
284
|
+
observer.complete();
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
.finally(() => {
|
|
288
|
+
checkPermissionPromise = null;
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!observer.closed) {
|
|
293
|
+
window.requestAnimationFrame(onFrame);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
window.requestAnimationFrame(onFrame);
|
|
297
|
+
})
|
|
298
|
+
.catch((error) => {
|
|
299
|
+
observer.error(error);
|
|
300
|
+
observer.complete();
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
async playAudio(url) {
|
|
305
|
+
const audio = new Audio(url);
|
|
306
|
+
await audio.play();
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
audio.onended = () => {
|
|
309
|
+
resolve();
|
|
310
|
+
};
|
|
311
|
+
audio.onerror = (event) => reject(event);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
setSelectedMediaDevices(data) {
|
|
315
|
+
this.selectedMediaDevices = data;
|
|
316
|
+
}
|
|
317
|
+
async checkPermission(data) {
|
|
318
|
+
try {
|
|
319
|
+
await this.getMediaStream(data);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
console.error(err);
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
getMediaStreamConstraints(data) {
|
|
328
|
+
const audioDeviceID = this.selectedMediaDevices?.audioDeviceId;
|
|
329
|
+
const videoDeviceID = this.selectedMediaDevices?.videoDeviceId;
|
|
330
|
+
return {
|
|
331
|
+
audio: data.audio && audioDeviceID
|
|
332
|
+
? { deviceId: { exact: audioDeviceID } }
|
|
333
|
+
: data.audio,
|
|
334
|
+
video: data.video && videoDeviceID
|
|
335
|
+
? { deviceId: { exact: videoDeviceID } }
|
|
336
|
+
: data.video,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MediaService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
340
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MediaService, providedIn: 'root' });
|
|
341
|
+
}
|
|
342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MediaService, decorators: [{
|
|
343
|
+
type: Injectable,
|
|
344
|
+
args: [{
|
|
345
|
+
providedIn: 'root',
|
|
346
|
+
}]
|
|
347
|
+
}] });
|
|
348
|
+
|
|
349
|
+
var THEME_VARS;
|
|
350
|
+
(function (THEME_VARS) {
|
|
351
|
+
THEME_VARS["COMPANY_COLOR"] = "--color-primary-50";
|
|
352
|
+
})(THEME_VARS || (THEME_VARS = {}));
|
|
353
|
+
class ThemeService {
|
|
354
|
+
BRAND_PINK_COLOR = '#D410AA';
|
|
355
|
+
getCompanyColor() {
|
|
356
|
+
return (this.getCSSVariableValue(THEME_VARS.COMPANY_COLOR) ||
|
|
357
|
+
this.BRAND_PINK_COLOR);
|
|
358
|
+
}
|
|
359
|
+
getCSSVariableValue(variableName) {
|
|
360
|
+
return getComputedStyle(document.documentElement)
|
|
361
|
+
.getPropertyValue(variableName)
|
|
362
|
+
.trim();
|
|
363
|
+
}
|
|
364
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
365
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ThemeService });
|
|
366
|
+
}
|
|
367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ThemeService, decorators: [{
|
|
368
|
+
type: Injectable
|
|
369
|
+
}] });
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The util is based on this issue https://github.com/jsverse/transloco/issues/608#issuecomment-1416113493
|
|
373
|
+
*
|
|
374
|
+
* Transloco can't have several forRoot modules. so the only way for now to decouple translations as a separate file and context
|
|
375
|
+
* from the shell (TAS in this case) is to use TRANSLOCO_SCOPE
|
|
376
|
+
*/
|
|
377
|
+
class TranslocoLazyModuleUtils {
|
|
378
|
+
static createInlineLoader(languages, loaderFn) {
|
|
379
|
+
const translocoInlineLoader = {};
|
|
380
|
+
languages.forEach((language) => {
|
|
381
|
+
translocoInlineLoader[language] = () => loaderFn(language);
|
|
382
|
+
});
|
|
383
|
+
return translocoInlineLoader;
|
|
384
|
+
}
|
|
385
|
+
static getScopeProvider(scope, languages, alias, loaderFn) {
|
|
386
|
+
return {
|
|
387
|
+
provide: TRANSLOCO_SCOPE,
|
|
388
|
+
useValue: {
|
|
389
|
+
scope,
|
|
390
|
+
alias,
|
|
391
|
+
loader: this.createInlineLoader(languages, loaderFn),
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
//Languages available in the app as enum
|
|
398
|
+
var Language;
|
|
399
|
+
(function (Language) {
|
|
400
|
+
Language["en"] = "en";
|
|
401
|
+
Language["es"] = "es";
|
|
402
|
+
Language["nl"] = "nl";
|
|
403
|
+
Language["fr"] = "fr";
|
|
404
|
+
Language["de"] = "de";
|
|
405
|
+
Language["it-it"] = "it-it";
|
|
406
|
+
Language["ja-jp"] = "ja-jp";
|
|
407
|
+
Language["pt-br"] = "pt-br";
|
|
408
|
+
Language["da-dk"] = "da-dk";
|
|
409
|
+
Language["nb-no"] = "nb-no";
|
|
410
|
+
Language["pl-pl"] = "pl-pl";
|
|
411
|
+
Language["sv-se"] = "sv-se";
|
|
412
|
+
})(Language || (Language = {}));
|
|
413
|
+
//This function retrieves all languages from the enum. It is used to initialize transloco
|
|
414
|
+
const getAvailableLangs = () => Object.values(Language);
|
|
415
|
+
|
|
416
|
+
const getTranslocoModule = (options = {}) => TranslocoTestingModule.forRoot({
|
|
417
|
+
translocoConfig: {
|
|
418
|
+
reRenderOnLangChange: false,
|
|
419
|
+
availableLangs: ['en'],
|
|
420
|
+
defaultLang: 'en',
|
|
421
|
+
},
|
|
422
|
+
preloadLangs: true,
|
|
423
|
+
...options,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
class ApiService {
|
|
427
|
+
httpClient;
|
|
428
|
+
snackbarService;
|
|
429
|
+
invitationUUID = '';
|
|
430
|
+
contentLanguage = '';
|
|
431
|
+
ROOT_TRANSLATIONS_SCOPE = '';
|
|
432
|
+
baseUrl = '';
|
|
433
|
+
constructor(httpClient, snackbarService) {
|
|
434
|
+
this.httpClient = httpClient;
|
|
435
|
+
this.snackbarService = snackbarService;
|
|
436
|
+
}
|
|
437
|
+
setParams(invitationUUID, contentLanguage, translationScope, baseUrl) {
|
|
438
|
+
this.invitationUUID = invitationUUID;
|
|
439
|
+
this.contentLanguage = contentLanguage;
|
|
440
|
+
this.ROOT_TRANSLATIONS_SCOPE = translationScope;
|
|
441
|
+
this.baseUrl = baseUrl;
|
|
442
|
+
}
|
|
443
|
+
getInvitationUUID() {
|
|
444
|
+
return this.invitationUUID;
|
|
445
|
+
}
|
|
446
|
+
get(url, options = {}, errorCallback = this.handleError.bind(this)) {
|
|
447
|
+
return this.httpClient
|
|
448
|
+
.get(`${this.baseUrl}${url}`, this.buildHeaders(options))
|
|
449
|
+
.pipe(catchError((error) => errorCallback(error)));
|
|
450
|
+
}
|
|
451
|
+
post(url, data, options = {}, errorCallback = this.handleError.bind(this), isStandaloneUrl = false) {
|
|
452
|
+
return this.httpClient
|
|
453
|
+
.post(isStandaloneUrl ? url : `${this.baseUrl}${url}`, data, this.buildHeaders(options))
|
|
454
|
+
.pipe(catchError((error) => errorCallback(error)));
|
|
455
|
+
}
|
|
456
|
+
put(url, data, options = {}, errorCallback = this.handleError.bind(this)) {
|
|
457
|
+
return this.httpClient
|
|
458
|
+
.put(`${this.baseUrl}${url}`, data, this.buildHeaders(options))
|
|
459
|
+
.pipe(catchError((error) => errorCallback(error)));
|
|
460
|
+
}
|
|
461
|
+
patch(url, data, options = {}, errorCallback = this.handleError.bind(this)) {
|
|
462
|
+
return this.httpClient
|
|
463
|
+
.patch(`${this.baseUrl}${url}`, data, this.buildHeaders(options))
|
|
464
|
+
.pipe(catchError((error) => errorCallback(error)));
|
|
465
|
+
}
|
|
466
|
+
delete(url, errorCallback = this.handleError.bind(this)) {
|
|
467
|
+
return this.httpClient
|
|
468
|
+
.delete(`${this.baseUrl}${url}`, this.buildHeaders({}))
|
|
469
|
+
.pipe(catchError((error) => errorCallback(error)));
|
|
470
|
+
}
|
|
471
|
+
handleError(errorObj) {
|
|
472
|
+
const { error } = errorObj;
|
|
473
|
+
if (error?.errors?.length > 0) {
|
|
474
|
+
return throwError(() => error.errors);
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
console.error(errorObj.message);
|
|
478
|
+
this.snackbarService.error(this.processGenericServerError());
|
|
479
|
+
return EMPTY;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
processGenericServerError() {
|
|
483
|
+
return translate(`${this.ROOT_TRANSLATIONS_SCOPE}.ERRORS.SYSTEM_ERROR`);
|
|
484
|
+
}
|
|
485
|
+
buildHeaders(options) {
|
|
486
|
+
if (typeof window.crypto.randomUUID === 'function') {
|
|
487
|
+
options = {
|
|
488
|
+
...options,
|
|
489
|
+
'X-REQUEST-ID': window.crypto.randomUUID(),
|
|
490
|
+
'X-INVITATION-UUID': this.invitationUUID,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const passedAuthorizationHeader = options['Authorization'];
|
|
494
|
+
const headers = {
|
|
495
|
+
headers: {
|
|
496
|
+
...options,
|
|
497
|
+
Authorization: passedAuthorizationHeader
|
|
498
|
+
? passedAuthorizationHeader
|
|
499
|
+
: `TokenCandidature ${this.invitationUUID?.replace(/-/g, '')}`,
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
if (this.contentLanguage) {
|
|
503
|
+
headers.headers['Accept-Language'] = this.contentLanguage;
|
|
504
|
+
}
|
|
505
|
+
return headers;
|
|
506
|
+
}
|
|
507
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ApiService, deps: [{ token: i1$1.HttpClient }, { token: i2.SnackbarService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
508
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ApiService });
|
|
509
|
+
}
|
|
510
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ApiService, decorators: [{
|
|
511
|
+
type: Injectable
|
|
512
|
+
}], ctorParameters: () => [{ type: i1$1.HttpClient }, { type: i2.SnackbarService }] });
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* MockedApiService provides fake API responses for demo/development purposes.
|
|
516
|
+
* This service mimics the real ApiService interface but returns mock data instead
|
|
517
|
+
* of making actual HTTP calls.
|
|
518
|
+
*
|
|
519
|
+
* Usage: Provide this service in place of ApiService in your demo application:
|
|
520
|
+
* ```
|
|
521
|
+
* { provide: ApiService, useClass: MockedApiService }
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
class MockedApiService {
|
|
525
|
+
invitationUUID = 'demo-uuid-12345';
|
|
526
|
+
contentLanguage = 'en';
|
|
527
|
+
ROOT_TRANSLATIONS_SCOPE = '';
|
|
528
|
+
// Simulate network delay (in ms) - set to 0 for instant responses
|
|
529
|
+
MOCK_DELAY = 500;
|
|
530
|
+
setParams(invitationUUID, contentLanguage, translationScope, baseUrl) {
|
|
531
|
+
this.invitationUUID = invitationUUID;
|
|
532
|
+
this.contentLanguage = contentLanguage;
|
|
533
|
+
this.ROOT_TRANSLATIONS_SCOPE = translationScope;
|
|
534
|
+
console.log('[MockedApiService] Params set:', {
|
|
535
|
+
invitationUUID,
|
|
536
|
+
contentLanguage,
|
|
537
|
+
translationScope,
|
|
538
|
+
baseUrl,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
getInvitationUUID() {
|
|
542
|
+
return this.invitationUUID;
|
|
543
|
+
}
|
|
544
|
+
get(url, options = {}, errorCallback) {
|
|
545
|
+
console.log('[MockedApiService] GET:', url, options);
|
|
546
|
+
return of(this.getMockResponse(url, 'GET')).pipe(delay(this.MOCK_DELAY));
|
|
547
|
+
}
|
|
548
|
+
post(url, data, options = {}, errorCallback, isStandaloneUrl = false) {
|
|
549
|
+
console.log('[MockedApiService] POST:', url, data, options);
|
|
550
|
+
return of(this.getMockResponse(url, 'POST', data)).pipe(delay(this.MOCK_DELAY));
|
|
551
|
+
}
|
|
552
|
+
put(url, data, options = {}, errorCallback) {
|
|
553
|
+
console.log('[MockedApiService] PUT:', url, data, options);
|
|
554
|
+
return of(this.getMockResponse(url, 'PUT', data)).pipe(delay(this.MOCK_DELAY));
|
|
555
|
+
}
|
|
556
|
+
patch(url, data, options = {}, errorCallback) {
|
|
557
|
+
console.log('[MockedApiService] PATCH:', url, data, options);
|
|
558
|
+
return of(this.getMockResponse(url, 'PATCH', data)).pipe(delay(this.MOCK_DELAY));
|
|
559
|
+
}
|
|
560
|
+
delete(url, errorCallback) {
|
|
561
|
+
console.log('[MockedApiService] DELETE:', url);
|
|
562
|
+
return of({}).pipe(delay(this.MOCK_DELAY));
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Returns mock responses based on the URL pattern
|
|
566
|
+
*/
|
|
567
|
+
getMockResponse(url, method, data) {
|
|
568
|
+
// CodeRunner API - Run Tests
|
|
569
|
+
if (url.includes('/api/attempts/public/')) {
|
|
570
|
+
return this.getMockCoderunnerAttemptResponse(data);
|
|
571
|
+
}
|
|
572
|
+
if (url.includes('/api/attempts/preview/')) {
|
|
573
|
+
return this.getMockCoderunnerAttemptResponse(data);
|
|
574
|
+
}
|
|
575
|
+
if (url.includes('/api/attempts/')) {
|
|
576
|
+
return this.getMockCoderunnerAttemptResponse(data);
|
|
577
|
+
}
|
|
578
|
+
// Code Events
|
|
579
|
+
if (url.includes('/api/code-events/')) {
|
|
580
|
+
return {
|
|
581
|
+
id: Math.floor(Math.random() * 10000),
|
|
582
|
+
created_at: new Date().toISOString(),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
// Programming Languages
|
|
586
|
+
if (url.includes('programming-languages')) {
|
|
587
|
+
return []; // Mock all languages
|
|
588
|
+
}
|
|
589
|
+
// Default response
|
|
590
|
+
console.warn(`[MockedApiService] No mock defined for ${method} ${url}`);
|
|
591
|
+
return { success: true, data: null };
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Returns a mock coderunner attempt response with test results
|
|
595
|
+
*/
|
|
596
|
+
getMockCoderunnerAttemptResponse(data) {
|
|
597
|
+
const requestData = data;
|
|
598
|
+
const testcases = requestData?.testcases || [];
|
|
599
|
+
// Generate mock results for each test case
|
|
600
|
+
const results = testcases.map((testcase, index) => {
|
|
601
|
+
// Make first test pass, others random for variety
|
|
602
|
+
const passed = index === 0 ? true : Math.random() > 0.3;
|
|
603
|
+
return {
|
|
604
|
+
name: testcase.name || `Test Case ${index + 1}`,
|
|
605
|
+
passed,
|
|
606
|
+
expected: testcase.solution || testcase.expected_output || 'Expected output',
|
|
607
|
+
actual: passed
|
|
608
|
+
? testcase.solution || testcase.expected_output || 'Expected output'
|
|
609
|
+
: 'Different output',
|
|
610
|
+
input: testcase.input || '',
|
|
611
|
+
error: passed ? null : 'AssertionError: Output does not match expected',
|
|
612
|
+
execution_time: Math.random() * 100,
|
|
613
|
+
memory_used: Math.floor(Math.random() * 1000000),
|
|
614
|
+
points: passed ? (testcase.points || 10) : 0,
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
const totalPoints = results.reduce((sum, r) => sum + r.points, 0);
|
|
618
|
+
const maxPoints = testcases.reduce((sum, tc) => sum + (tc.points || 10), 0);
|
|
619
|
+
return {
|
|
620
|
+
id: Math.floor(Math.random() * 100000),
|
|
621
|
+
uuid: crypto.randomUUID(),
|
|
622
|
+
created_at: new Date().toISOString(),
|
|
623
|
+
language: requestData?.language || 'python-3.10.0',
|
|
624
|
+
code: requestData?.code || '',
|
|
625
|
+
status: 'completed',
|
|
626
|
+
results,
|
|
627
|
+
score: maxPoints > 0 ? (totalPoints / maxPoints) * 100 : 0,
|
|
628
|
+
total_points: totalPoints,
|
|
629
|
+
max_points: maxPoints,
|
|
630
|
+
execution_time: results.reduce((sum, r) => sum + r.execution_time, 0),
|
|
631
|
+
compilation_error: null,
|
|
632
|
+
runtime_error: null,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MockedApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
636
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MockedApiService });
|
|
637
|
+
}
|
|
638
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MockedApiService, decorators: [{
|
|
639
|
+
type: Injectable
|
|
640
|
+
}] });
|
|
641
|
+
|
|
642
|
+
class EnvironmentService {
|
|
643
|
+
get isMobile() {
|
|
644
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
645
|
+
}
|
|
646
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EnvironmentService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
647
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EnvironmentService });
|
|
648
|
+
}
|
|
649
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EnvironmentService, decorators: [{
|
|
650
|
+
type: Injectable
|
|
651
|
+
}] });
|
|
652
|
+
|
|
653
|
+
class MixpanelService {
|
|
654
|
+
mixpanel = null;
|
|
655
|
+
track(eventName, properties = {}) {
|
|
656
|
+
if (this.mixpanel) {
|
|
657
|
+
return this.sendTrackEvent(eventName, properties);
|
|
658
|
+
}
|
|
659
|
+
if (window.mixpanel) {
|
|
660
|
+
this.mixpanel = window.mixpanel;
|
|
661
|
+
return this.sendTrackEvent(eventName, properties);
|
|
662
|
+
}
|
|
663
|
+
console.error('Mixpanel is not initialized or not available');
|
|
664
|
+
}
|
|
665
|
+
sendTrackEvent(eventName, properties = {}) {
|
|
666
|
+
try {
|
|
667
|
+
this.mixpanel.track(eventName, properties);
|
|
668
|
+
}
|
|
669
|
+
catch (error) {
|
|
670
|
+
console.error(error);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MixpanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
674
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MixpanelService });
|
|
675
|
+
}
|
|
676
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: MixpanelService, decorators: [{
|
|
677
|
+
type: Injectable
|
|
678
|
+
}] });
|
|
679
|
+
|
|
680
|
+
const mockAssessment = {
|
|
681
|
+
uuid: 'fa67cd96-2a4c-490a-9a44-b11f759f228a',
|
|
682
|
+
locale: 'en',
|
|
683
|
+
is_snapshots_enabled: true,
|
|
684
|
+
has_video_question: false,
|
|
685
|
+
allow_non_native_speakers: true,
|
|
686
|
+
allow_other_conditions: true,
|
|
687
|
+
general_settings: {
|
|
688
|
+
id: 2157,
|
|
689
|
+
owner_profile_id: 271652,
|
|
690
|
+
company_name: 'x',
|
|
691
|
+
company_color: '#46a997',
|
|
692
|
+
company_logo: '',
|
|
693
|
+
company_redirect_url: null,
|
|
694
|
+
has_requested_demographic_details: true,
|
|
695
|
+
has_requested_feedback: true,
|
|
696
|
+
original_modified: '2024-01-16T20:30:04.054308Z',
|
|
697
|
+
},
|
|
698
|
+
sharing_results: true,
|
|
699
|
+
};
|
|
700
|
+
const mockQuestion = {
|
|
701
|
+
id: 747282,
|
|
702
|
+
text: '<p>Charlie was a flight attendant _______ seven years.</p>',
|
|
703
|
+
type: 'code',
|
|
704
|
+
context: {
|
|
705
|
+
intro_text: null,
|
|
706
|
+
answers: [
|
|
707
|
+
{
|
|
708
|
+
id: 578594,
|
|
709
|
+
text: '<p>during</p>',
|
|
710
|
+
answer_uuid: 'cf98b452-2704-44fa-adce-d5d93ad7c643',
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
id: 578596,
|
|
714
|
+
text: '<p>through</p>',
|
|
715
|
+
answer_uuid: '8e848dc4-a0cf-4c8d-b8d0-42b0fc85b19f',
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
id: 578595,
|
|
719
|
+
text: '<p>after</p>',
|
|
720
|
+
answer_uuid: '1e97f320-7d22-4dec-8555-1aa8228df131',
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
id: 578593,
|
|
724
|
+
text: '<p>for</p>',
|
|
725
|
+
answer_uuid: 'ccc648cc-435b-460a-89e8-432d13574e1e',
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
initial_code: null,
|
|
729
|
+
code_language: null,
|
|
730
|
+
typing_test_type: null,
|
|
731
|
+
typing_test_config: null,
|
|
732
|
+
typing_test_mode: {
|
|
733
|
+
numbers: false,
|
|
734
|
+
punctuation: false,
|
|
735
|
+
funbox: 'none',
|
|
736
|
+
sentences: false,
|
|
737
|
+
},
|
|
738
|
+
long_answer_list: false,
|
|
739
|
+
max_items: 100,
|
|
740
|
+
min_items: 0,
|
|
741
|
+
duration: null,
|
|
742
|
+
attempts: 0,
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
const mockTestResultRead = {
|
|
746
|
+
id: 123,
|
|
747
|
+
test: {
|
|
748
|
+
id: 1,
|
|
749
|
+
name: 'test-name',
|
|
750
|
+
duration: 100,
|
|
751
|
+
algorithm: 'testAlgorithm',
|
|
752
|
+
},
|
|
753
|
+
locale: 'en-EN',
|
|
754
|
+
order: 1,
|
|
755
|
+
is_preview_mode: true,
|
|
756
|
+
};
|
|
757
|
+
const mockSQLTestCases = [
|
|
758
|
+
{
|
|
759
|
+
category: 'Test Category 1',
|
|
760
|
+
id: 1,
|
|
761
|
+
initial_sql: 'Test Initial SQL',
|
|
762
|
+
name: 'Test Name 1',
|
|
763
|
+
post_sql: 'Test Post SQL 1',
|
|
764
|
+
pre_sql: 'Test Pre SQL 1',
|
|
765
|
+
result: 'Test Result 1',
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
category: 'Test Category 2',
|
|
769
|
+
id: 2,
|
|
770
|
+
initial_sql: 'Test Initial SQL',
|
|
771
|
+
name: 'Test Name 2',
|
|
772
|
+
post_sql: 'Test Post SQL 2',
|
|
773
|
+
pre_sql: 'Test Pre SQL 2',
|
|
774
|
+
result: 'Test Result 2',
|
|
775
|
+
},
|
|
776
|
+
];
|
|
777
|
+
const mockTestCases = [
|
|
778
|
+
{
|
|
779
|
+
name: 'Test Case 1',
|
|
780
|
+
input: 'Test Input 1',
|
|
781
|
+
solution: 'Test Solution 1',
|
|
782
|
+
points: 10,
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
name: 'Test Case 2',
|
|
786
|
+
input: 'Test Input 2',
|
|
787
|
+
solution: 'Test Solution 2',
|
|
788
|
+
points: 20,
|
|
789
|
+
},
|
|
790
|
+
];
|
|
791
|
+
|
|
792
|
+
// Mock modules
|
|
793
|
+
const ButtonComponentModule = NgModule({
|
|
794
|
+
imports: [CommonModule],
|
|
795
|
+
declarations: [],
|
|
796
|
+
exports: [],
|
|
797
|
+
})(class {
|
|
798
|
+
});
|
|
799
|
+
const IconComponentModule = NgModule({
|
|
800
|
+
imports: [CommonModule],
|
|
801
|
+
declarations: [],
|
|
802
|
+
exports: [],
|
|
803
|
+
})(class {
|
|
804
|
+
});
|
|
805
|
+
const CardComponentModule = NgModule({
|
|
806
|
+
imports: [CommonModule],
|
|
807
|
+
declarations: [],
|
|
808
|
+
exports: [],
|
|
809
|
+
})(class {
|
|
810
|
+
});
|
|
811
|
+
const DialogComponentModule = NgModule({
|
|
812
|
+
imports: [CommonModule],
|
|
813
|
+
declarations: [],
|
|
814
|
+
exports: [],
|
|
815
|
+
})(class {
|
|
816
|
+
});
|
|
817
|
+
// Mock DialogService
|
|
818
|
+
class DialogService {
|
|
819
|
+
open = jest.fn();
|
|
820
|
+
}
|
|
821
|
+
// Mock SnackbarService
|
|
822
|
+
class SnackbarService {
|
|
823
|
+
error = jest.fn();
|
|
824
|
+
}
|
|
825
|
+
// Export all other potential exports as empty objects
|
|
826
|
+
const QuillViewComponent = class {
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Generated bundle index. Do not edit.
|
|
831
|
+
*/
|
|
832
|
+
|
|
833
|
+
export { ApiService, AudioAnimationComponent, ButtonComponentModule, CardComponentModule, DialogComponentModule, DialogService, EnvironmentService, IconComponentModule, Language, MediaService, MixpanelService, MockedApiService, QuillViewComponent, ROOT_TRANSLATIONS_SCOPE, SnackbarService, ThemeService, TranslocoLazyModuleUtils, VimeoVideoComponent, getAvailableLangs, getTranslocoModule, mockAssessment, mockQuestion, mockSQLTestCases, mockTestCases, mockTestResultRead };
|
|
834
|
+
//# sourceMappingURL=tgo-coding-test-shared.mjs.map
|