@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.
- package/.eslintrc.json +45 -0
- package/README.md +257 -0
- package/jest.config.ts +21 -0
- package/ng-package.json +14 -0
- package/package.json +33 -0
- package/project.json +36 -0
- package/src/assets/i18n/en.json +124 -0
- package/src/index.ts +30 -0
- package/src/lib/components/.gitkeep +0 -0
- package/src/lib/components/code-editor/code-editor.component.html +10 -0
- package/src/lib/components/code-editor/code-editor.component.scss +21 -0
- package/src/lib/components/code-editor/code-editor.component.spec.ts +136 -0
- package/src/lib/components/code-editor/code-editor.component.ts +369 -0
- package/src/lib/components/code-editor/code-editor.mocks.ts +28 -0
- package/src/lib/components/code-editor/code-editor.service.spec.ts +160 -0
- package/src/lib/components/code-editor/code-editor.service.ts +94 -0
- package/src/lib/components/code-editor/helpers/c-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/c-helper.ts +51 -0
- package/src/lib/components/code-editor/helpers/code-editor-helper.base.spec.ts +30 -0
- package/src/lib/components/code-editor/helpers/code-editor-helper.base.ts +16 -0
- package/src/lib/components/code-editor/helpers/code-editor-helper.mocks.ts +24 -0
- package/src/lib/components/code-editor/helpers/code-editor-helper.model.ts +67 -0
- package/src/lib/components/code-editor/helpers/cpp-helper.spec.ts +40 -0
- package/src/lib/components/code-editor/helpers/cpp-helper.ts +52 -0
- package/src/lib/components/code-editor/helpers/csharp-helper.spec.ts +42 -0
- package/src/lib/components/code-editor/helpers/csharp-helper.ts +55 -0
- package/src/lib/components/code-editor/helpers/go-helper.spec.ts +41 -0
- package/src/lib/components/code-editor/helpers/go-helper.ts +54 -0
- package/src/lib/components/code-editor/helpers/index.ts +15 -0
- package/src/lib/components/code-editor/helpers/java-helper.spec.ts +41 -0
- package/src/lib/components/code-editor/helpers/java-helper.ts +54 -0
- package/src/lib/components/code-editor/helpers/javascript-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/javascript-helper.ts +32 -0
- package/src/lib/components/code-editor/helpers/kotlin-helper.spec.ts +41 -0
- package/src/lib/components/code-editor/helpers/kotlin-helper.ts +54 -0
- package/src/lib/components/code-editor/helpers/php-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/php-helper.ts +32 -0
- package/src/lib/components/code-editor/helpers/python-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/python-helper.ts +32 -0
- package/src/lib/components/code-editor/helpers/r-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/r-helper.ts +32 -0
- package/src/lib/components/code-editor/helpers/ruby-helper.spec.ts +39 -0
- package/src/lib/components/code-editor/helpers/ruby-helper.ts +32 -0
- package/src/lib/components/code-editor/helpers/scala-helper.spec.ts +41 -0
- package/src/lib/components/code-editor/helpers/scala-helper.ts +53 -0
- package/src/lib/components/code-editor/helpers/sql-helper.spec.ts +87 -0
- package/src/lib/components/code-editor/helpers/sql-helper.ts +44 -0
- package/src/lib/components/code-editor/helpers/swift-helper.spec.ts +40 -0
- package/src/lib/components/code-editor/helpers/swift-helper.ts +51 -0
- package/src/lib/components/code-editor/helpers/typescript-helper.spec.ts +40 -0
- package/src/lib/components/code-editor/helpers/typescript-helper.ts +52 -0
- package/src/lib/components/code-editor/models/code-editor.model.ts +9 -0
- package/src/lib/components/code-editor/models/coding-snapshot.model.ts +4 -0
- package/src/lib/components/coding-question/coding-question.component.html +78 -0
- package/src/lib/components/coding-question/coding-question.component.scss +76 -0
- package/src/lib/components/coding-question/coding-question.component.spec.ts +85 -0
- package/src/lib/components/coding-question/coding-question.component.ts +102 -0
- package/src/lib/components/coding-section/coding-section.component.html +82 -0
- package/src/lib/components/coding-section/coding-section.component.scss +64 -0
- package/src/lib/components/coding-section/coding-section.component.spec.ts +257 -0
- package/src/lib/components/coding-section/coding-section.component.ts +187 -0
- package/src/lib/components/coding-test.module.ts +124 -0
- package/src/lib/components/common/truncated-text/truncated-text.component.html +6 -0
- package/src/lib/components/common/truncated-text/truncated-text.component.scss +18 -0
- package/src/lib/components/common/truncated-text/truncated-text.component.spec.ts +84 -0
- package/src/lib/components/common/truncated-text/truncated-text.component.ts +37 -0
- package/src/lib/components/configurations/configurations.component.html +57 -0
- package/src/lib/components/configurations/configurations.component.scss +42 -0
- package/src/lib/components/configurations/configurations.component.spec.ts +186 -0
- package/src/lib/components/configurations/configurations.component.ts +98 -0
- package/src/lib/components/instructions/instructions.component.html +41 -0
- package/src/lib/components/instructions/instructions.component.scss +167 -0
- package/src/lib/components/instructions/instructions.component.spec.ts +106 -0
- package/src/lib/components/instructions/instructions.component.ts +138 -0
- package/src/lib/components/panel/panel.component.html +19 -0
- package/src/lib/components/panel/panel.component.scss +41 -0
- package/src/lib/components/panel/panel.component.spec.ts +40 -0
- package/src/lib/components/panel/panel.component.ts +34 -0
- package/src/lib/components/runnable-editor/runnable-editor.component.html +75 -0
- package/src/lib/components/runnable-editor/runnable-editor.component.scss +55 -0
- package/src/lib/components/runnable-editor/runnable-editor.component.spec.ts +124 -0
- package/src/lib/components/runnable-editor/runnable-editor.component.ts +155 -0
- package/src/lib/components/tests/test-cases/test-cases.component.html +135 -0
- package/src/lib/components/tests/test-cases/test-cases.component.scss +220 -0
- package/src/lib/components/tests/test-cases/test-cases.component.spec.ts +401 -0
- package/src/lib/components/tests/test-cases/test-cases.component.ts +205 -0
- package/src/lib/components/tests/test-cases-content/test-cases-content.component.html +94 -0
- package/src/lib/components/tests/test-cases-content/test-cases-content.component.scss +103 -0
- package/src/lib/components/tests/test-cases-content/test-cases-content.component.spec.ts +122 -0
- package/src/lib/components/tests/test-cases-content/test-cases-content.component.ts +102 -0
- package/src/lib/components/tests/test-cases-status/test-cases-status.component.html +16 -0
- package/src/lib/components/tests/test-cases-status/test-cases-status.component.scss +49 -0
- package/src/lib/components/tests/test-cases-status/test-cases-status.component.spec.ts +22 -0
- package/src/lib/components/tests/test-cases-status/test-cases-status.component.ts +18 -0
- package/src/lib/components/tests/test-results.component.html +119 -0
- package/src/lib/components/tests/test-results.component.scss +189 -0
- package/src/lib/components/tests/test-results.component.spec.ts +140 -0
- package/src/lib/components/tests/test-results.component.ts +98 -0
- package/src/lib/components/tgo-coding-test/tgo-coding-test.component.html +96 -0
- package/src/lib/components/tgo-coding-test/tgo-coding-test.component.scss +6 -0
- package/src/lib/components/tgo-coding-test/tgo-coding-test.component.spec.ts +599 -0
- package/src/lib/components/tgo-coding-test/tgo-coding-test.component.ts +279 -0
- package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.html +36 -0
- package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.scss +183 -0
- package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.spec.ts +883 -0
- package/src/lib/components/tgo-coding-test-candidate-view/tgo-coding-test-candidate-view.component.ts +575 -0
- package/src/lib/config/index.ts +3 -0
- package/src/lib/config/tgo-coding-test.config.ts +26 -0
- package/src/lib/config/tgo-coding-test.provider.ts +38 -0
- package/src/lib/config/tgo-coding-test.token.ts +21 -0
- package/src/lib/models/.gitkeep +0 -0
- package/src/lib/models/auto-saved-data.ts +51 -0
- package/src/lib/models/code-event.ts +17 -0
- package/src/lib/models/coderunner-execution-results.ts +58 -0
- package/src/lib/models/coding-lib.mocks.ts +246 -0
- package/src/lib/models/configs.ts +18 -0
- package/src/lib/models/language-change-action.ts +4 -0
- package/src/lib/models/lat-languages.ts +12 -0
- package/src/lib/models/mixpanel-events.ts +3 -0
- package/src/lib/models/mode.ts +5 -0
- package/src/lib/models/paste-data.ts +4 -0
- package/src/lib/models/programming-language.ts +9 -0
- package/src/lib/models/test-cases.ts +74 -0
- package/src/lib/models/theme.ts +5 -0
- package/src/lib/models/translations.ts +1 -0
- package/src/lib/models/view-mode.ts +6 -0
- package/src/lib/pipes/memoize-func.pipe.ts +34 -0
- package/src/lib/services/.gitkeep +0 -0
- package/src/lib/services/candidate-coding-test-services/candidature-api.service.spec.ts +40 -0
- package/src/lib/services/candidate-coding-test-services/candidature-api.service.ts +15 -0
- package/src/lib/services/candidate-coding-test-services/coderunner-api.service.spec.ts +134 -0
- package/src/lib/services/candidate-coding-test-services/coderunner-api.service.ts +105 -0
- package/src/lib/services/candidate-coding-test-services/coding-test-tour.service.spec.ts +161 -0
- package/src/lib/services/candidate-coding-test-services/coding-test-tour.service.ts +100 -0
- package/src/lib/services/candidate-coding-test-services/coding-test.service.spec.ts +1524 -0
- package/src/lib/services/candidate-coding-test-services/coding-test.service.ts +843 -0
- package/src/lib/services/candidate-coding-test-services/index.ts +4 -0
- package/src/lib/services/coding-test-config.service.ts +48 -0
- package/src/lib/services/configurations.service.mocks.ts +77 -0
- package/src/lib/services/configurations.service.spec.ts +79 -0
- package/src/lib/services/configurations.service.ts +111 -0
- package/src/lib/services/index.ts +0 -0
- package/src/lib/services/lib-coding-test.service.spec.ts +265 -0
- package/src/lib/services/lib-coding-test.service.ts +157 -0
- package/src/lib/services/local-storage.service.mocks.ts +22 -0
- package/src/lib/services/storage.service.spec.ts +1120 -0
- package/src/lib/services/storage.service.ts +729 -0
- package/src/lib/services/test-cases.service.spec.ts +53 -0
- package/src/lib/services/test-cases.service.ts +29 -0
- package/src/lib/services/theme.service.spec.ts +76 -0
- package/src/lib/services/theme.service.ts +34 -0
- package/src/lib/styles/mixins.scss +86 -0
- package/src/lib/styles/styles.scss +112 -0
- package/src/lib/styles/variables.scss +105 -0
- package/src/lib/utils/.gitkeep +0 -0
- package/src/lib/utils/additional-languages/erlang.ts +115 -0
- package/src/lib/utils/resize-element.ts +15 -0
- package/src/lib/utils/time-to-ms.util.ts +10 -0
- package/src/test-setup.ts +1 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +13 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { TestBed, waitForAsync } from '@angular/core/testing';
|
|
2
|
+
import { CoderunnerApiService } from './coderunner-api.service';
|
|
3
|
+
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|
4
|
+
import {
|
|
5
|
+
ApiService,
|
|
6
|
+
getTranslocoModule,
|
|
7
|
+
} from '@testgorilla/tgo-test-shared';
|
|
8
|
+
import { of } from 'rxjs';
|
|
9
|
+
import { SnackbarService } from '@testgorilla/tgo-ui';
|
|
10
|
+
import { CodingTestConfigService } from '../../services/coding-test-config.service';
|
|
11
|
+
|
|
12
|
+
// todo: add tests to check return type
|
|
13
|
+
describe('CoderunnerApiService', () => {
|
|
14
|
+
let service: CoderunnerApiService;
|
|
15
|
+
let apiService: ApiService;
|
|
16
|
+
const baseUrl = 'www.some-test-url.com';
|
|
17
|
+
const invitationUuid = '123-456-789';
|
|
18
|
+
const testResultId = 123789;
|
|
19
|
+
const questionId = 123;
|
|
20
|
+
const questionResultId = 789;
|
|
21
|
+
const code = 'lorem ipsum';
|
|
22
|
+
const language = 'python';
|
|
23
|
+
const testCases = [
|
|
24
|
+
{
|
|
25
|
+
input: '1 2 3',
|
|
26
|
+
solution: '3 4 5',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
const headers = {};
|
|
30
|
+
|
|
31
|
+
beforeEach(waitForAsync(() => {
|
|
32
|
+
TestBed.configureTestingModule({
|
|
33
|
+
providers: [
|
|
34
|
+
CoderunnerApiService,
|
|
35
|
+
{
|
|
36
|
+
provide: ApiService,
|
|
37
|
+
useValue: {
|
|
38
|
+
post: jest.fn().mockReturnValue(of({})),
|
|
39
|
+
getInvitationUUID: jest.fn().mockReturnValue(invitationUuid),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
provide: CodingTestConfigService,
|
|
44
|
+
useValue: {
|
|
45
|
+
getCoderunnerV2Endpoint: jest.fn().mockReturnValue(baseUrl),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{ provide: SnackbarService, useValue: { error: jest.fn() } },
|
|
49
|
+
],
|
|
50
|
+
imports: [getTranslocoModule()],
|
|
51
|
+
schemas: [NO_ERRORS_SCHEMA],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
service = TestBed.inject(CoderunnerApiService);
|
|
55
|
+
apiService = TestBed.inject(ApiService);
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
describe('when runPublicTest$ is called', () => {
|
|
59
|
+
const expectedUrl = `${baseUrl}/api/attempts/public/`;
|
|
60
|
+
|
|
61
|
+
it('should call api service with POST and proper params', () => {
|
|
62
|
+
const spy = jest.spyOn(apiService, 'post');
|
|
63
|
+
|
|
64
|
+
service.runPublicTest$(code, testCases, language);
|
|
65
|
+
|
|
66
|
+
expect(spy).toHaveBeenCalledWith(
|
|
67
|
+
expectedUrl,
|
|
68
|
+
{
|
|
69
|
+
question_id: 0,
|
|
70
|
+
code,
|
|
71
|
+
language,
|
|
72
|
+
testcases: testCases,
|
|
73
|
+
question_result_id: 0,
|
|
74
|
+
},
|
|
75
|
+
headers,
|
|
76
|
+
expect.any(Function),
|
|
77
|
+
true
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('when runPreviewTest$ is called', () => {
|
|
83
|
+
const expectedUrl = `${baseUrl}/api/attempts/preview/`;
|
|
84
|
+
|
|
85
|
+
it('should call api service with POST and proper params', () => {
|
|
86
|
+
const spy = jest.spyOn(apiService, 'post');
|
|
87
|
+
|
|
88
|
+
service.runPreviewTest$(testResultId, code, testCases, language);
|
|
89
|
+
|
|
90
|
+
expect(spy).toHaveBeenCalledWith(
|
|
91
|
+
expectedUrl,
|
|
92
|
+
{
|
|
93
|
+
code,
|
|
94
|
+
language,
|
|
95
|
+
testcases: testCases,
|
|
96
|
+
test_result_id: testResultId,
|
|
97
|
+
},
|
|
98
|
+
headers,
|
|
99
|
+
expect.any(Function),
|
|
100
|
+
true
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('when runNonPreviewTest$ is called', () => {
|
|
106
|
+
const expectedUrl = `${baseUrl}/api/attempts/`;
|
|
107
|
+
|
|
108
|
+
it('should call api service with POST and proper params', () => {
|
|
109
|
+
const spy = jest.spyOn(apiService, 'post');
|
|
110
|
+
|
|
111
|
+
service.runNonPreviewTest$(
|
|
112
|
+
questionId,
|
|
113
|
+
questionResultId,
|
|
114
|
+
code,
|
|
115
|
+
testCases,
|
|
116
|
+
language
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(spy).toHaveBeenCalledWith(
|
|
120
|
+
expectedUrl,
|
|
121
|
+
{
|
|
122
|
+
question_id: questionId,
|
|
123
|
+
question_result_id: questionResultId,
|
|
124
|
+
code,
|
|
125
|
+
language,
|
|
126
|
+
testcases: testCases,
|
|
127
|
+
},
|
|
128
|
+
headers,
|
|
129
|
+
expect.any(Function),
|
|
130
|
+
true
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { ApiService } from '@testgorilla/tgo-test-shared';
|
|
3
|
+
import { EMPTY, Observable, throwError } from 'rxjs';
|
|
4
|
+
import { LATFormattedTestCases, SqlTestCase } from '../../models/test-cases';
|
|
5
|
+
import { CoderunnerAttemptModel } from '../../models/coderunner-execution-results';
|
|
6
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
7
|
+
import { SnackbarService } from '@testgorilla/tgo-ui';
|
|
8
|
+
import { ROOT_TRANSLATIONS_SCOPE } from '../../models/translations';
|
|
9
|
+
import { translate } from '@ngneat/transloco';
|
|
10
|
+
import { CodeEvent, CodeEventResponse } from '../../models/code-event';
|
|
11
|
+
import { CodingTestConfigService } from '../coding-test-config.service';
|
|
12
|
+
|
|
13
|
+
@Injectable()
|
|
14
|
+
export class CoderunnerApiService {
|
|
15
|
+
private readonly snackbarService = inject(SnackbarService);
|
|
16
|
+
private readonly apiService = inject(ApiService);
|
|
17
|
+
private readonly configService = inject(CodingTestConfigService);
|
|
18
|
+
|
|
19
|
+
private readonly CR2Url = `${this.configService.getCoderunnerV2Endpoint()}/`;
|
|
20
|
+
|
|
21
|
+
// todo: will public preview be supported by mFE? if not - remove this
|
|
22
|
+
runPublicTest$(
|
|
23
|
+
code: string,
|
|
24
|
+
testcases: LATFormattedTestCases[] | SqlTestCase[],
|
|
25
|
+
language: string
|
|
26
|
+
): Observable<CoderunnerAttemptModel> {
|
|
27
|
+
return this.apiService.post(
|
|
28
|
+
`${this.CR2Url}api/attempts/public/`,
|
|
29
|
+
{
|
|
30
|
+
question_id: 0,
|
|
31
|
+
language,
|
|
32
|
+
code,
|
|
33
|
+
testcases,
|
|
34
|
+
question_result_id: 0,
|
|
35
|
+
},
|
|
36
|
+
{},
|
|
37
|
+
this.handleError.bind(this),
|
|
38
|
+
true
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
runPreviewTest$(
|
|
43
|
+
test_result_id: number,
|
|
44
|
+
code: string,
|
|
45
|
+
testcases: LATFormattedTestCases[] | SqlTestCase[],
|
|
46
|
+
language: string
|
|
47
|
+
): Observable<CoderunnerAttemptModel> {
|
|
48
|
+
return this.apiService.post(
|
|
49
|
+
`${this.CR2Url}api/attempts/preview/`,
|
|
50
|
+
{
|
|
51
|
+
language,
|
|
52
|
+
code,
|
|
53
|
+
testcases,
|
|
54
|
+
test_result_id,
|
|
55
|
+
},
|
|
56
|
+
{},
|
|
57
|
+
this.handleError.bind(this),
|
|
58
|
+
true
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
runNonPreviewTest$(
|
|
63
|
+
question_id: number,
|
|
64
|
+
question_result_id: number,
|
|
65
|
+
code: string,
|
|
66
|
+
testcases: LATFormattedTestCases[] | SqlTestCase[],
|
|
67
|
+
language: string
|
|
68
|
+
): Observable<CoderunnerAttemptModel> {
|
|
69
|
+
return this.apiService.post(
|
|
70
|
+
`${this.CR2Url}api/attempts/`,
|
|
71
|
+
{
|
|
72
|
+
question_id,
|
|
73
|
+
language,
|
|
74
|
+
code,
|
|
75
|
+
testcases,
|
|
76
|
+
question_result_id,
|
|
77
|
+
},
|
|
78
|
+
{},
|
|
79
|
+
this.handleError.bind(this),
|
|
80
|
+
true
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
sendCodeEvent$(event: CodeEvent): Observable<CodeEventResponse> {
|
|
85
|
+
return this.apiService.post(
|
|
86
|
+
`${this.CR2Url}api/events/`,
|
|
87
|
+
event,
|
|
88
|
+
{},
|
|
89
|
+
this.handleError.bind(this),
|
|
90
|
+
true
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private handleError({ error }: HttpErrorResponse): Observable<never> {
|
|
95
|
+
if (error.detail) {
|
|
96
|
+
return throwError(() => error);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const genericErrorText = translate(
|
|
100
|
+
`${ROOT_TRANSLATIONS_SCOPE}.ERRORS.SYSTEM_ERROR`
|
|
101
|
+
);
|
|
102
|
+
this.snackbarService.error(genericErrorText);
|
|
103
|
+
return EMPTY;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { TestBed, waitForAsync } from '@angular/core/testing';
|
|
2
|
+
import {
|
|
3
|
+
CodingTestTourMode,
|
|
4
|
+
CodingTestTourService,
|
|
5
|
+
} from './coding-test-tour.service';
|
|
6
|
+
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|
7
|
+
import { getTranslocoModule } from '@testgorilla/tgo-test-shared';
|
|
8
|
+
import { GuidedTourService } from 'ngx-guided-tour';
|
|
9
|
+
|
|
10
|
+
describe('CodingTestTourService', () => {
|
|
11
|
+
let service: CodingTestTourService;
|
|
12
|
+
let guidedTourService: GuidedTourService;
|
|
13
|
+
let startTourSpy: jest.SpyInstance;
|
|
14
|
+
|
|
15
|
+
const minimumScreenSize = 599;
|
|
16
|
+
const tourId = 'lat-coding-tour';
|
|
17
|
+
const tourSelectors = [
|
|
18
|
+
'#language-dropdown',
|
|
19
|
+
'#test-cases',
|
|
20
|
+
'#configuration-ide-button',
|
|
21
|
+
];
|
|
22
|
+
const practiceQuestionSelector = '#question-section';
|
|
23
|
+
const submitStepSelector = '[data-tourId*="question-next-finish-button"]';
|
|
24
|
+
|
|
25
|
+
beforeEach(waitForAsync(() => {
|
|
26
|
+
TestBed.configureTestingModule({
|
|
27
|
+
providers: [
|
|
28
|
+
CodingTestTourService,
|
|
29
|
+
{
|
|
30
|
+
provide: GuidedTourService,
|
|
31
|
+
useValue: {
|
|
32
|
+
currentTourStepCount: 10,
|
|
33
|
+
skipTour: jest.fn(),
|
|
34
|
+
startTour: jest.fn(),
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
imports: [getTranslocoModule()],
|
|
39
|
+
schemas: [NO_ERRORS_SCHEMA],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
service = TestBed.inject(CodingTestTourService);
|
|
43
|
+
guidedTourService = TestBed.inject(GuidedTourService);
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
describe('when init is called', () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
startTourSpy = jest.spyOn(guidedTourService, 'startTour');
|
|
49
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
50
|
+
value: minimumScreenSize + 1,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('and window innerWidth is MORE than minimum screen size', () => {
|
|
55
|
+
describe('and mode is PracticeQuestion', () => {
|
|
56
|
+
it('should start tour with proper steps', () => {
|
|
57
|
+
const stepsExpectedList = [
|
|
58
|
+
practiceQuestionSelector,
|
|
59
|
+
...tourSelectors,
|
|
60
|
+
submitStepSelector,
|
|
61
|
+
].map((selector) =>
|
|
62
|
+
expect.objectContaining({
|
|
63
|
+
selector,
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
service.init(CodingTestTourMode.PracticeQuestion);
|
|
68
|
+
|
|
69
|
+
expect(startTourSpy).toHaveBeenCalledWith(
|
|
70
|
+
expect.objectContaining({
|
|
71
|
+
tourId,
|
|
72
|
+
useOrb: false,
|
|
73
|
+
minimumScreenSize,
|
|
74
|
+
steps: expect.arrayContaining(stepsExpectedList),
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('and mode is RealQuestion', () => {
|
|
81
|
+
it('should start tour with proper steps', () => {
|
|
82
|
+
const stepsExpectedList = tourSelectors.map((selector) =>
|
|
83
|
+
expect.objectContaining({
|
|
84
|
+
selector,
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
service.init(CodingTestTourMode.PracticeQuestion);
|
|
89
|
+
|
|
90
|
+
expect(startTourSpy).toHaveBeenCalledWith(
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
tourId,
|
|
93
|
+
useOrb: false,
|
|
94
|
+
minimumScreenSize,
|
|
95
|
+
steps: expect.arrayContaining(stepsExpectedList),
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('and window innerWidth is LESS than minimum screen size', () => {
|
|
103
|
+
it('should NOT start tour', () => {
|
|
104
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
105
|
+
value: minimumScreenSize - 1,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
service.init(CodingTestTourMode.RealQuestion);
|
|
109
|
+
|
|
110
|
+
expect(startTourSpy).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should publish guided tour id', (done) => {
|
|
115
|
+
service.init(CodingTestTourMode.RealQuestion);
|
|
116
|
+
|
|
117
|
+
service.activeGuidedTourId$.subscribe((value) => {
|
|
118
|
+
expect(value).toEqual(tourId);
|
|
119
|
+
done();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
startTourSpy.mockRestore();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('when skipTour is called', () => {
|
|
129
|
+
let skipTourSpy: jest.SpyInstance;
|
|
130
|
+
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
skipTourSpy = jest.spyOn(guidedTourService, 'skipTour');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('and there is currentTourStepCount', () => {
|
|
136
|
+
it('should call guidedTourService skipTour', () => {
|
|
137
|
+
Object.defineProperty(guidedTourService, 'currentTourStepCount', {
|
|
138
|
+
get: jest.fn().mockReturnValue(1),
|
|
139
|
+
});
|
|
140
|
+
service.skipTour();
|
|
141
|
+
|
|
142
|
+
expect(skipTourSpy).toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('and there is NO currentTourStepCount', () => {
|
|
147
|
+
it('should NOT call guidedTourService skipTour', () => {
|
|
148
|
+
Object.defineProperty(guidedTourService, 'currentTourStepCount', {
|
|
149
|
+
get: jest.fn().mockReturnValue(null),
|
|
150
|
+
});
|
|
151
|
+
service.skipTour();
|
|
152
|
+
|
|
153
|
+
expect(skipTourSpy).not.toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
afterEach(() => {
|
|
158
|
+
skipTourSpy.mockRestore();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { translate } from '@ngneat/transloco';
|
|
3
|
+
import {
|
|
4
|
+
GuidedTour,
|
|
5
|
+
GuidedTourService,
|
|
6
|
+
Orientation,
|
|
7
|
+
TourStep,
|
|
8
|
+
} from 'ngx-guided-tour';
|
|
9
|
+
import { BehaviorSubject } from 'rxjs';
|
|
10
|
+
|
|
11
|
+
import { ROOT_TRANSLATIONS_SCOPE } from '../../models/translations';
|
|
12
|
+
|
|
13
|
+
export const enum CodingTestTourMode {
|
|
14
|
+
PracticeQuestion = 'practice',
|
|
15
|
+
RealQuestion = 'real',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class CodingTestTourService {
|
|
20
|
+
private readonly translationContext = `${ROOT_TRANSLATIONS_SCOPE}.TOUR.`;
|
|
21
|
+
private readonly guidedTourId = 'lat-coding-tour';
|
|
22
|
+
private readonly MINIMUM_SCREEN_SIZE = 599;
|
|
23
|
+
|
|
24
|
+
private activeGuidedTourIdSub$ = new BehaviorSubject('');
|
|
25
|
+
activeGuidedTourId$ = this.activeGuidedTourIdSub$.asObservable();
|
|
26
|
+
|
|
27
|
+
constructor(private guidedTourService: GuidedTourService) {}
|
|
28
|
+
|
|
29
|
+
init(mode: CodingTestTourMode): void {
|
|
30
|
+
this.start(mode);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
skipTour(): void {
|
|
34
|
+
// currentTourStepCount checks if there is a currentTour before checking stepCount.
|
|
35
|
+
if (this.guidedTourService.currentTourStepCount) {
|
|
36
|
+
this.guidedTourService.skipTour();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private start(mode: CodingTestTourMode) {
|
|
41
|
+
if (window.innerWidth > this.MINIMUM_SCREEN_SIZE) {
|
|
42
|
+
this.guidedTourService.startTour(this.getTour(mode));
|
|
43
|
+
}
|
|
44
|
+
this.publishActiveGuidedTourId();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private publishActiveGuidedTourId(): void {
|
|
48
|
+
this.activeGuidedTourIdSub$.next(this.guidedTourId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private getTour(mode: CodingTestTourMode): GuidedTour {
|
|
52
|
+
let steps: TourStep[] = [];
|
|
53
|
+
const step1 = {
|
|
54
|
+
title: translate(`${this.translationContext}STEP_1.TITLE`),
|
|
55
|
+
selector: '#question-section',
|
|
56
|
+
content: translate(`${this.translationContext}STEP_1.MESSAGE`),
|
|
57
|
+
orientation: Orientation.Right,
|
|
58
|
+
};
|
|
59
|
+
const step2 = {
|
|
60
|
+
title: translate(`${this.translationContext}STEP_2.TITLE`),
|
|
61
|
+
selector: '#language-dropdown',
|
|
62
|
+
content: translate(`${this.translationContext}STEP_2.MESSAGE`),
|
|
63
|
+
orientation: Orientation.Bottom,
|
|
64
|
+
};
|
|
65
|
+
const step3 = {
|
|
66
|
+
title: translate(`${this.translationContext}STEP_3.TITLE`),
|
|
67
|
+
selector: '#test-cases',
|
|
68
|
+
content: translate(`${this.translationContext}STEP_3.MESSAGE`),
|
|
69
|
+
orientation: Orientation.Top,
|
|
70
|
+
};
|
|
71
|
+
const step4 = {
|
|
72
|
+
title: translate(`${this.translationContext}STEP_4.TITLE`),
|
|
73
|
+
selector: '#configuration-ide-button',
|
|
74
|
+
content: translate(`${this.translationContext}STEP_4.MESSAGE`),
|
|
75
|
+
orientation: Orientation.BottomRight,
|
|
76
|
+
};
|
|
77
|
+
const step5 = {
|
|
78
|
+
title: translate(`${this.translationContext}STEP_5.TITLE`),
|
|
79
|
+
selector: '[data-tourId*="question-next-finish-button"]',
|
|
80
|
+
content: translate(`${this.translationContext}STEP_5.MESSAGE`),
|
|
81
|
+
orientation: Orientation.Bottom,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
switch (mode) {
|
|
85
|
+
case CodingTestTourMode.PracticeQuestion:
|
|
86
|
+
steps = [step1, step2, step3, step4, step5];
|
|
87
|
+
break;
|
|
88
|
+
case CodingTestTourMode.RealQuestion:
|
|
89
|
+
steps = [step2, step3, step4];
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
tourId: this.guidedTourId,
|
|
95
|
+
useOrb: false,
|
|
96
|
+
minimumScreenSize: this.MINIMUM_SCREEN_SIZE,
|
|
97
|
+
steps,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|