@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,82 @@
1
+ <section class="section">
2
+ <header *ngIf="isFullViewMode || isRunnableWithLangSelector" class="section-header">
3
+ <h2 class="heading">{{ translations['HEADINGS']['CODING'] }}</h2>
4
+ <div class="header-controls" [class.is-full-screen]="isFullscreenMode$ | async">
5
+ <form *ngIf="isLAT" [formGroup]="headerForm" class="header-form">
6
+ <ui-tooltip [message]="languages.length === 1 ? translations['TOOLTIPS']['DISABLE_LANGUAGE'] : null">
7
+ <ui-dropdown
8
+ id="language-dropdown"
9
+ class="language-dropdown"
10
+ formControlName="language"
11
+ [valueList]="languagesFormattedList"
12
+ [showBottomContent]="false"
13
+ [allowClear]="false"
14
+ [applicationTheme]="applicationTheme"
15
+ ></ui-dropdown>
16
+ </ui-tooltip>
17
+ </form>
18
+ <ui-button
19
+ [variant]="'icon-button'"
20
+ [iconName]="'Reset'"
21
+ [tooltip]="translations['TOOLTIPS']['RESET_CODE']"
22
+ [disabled]="hasNoCodeChanges"
23
+ [applicationTheme]="applicationTheme"
24
+ (buttonClickEvent)="resetCode()"
25
+ class="themed-button"
26
+ >
27
+ </ui-button>
28
+ <ui-button
29
+ *ngIf="isLAT && !isMobile && !isRunnableWithLangSelector"
30
+ [variant]="'icon-button'"
31
+ [iconName]="'Help'"
32
+ [tooltip]="translations['TOOLTIPS']['HELP']"
33
+ [applicationTheme]="applicationTheme"
34
+ (buttonClickEvent)="startGuidedTour()"
35
+ class="themed-button"
36
+ >
37
+ </ui-button>
38
+ <ui-button
39
+ *ngIf="!isRunnableWithLangSelector"
40
+ [variant]="'icon-button'"
41
+ [iconName]="'Settings'"
42
+ [tooltip]="translations['TOOLTIPS']['CONFIGURATIONS']"
43
+ [applicationTheme]="applicationTheme"
44
+ id="configuration-ide-button"
45
+ class="themed-button"
46
+ (buttonClickEvent)="openConfigPanel()"
47
+ >
48
+ </ui-button>
49
+
50
+ <ui-button
51
+ *ngIf="(isFullscreenMode$ | async) && !isMobile"
52
+ [variant]="'icon-button'"
53
+ [iconName]="'Minimize'"
54
+ [tooltip]="translations['TOOLTIPS']['MINIMIZE']"
55
+ [tooltipPosition]="'left'"
56
+ [applicationTheme]="applicationTheme"
57
+ class="themed-button"
58
+ (buttonClickEvent)="disableFullscreen()"
59
+ >
60
+ </ui-button>
61
+ </div>
62
+ </header>
63
+
64
+ <div class="ide" [class.spinner]="isLAT | memoizeFunc: isCodeLoading$ : this | async">
65
+ <tgo-code-editor
66
+ *ngIf="(isLAT | memoizeFunc: isCodeLoading$ : this | async) === false; else showSpinner"
67
+ [initCode]="initCode"
68
+ [functionParams]="functionParams"
69
+ [functionName]="functionName"
70
+ [returnType]="returnType"
71
+ [shouldGenerateInitCode]="!isFullViewMode && !isRunnableWithLangSelector && shouldGenerateInitCode"
72
+ [hasCodingConfigPanel]="isFullViewMode"
73
+ [autoHeight]="autoHeight"
74
+ (pasteEvent)="pasteEvent.emit($event)"
75
+ (codeChange)="parseCodeChange($event)"
76
+ >
77
+ </tgo-code-editor>
78
+ <ng-template #showSpinner>
79
+ <ui-spinner [isLoader]="false" class="code-loading-spinner"></ui-spinner>
80
+ </ng-template>
81
+ </div>
82
+ </section>
@@ -0,0 +1,64 @@
1
+ .section {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ overflow-y: hidden;
6
+ }
7
+
8
+ .section-header {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ align-items: center;
12
+ padding: 8px 16px;
13
+ border-bottom: 1px solid #E0E0E0;
14
+ max-height: 67px;
15
+
16
+ .header-controls {
17
+ display: flex;
18
+ width: 100%;
19
+ max-width: 265px;
20
+ justify-content: flex-end;
21
+ align-items: center;
22
+ flex: 1 1 auto;
23
+
24
+ .header-form {
25
+ flex: 1;
26
+ padding-right: 1em;
27
+ }
28
+
29
+ &.is-full-screen {
30
+ max-width: 305px;
31
+ }
32
+ }
33
+ }
34
+
35
+ .heading {
36
+ margin: 0;
37
+ padding: 10px 8px;
38
+ flex: 0 0 auto;
39
+ }
40
+
41
+ .ide {
42
+ flex: 1 0 auto;
43
+
44
+ @media screen and (max-width: 959px) {
45
+ flex: 1 0 350px;
46
+ }
47
+
48
+ &.spinner {
49
+ display: flex;
50
+ align-items: center;
51
+
52
+ .code-loading-spinner {
53
+ margin: 0 auto;
54
+ }
55
+ }
56
+ }
57
+
58
+ .icon {
59
+ ::ng-deep {
60
+ svg, path {
61
+ fill: var(--icon-bg-color);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,257 @@
1
+ import { NO_ERRORS_SCHEMA, SimpleChange, SimpleChanges } from '@angular/core';
2
+ import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
3
+
4
+ import { MockProvider } from 'ng-mocks';
5
+ import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
6
+ import { BehaviorSubject, Subject } from 'rxjs';
7
+ import { MemoizeFuncPipe } from '../../pipes/memoize-func.pipe';
8
+ import { LibCodingTestService } from '../../services/lib-coding-test.service';
9
+ import { CodeEditorLanguages } from '../code-editor/helpers/code-editor-helper.model';
10
+ import { CodingSectionComponent } from './coding-section.component';
11
+ import { ConfigurationsService } from '../../services/configurations.service';
12
+ import { CONFIGURATIONS_SERVICE_MOCK } from '../../services/configurations.service.mocks';
13
+ import { FormBuilder } from '@angular/forms';
14
+ import { StorageCodingService } from '../../services/storage.service';
15
+ import { LanguageChangeAction } from '../../models/language-change-action';
16
+
17
+ describe('CodingSectionComponent', () => {
18
+ let component: CodingSectionComponent;
19
+ let fixture: ComponentFixture<CodingSectionComponent>;
20
+ let libCodingTestService: LibCodingTestService;
21
+ let configurationsService: ConfigurationsService;
22
+ let changeCurrentCodeSpy: jest.SpyInstance;
23
+ const initCode = 'init code';
24
+
25
+ beforeEach(waitForAsync(() => {
26
+ TestBed.configureTestingModule({
27
+ imports: [CodingSectionComponent, MonacoEditorModule.forRoot(), MemoizeFuncPipe],
28
+ schemas: [NO_ERRORS_SCHEMA],
29
+ providers: [
30
+ MockProvider(LibCodingTestService, {
31
+ codeChange$: new Subject(),
32
+ changeCurrentCode(code: string) {
33
+ this.codeChange$.next(code);
34
+ },
35
+ isBoilerplateLoading$: new Subject(),
36
+ setBoilerplateLoadingStatus(value: boolean) {
37
+ this.isBoilerplateLoading$.next(value);
38
+ },
39
+ languageChange$: new Subject(),
40
+ codeReset$: new Subject(),
41
+ getInitCode: () => initCode,
42
+ currentLanguage$: new BehaviorSubject<CodeEditorLanguages>(CodeEditorLanguages.Javascript),
43
+ }),
44
+ MockProvider(ConfigurationsService, CONFIGURATIONS_SERVICE_MOCK),
45
+ FormBuilder,
46
+ MockProvider(StorageCodingService),
47
+ ],
48
+ })
49
+ .overrideComponent(CodingSectionComponent, {
50
+ set: {
51
+ template: '',
52
+ },
53
+ })
54
+ .compileComponents();
55
+ }));
56
+
57
+ beforeEach(() => {
58
+ fixture = TestBed.createComponent(CodingSectionComponent);
59
+ component = fixture.componentInstance;
60
+ component.translations = {
61
+ HEADINGS: {},
62
+ TOOLTIPS: {},
63
+ };
64
+ component.languages = [
65
+ {
66
+ value: CodeEditorLanguages.Python,
67
+ label: 'python',
68
+ version: '1',
69
+ },
70
+ ];
71
+ component.initCode = 'sample python code';
72
+ libCodingTestService = TestBed.inject(LibCodingTestService);
73
+ configurationsService = TestBed.inject(ConfigurationsService);
74
+ changeCurrentCodeSpy = jest.spyOn(libCodingTestService, 'changeCurrentCode');
75
+
76
+ fixture.detectChanges();
77
+ });
78
+
79
+ describe('when the component is initialized', () => {
80
+ describe('and when isLAT is true', () => {
81
+ beforeEach(() => {
82
+ component.isLAT = true;
83
+ });
84
+
85
+ it('should initialize header form when isLAT is true', () => {
86
+ component.ngOnInit();
87
+
88
+ expect(component.headerForm).toBeDefined();
89
+ expect(component.headerForm.get('language')).toBeDefined();
90
+ expect(component.headerForm.get('language').value).toBe(CodeEditorLanguages.Javascript);
91
+ expect(component.headerForm.get('language').disabled).toBe(true);
92
+ });
93
+
94
+ it('should NOT disable language control of headerForm', () => {
95
+ component.languages = [
96
+ ...component.languages,
97
+ {
98
+ value: CodeEditorLanguages.Java,
99
+ label: 'java',
100
+ },
101
+ ];
102
+
103
+ component.ngOnInit();
104
+
105
+ expect(component.headerForm).toBeDefined();
106
+ expect(component.headerForm.get('language').enabled).toBe(true);
107
+ });
108
+
109
+ it('should set previousLanguage to languageControl value', () => {
110
+ component.ngOnInit();
111
+
112
+ expect(component.previousLanguage).toBe(CodeEditorLanguages.Javascript);
113
+ });
114
+
115
+ it('should emit languageChange value when language form value is changed', fakeAsync(() => {
116
+ const languageChangeEmitSpy = jest.spyOn(component.languageChange, 'emit');
117
+
118
+ component.ngOnInit();
119
+ component.language.patchValue(CodeEditorLanguages.Cpp);
120
+ tick();
121
+
122
+ expect(languageChangeEmitSpy).toHaveBeenCalledWith(CodeEditorLanguages.Cpp);
123
+ }));
124
+
125
+ it('should set language control value to previousLang if LanguageChangeAction.Cancel', fakeAsync(() => {
126
+ component.ngOnInit();
127
+ component.previousLanguage = CodeEditorLanguages.Php;
128
+ (libCodingTestService.languageChange$ as Subject<LanguageChangeAction>).next(LanguageChangeAction.Cancel);
129
+ tick();
130
+
131
+ expect(component.language.value).toBe(CodeEditorLanguages.Php);
132
+ }));
133
+
134
+ describe('and when LanguageChangeAction.Confirm is emitted', () => {
135
+ it('should set prevLanguage to language control value and changeCurrentLanguage is called', fakeAsync(() => {
136
+ const changeCurrentLanguageSpy = jest.spyOn(libCodingTestService, 'changeCurrentLanguage');
137
+
138
+ component.ngOnInit();
139
+ component.previousLanguage = CodeEditorLanguages.Php;
140
+ component.language.patchValue(CodeEditorLanguages.Python);
141
+ (libCodingTestService.languageChange$ as Subject<LanguageChangeAction>).next(LanguageChangeAction.Confirm);
142
+ tick();
143
+
144
+ expect(component.previousLanguage).toBe(CodeEditorLanguages.Python);
145
+ expect(changeCurrentLanguageSpy).toHaveBeenCalledWith(CodeEditorLanguages.Python);
146
+ }));
147
+ });
148
+ });
149
+ });
150
+
151
+ describe('when initCode changes', () => {
152
+ it('should call the necessary setters', () => {
153
+ const codeChangeSpy = jest.spyOn(component.codeChange, 'emit');
154
+ const oldInitCode = component.initCode;
155
+ const newInitCode = 'sample java code';
156
+ component.initCode = newInitCode;
157
+ const changes: SimpleChanges = {
158
+ initCode: new SimpleChange(oldInitCode, newInitCode, false),
159
+ };
160
+ component.ngOnChanges(changes);
161
+
162
+ expect(codeChangeSpy).toBeCalledWith(newInitCode);
163
+ expect(changeCurrentCodeSpy).toBeCalledWith(newInitCode);
164
+ });
165
+ });
166
+
167
+ describe('when parseCodeChange is called', () => {
168
+ it('should emit code change event with new code string', () => {
169
+ const code = 'new code';
170
+ const emitSpy = jest.spyOn(component.codeChange, 'emit');
171
+
172
+ component.parseCodeChange(code);
173
+
174
+ expect(emitSpy).toHaveBeenCalledWith(code);
175
+ expect(changeCurrentCodeSpy).toHaveBeenCalledWith(code);
176
+ });
177
+
178
+ it('should set hasNoCodeChanges to false if code is different from initial code', () => {
179
+ const code = 'new code';
180
+
181
+ component.parseCodeChange(code);
182
+
183
+ expect(component.hasNoCodeChanges).toBe(false);
184
+ });
185
+
186
+ it('should set hasNoCodeChanges to true if code is the same as initial code', () => {
187
+ const code = initCode;
188
+
189
+ component.parseCodeChange(code);
190
+
191
+ expect(component.hasNoCodeChanges).toBe(true);
192
+ });
193
+ });
194
+
195
+ describe('when the code is reset', () => {
196
+ let resetCodeSpy: jest.SpyInstance;
197
+
198
+ beforeEach(() => {
199
+ resetCodeSpy = jest.spyOn(libCodingTestService, 'resetCode');
200
+ component.resetCode();
201
+ });
202
+
203
+ it('should call service reset code method', () => {
204
+ expect(resetCodeSpy).toBeCalled();
205
+ });
206
+ });
207
+
208
+ describe('when the guided tour is triggered', () => {
209
+ let startGuidedTourSpy: jest.SpyInstance;
210
+
211
+ beforeEach(() => {
212
+ startGuidedTourSpy = jest.spyOn(libCodingTestService, 'startGuidedTour');
213
+ component.startGuidedTour();
214
+ });
215
+
216
+ it('should call service reset code method', () => {
217
+ expect(startGuidedTourSpy).toBeCalled();
218
+ });
219
+ });
220
+
221
+ describe('when disableFullscreen is called', () => {
222
+ it('should call setFullscreen method with false', () => {
223
+ const setFullScreenSpy = jest.spyOn(libCodingTestService, 'setFullscreen');
224
+ component.disableFullscreen();
225
+
226
+ expect(setFullScreenSpy).toHaveBeenCalledWith(false);
227
+ });
228
+ });
229
+
230
+ describe('when openConfigPanel is called', () => {
231
+ it('should call openConfigPanel method of ConfigurationsService', () => {
232
+ const openConfigPanelSpy = jest.spyOn(configurationsService, 'openConfigPanel');
233
+ component.openConfigPanel();
234
+
235
+ expect(openConfigPanelSpy).toHaveBeenCalled();
236
+ });
237
+ });
238
+
239
+ describe('when isCodeLoading$ is called', () => {
240
+ it('should return false observable if test is NOT LAT', done => {
241
+ component.isLAT = false;
242
+ component.isCodeLoading$(component.isLAT).subscribe(val => {
243
+ expect(val).toEqual(false);
244
+ done();
245
+ });
246
+ });
247
+ it('should return boilerplate loading status if test is LAT', done => {
248
+ component.isLAT = true;
249
+ const boilerplateLoadingStatus = true;
250
+ component.isCodeLoading$(component.isLAT).subscribe(val => {
251
+ expect(val).toEqual(boilerplateLoadingStatus);
252
+ done();
253
+ });
254
+ libCodingTestService.setBoilerplateLoadingStatus(boilerplateLoadingStatus);
255
+ });
256
+ });
257
+ });
@@ -0,0 +1,187 @@
1
+ import { ChangeDetectionStrategy, Component, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { AbstractControl, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
4
+ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
5
+ import { distinctUntilChanged, Observable, of, take, tap } from 'rxjs';
6
+ import {
7
+ ApplicationTheme,
8
+ OptionType,
9
+ TooltipComponentModule,
10
+ DropdownComponentModule,
11
+ ButtonComponentModule,
12
+ SpinnerComponentModule,
13
+ } from '@testgorilla/tgo-ui';
14
+
15
+ import { LanguageChangeAction } from '../../models/language-change-action';
16
+ import { LATLanguages } from '../../models/lat-languages';
17
+ import { ConfigurationsService } from '../../services/configurations.service';
18
+ import { LibCodingTestService } from '../../services/lib-coding-test.service';
19
+ import { StorageCodingService } from '../../services/storage.service';
20
+ import {
21
+ CodeEditorFuncParam,
22
+ CodeEditorLanguages,
23
+ CodeEditorTypesMap,
24
+ } from '../code-editor/helpers/code-editor-helper.model';
25
+ import { PasteData } from '../code-editor/models/code-editor.model';
26
+ import { MemoizeFuncPipe } from '../../pipes/memoize-func.pipe';
27
+ import { CodeEditorComponent } from '../code-editor/code-editor.component';
28
+
29
+ @UntilDestroy()
30
+ @Component({
31
+ standalone: true,
32
+ selector: 'tgo-coding-section',
33
+ templateUrl: './coding-section.component.html',
34
+ styleUrls: ['./coding-section.component.scss'],
35
+ changeDetection: ChangeDetectionStrategy.OnPush,
36
+ imports: [
37
+ CommonModule,
38
+ ReactiveFormsModule,
39
+ TooltipComponentModule,
40
+ DropdownComponentModule,
41
+ ButtonComponentModule,
42
+ SpinnerComponentModule,
43
+ MemoizeFuncPipe,
44
+ CodeEditorComponent,
45
+ ],
46
+ })
47
+ export class CodingSectionComponent implements OnInit, OnChanges {
48
+ private readonly configurationsService = inject(ConfigurationsService);
49
+ private readonly libCodingTestService = inject(LibCodingTestService);
50
+ private readonly StorageCodingService = inject(StorageCodingService);
51
+ private readonly fb = inject(FormBuilder);
52
+
53
+ @Input() initCode: string;
54
+ @Input() functionParams: CodeEditorFuncParam[];
55
+ @Input() functionName: string;
56
+ @Input() returnType: keyof CodeEditorTypesMap;
57
+ @Input() isLAT = false;
58
+ @Input() languages: LATLanguages;
59
+ @Input() isFullViewMode: boolean;
60
+ @Input() shouldGenerateInitCode: boolean;
61
+ @Input() isRunnableWithLangSelector: boolean;
62
+ @Input() autoHeight: boolean;
63
+ @Input() translations: Record<string, unknown>;
64
+ @Input() isMobile: boolean;
65
+ @Input() applicationTheme: ApplicationTheme;
66
+
67
+ @Output() pasteEvent = new EventEmitter<PasteData>();
68
+ @Output() codeChange = new EventEmitter<string>();
69
+ @Output() languageChange = new EventEmitter<CodeEditorLanguages>();
70
+
71
+ headerForm: FormGroup;
72
+ isFullscreenMode$ = this.libCodingTestService.isFullscreen$;
73
+ previousLanguage: CodeEditorLanguages;
74
+ code: string;
75
+
76
+ languagesFormattedList: OptionType[];
77
+
78
+ ngOnInit() {
79
+ if (this.isLAT) {
80
+ this.createHeaderForm().subscribe(() => {
81
+ this.initializeLanguage();
82
+ this.listenLanguageDropdownChanges();
83
+ this.handleLanguageChangeModal();
84
+ });
85
+ }
86
+ }
87
+
88
+ ngOnChanges(changes: SimpleChanges): void {
89
+ if (typeof changes.initCode?.currentValue === 'string') {
90
+ this.codeChange.emit(changes.initCode.currentValue);
91
+ this.libCodingTestService.changeCurrentCode(changes.initCode.currentValue);
92
+ }
93
+ if (changes.languages?.currentValue) {
94
+ this.languagesFormattedList = this.languages.map(({ value, label }) => ({
95
+ value,
96
+ displayValue: label,
97
+ }));
98
+ }
99
+ }
100
+
101
+ private createHeaderForm(): Observable<CodeEditorLanguages> {
102
+ return this.libCodingTestService.currentLanguage$.pipe(
103
+ take(1),
104
+ tap(lastLanguage => {
105
+ this.headerForm = this.fb.group({
106
+ language: lastLanguage ? lastLanguage : this.languages[0].value,
107
+ });
108
+ if (this.languages.length === 1) {
109
+ this.language.disable();
110
+ }
111
+ })
112
+ );
113
+ }
114
+
115
+ private initializeLanguage() {
116
+ this.previousLanguage = this.language.value as CodeEditorLanguages;
117
+ }
118
+
119
+ private listenLanguageDropdownChanges() {
120
+ this.language.valueChanges
121
+ .pipe(untilDestroyed(this), distinctUntilChanged())
122
+ .subscribe((value: CodeEditorLanguages) => {
123
+ if (value !== this.previousLanguage) {
124
+ this.languageChange.emit(value);
125
+ }
126
+ });
127
+ }
128
+
129
+ private handleLanguageChangeModal() {
130
+ this.libCodingTestService.languageChange$.pipe(untilDestroyed(this)).subscribe(value => {
131
+ if (value === LanguageChangeAction.Cancel) {
132
+ // Revert language to previous selection if user cancels
133
+ this.language.setValue(this.previousLanguage);
134
+ } else if (value === LanguageChangeAction.Confirm) {
135
+ this.previousLanguage = this.language.value as CodeEditorLanguages;
136
+ // Update current language if user confirms the new selection
137
+ this.libCodingTestService.changeCurrentLanguage(this.language.value as CodeEditorLanguages);
138
+ this.setVersion();
139
+ }
140
+ });
141
+ }
142
+
143
+ parseCodeChange(code: string): void {
144
+ this.codeChange.emit(code);
145
+ this.libCodingTestService.changeCurrentCode(code);
146
+ this.code = code;
147
+ }
148
+
149
+ get hasNoCodeChanges(): boolean {
150
+ return this.code === this.libCodingTestService.getInitCode();
151
+ }
152
+
153
+ resetCode(): void {
154
+ this.libCodingTestService.resetCode();
155
+ }
156
+
157
+ startGuidedTour(): void {
158
+ this.libCodingTestService.startGuidedTour();
159
+ }
160
+
161
+ disableFullscreen(): void {
162
+ this.libCodingTestService.setFullscreen(false);
163
+ }
164
+
165
+ openConfigPanel(): void {
166
+ this.configurationsService.openConfigPanel();
167
+ }
168
+
169
+ isCodeLoading$(isLAT: boolean): Observable<boolean> {
170
+ if (isLAT) {
171
+ return this.libCodingTestService.isBoilerplateLoading$;
172
+ }
173
+ return of(false);
174
+ }
175
+
176
+ get language(): AbstractControl {
177
+ return this.headerForm.controls.language;
178
+ }
179
+
180
+ private setVersion() {
181
+ const version = this.languages.find(x => x.value === this.language.value).version;
182
+ this.libCodingTestService.setVersion(version);
183
+ if (this.isFullViewMode) {
184
+ this.StorageCodingService.updateVersion(version);
185
+ }
186
+ }
187
+ }