@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,136 @@
1
+ import { NO_ERRORS_SCHEMA, SimpleChange, SimpleChanges } from '@angular/core';
2
+ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
+ import { MockProvider } from 'ng-mocks';
4
+ import { Observable, ReplaySubject } from 'rxjs';
5
+
6
+ import { LibCodingTestService } from '../../services/lib-coding-test.service';
7
+ import { StorageCodingService } from '../../services/storage.service';
8
+ import { CodeEditorComponent } from './code-editor.component';
9
+ import { editorMock, functionParamsMock, initCodeMock } from './code-editor.mocks';
10
+ import { CodeEditorService } from './code-editor.service';
11
+ import { CodeEditorLanguages } from './helpers/code-editor-helper.model';
12
+ import { CONFIGURATIONS_SERVICE_MOCK } from '../../services/configurations.service.mocks';
13
+ import { ConfigurationsService } from '../../services/configurations.service';
14
+ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
15
+ import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
16
+
17
+ describe('CodeEditorComponent', () => {
18
+ let component: CodeEditorComponent;
19
+ let fixture: ComponentFixture<CodeEditorComponent>;
20
+
21
+ let codeEditorService: CodeEditorService;
22
+ let libCodingTestService: LibCodingTestService;
23
+
24
+ let setHelperSpy: jest.SpyInstance;
25
+ let getEditorLangSpy: jest.SpyInstance;
26
+ let getInitCodeSpy: jest.SpyInstance;
27
+ let codeChangeSpy: jest.SpyInstance;
28
+ let pasteEventSpy: jest.SpyInstance;
29
+
30
+ const currentLanguage = CodeEditorLanguages.Python;
31
+
32
+ beforeEach(waitForAsync(() => {
33
+ TestBed.configureTestingModule({
34
+ imports: [CodeEditorComponent, MonacoEditorModule.forRoot(), NoopAnimationsModule],
35
+ schemas: [NO_ERRORS_SCHEMA],
36
+ providers: [
37
+ MockProvider(CodeEditorService, {
38
+ getEditorLang: () => currentLanguage,
39
+ }),
40
+ MockProvider(LibCodingTestService, {
41
+ codeReset$: new Observable(),
42
+ currentLanguage$: new ReplaySubject(1),
43
+ changeCurrentLanguage(value: CodeEditorLanguages): void {
44
+ this.currentLanguage$.next(value);
45
+ },
46
+ }),
47
+ MockProvider(ConfigurationsService, CONFIGURATIONS_SERVICE_MOCK),
48
+ MockProvider(StorageCodingService),
49
+ ],
50
+ }).compileComponents();
51
+ }));
52
+
53
+ beforeEach(() => {
54
+ fixture = TestBed.createComponent(CodeEditorComponent);
55
+ component = fixture.componentInstance;
56
+ component.functionParams = [...functionParamsMock];
57
+ component.functionName = 'mockFunction';
58
+ component.returnType = 'boolean';
59
+ component.hasCodingConfigPanel = true;
60
+ component.isReadonly = false;
61
+ component.initCode = initCodeMock;
62
+ component.shouldGenerateInitCode = true;
63
+
64
+ codeEditorService = TestBed.inject(CodeEditorService);
65
+ libCodingTestService = TestBed.inject(LibCodingTestService);
66
+ setHelperSpy = jest.spyOn(codeEditorService, 'setHelper');
67
+ getEditorLangSpy = jest.spyOn(codeEditorService, 'getEditorLang');
68
+ getInitCodeSpy = jest.spyOn(codeEditorService, 'getInitCode');
69
+ codeChangeSpy = jest.spyOn(component.codeChange, 'emit');
70
+
71
+ libCodingTestService.changeCurrentLanguage(currentLanguage);
72
+ fixture.detectChanges();
73
+ });
74
+
75
+ describe('when initialized', () => {
76
+ beforeEach(() => {
77
+ component.editor = editorMock;
78
+ pasteEventSpy = jest.spyOn(component.editor, 'onDidPaste');
79
+ component.initEditor(editorMock);
80
+ });
81
+
82
+ it('should set configurations', () => {
83
+ const params = {
84
+ params: component.functionParams,
85
+ name: component.functionName,
86
+ returnType: component.returnType,
87
+ };
88
+
89
+ expect(setHelperSpy).toBeCalledWith(currentLanguage, params);
90
+ expect(getEditorLangSpy).toBeCalled();
91
+ expect(component.editorOptions.language).toEqual(currentLanguage);
92
+ expect(component.editorOptions.readOnly).toEqual(component.isReadonly);
93
+ });
94
+
95
+ describe('and has initCode passed', () => {
96
+ it('should set passed init code to local code', () => {
97
+ expect(component.code).toEqual(initCodeMock);
98
+ });
99
+ });
100
+
101
+ describe('and has NO initCode passed and has shouldGenerateInitCode = true', () => {
102
+ beforeEach(() => {
103
+ component.initCode = '';
104
+ component.shouldGenerateInitCode = true;
105
+ component.ngOnInit();
106
+ });
107
+
108
+ it('should get init code from service', () => {
109
+ expect(getInitCodeSpy).toBeCalled();
110
+ });
111
+ });
112
+
113
+ it('should publish updated init code', () => {
114
+ expect(component.codePublished).toEqual(component.code);
115
+ expect(codeChangeSpy).toBeCalledWith(component.codePublished);
116
+ });
117
+
118
+ it('should listen to paste editor event', () => {
119
+ expect(pasteEventSpy).toHaveBeenCalled();
120
+ });
121
+ });
122
+
123
+ describe('when initCode changes', () => {
124
+ it('should update code passed to the editor', () => {
125
+ const oldInitCode = component.initCode;
126
+ const newInitCode = 'sample java code';
127
+ component.initCode = newInitCode;
128
+ const changes: SimpleChanges = {
129
+ initCode: new SimpleChange(oldInitCode, newInitCode, false),
130
+ };
131
+ component.ngOnChanges(changes);
132
+
133
+ expect(component.code).toEqual(newInitCode);
134
+ });
135
+ });
136
+ });
@@ -0,0 +1,369 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ ChangeDetectorRef,
4
+ Component,
5
+ ElementRef,
6
+ EventEmitter,
7
+ inject,
8
+ Input,
9
+ OnChanges,
10
+ OnDestroy,
11
+ OnInit,
12
+ Output,
13
+ SimpleChanges,
14
+ ViewChild,
15
+ } from '@angular/core';
16
+ import { CommonModule } from '@angular/common';
17
+ import { FormsModule } from '@angular/forms';
18
+ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
19
+ import { editor, IDisposable } from 'monaco-editor';
20
+ import { combineLatest, distinctUntilChanged, filter, first, interval, map, take } from 'rxjs';
21
+ import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
22
+
23
+ import { erlangLanguage } from '../../utils/additional-languages/erlang';
24
+ import { CodingAssistanceOptions, Config, FormattingOptions } from '../../models/configs';
25
+ import { Themes } from '../../models/theme';
26
+ import { ConfigurationsService } from '../../services/configurations.service';
27
+ import { LibCodingTestService, ResetCodeAction } from '../../services/lib-coding-test.service';
28
+ import { StorageCodingService } from '../../services/storage.service';
29
+ import { CodeEditorService } from './code-editor.service';
30
+ import { CodeEditorFuncParam, CodeEditorLanguages, CodeEditorTypesMap } from './helpers/code-editor-helper.model';
31
+ import { MonacoColorTheme, PasteData } from './models/code-editor.model';
32
+ import { CodingSnapshot } from './models/coding-snapshot.model';
33
+
34
+ @UntilDestroy()
35
+ @Component({
36
+ standalone: true,
37
+ selector: 'tgo-code-editor',
38
+ templateUrl: './code-editor.component.html',
39
+ styleUrls: ['./code-editor.component.scss'],
40
+ changeDetection: ChangeDetectionStrategy.OnPush,
41
+ imports: [CommonModule, FormsModule, MonacoEditorModule],
42
+ })
43
+ export class CodeEditorComponent implements OnInit, OnDestroy, OnChanges {
44
+ private readonly libCodingTestService = inject(LibCodingTestService);
45
+ private readonly configurationsService = inject(ConfigurationsService);
46
+ private readonly codeEditorService = inject(CodeEditorService);
47
+ private readonly changeDetectorRef = inject(ChangeDetectorRef);
48
+ private readonly StorageCodingService = inject(StorageCodingService);
49
+
50
+ @Input() initCode: string;
51
+ @Input() functionParams: CodeEditorFuncParam[];
52
+ @Input() functionName: string;
53
+ @Input() returnType: keyof CodeEditorTypesMap;
54
+ @Input() hasCodingConfigPanel: boolean;
55
+ @Input() isReadonly: boolean;
56
+ @Input() autoHeight: boolean;
57
+ @Input() shouldGenerateInitCode: boolean;
58
+
59
+ @Input() set snapshot(snap: CodingSnapshot) {
60
+ if (snap) {
61
+ this.currentSnapshot = snap;
62
+ this.handleSnapshot(snap.text);
63
+ }
64
+ }
65
+
66
+ @Output() pasteEvent = new EventEmitter<PasteData>();
67
+ @Output() codeChange = new EventEmitter<string>();
68
+
69
+ @ViewChild('editor', { read: ElementRef }) codeEditorElRef: ElementRef;
70
+
71
+ code = '';
72
+ codePublished = '';
73
+ codeLanguage = '';
74
+ editorOptions: { [key: string]: any } = {
75
+ theme: MonacoColorTheme.Dark,
76
+ fontSize: '14',
77
+ scrollBeyondLastLine: false,
78
+ minimap: { enabled: false },
79
+ fixedOverflowWidgets: true,
80
+ insertSpaces: false,
81
+ detectIndentation: false,
82
+ automaticLayout: true,
83
+ };
84
+
85
+ editor: editor.ICodeEditor;
86
+ previousConfig = this.StorageCodingService.getSavedConfig();
87
+
88
+ currentSnapshot: CodingSnapshot;
89
+ previousMonacoDecoration: editor.IEditorDecorationsCollection;
90
+
91
+ private pasteListener: IDisposable;
92
+ private contentChangedSub: IDisposable;
93
+
94
+ ngOnInit(): void {
95
+ if (!this.previousConfig) {
96
+ this.previousConfig = this.configurationsService.getInitialConfig();
97
+ }
98
+
99
+ if (this.hasCodingConfigPanel) {
100
+ this.updateIDEConfig();
101
+ }
102
+ this.listenUpdates();
103
+
104
+ this.setupConfigs();
105
+ this.setupCode();
106
+ }
107
+
108
+ ngOnChanges(changes: SimpleChanges): void {
109
+ if (typeof changes.initCode?.currentValue === 'string') {
110
+ this.code = changes.initCode.currentValue;
111
+ }
112
+ }
113
+
114
+ ngOnDestroy(): void {
115
+ this.contentChangedSub?.dispose();
116
+ this.pasteListener?.dispose();
117
+ }
118
+
119
+ initEditor(editor: editor.ICodeEditor): void {
120
+ this.editor = editor;
121
+
122
+ this.pasteListener = this.editor.onDidPaste(event => this.catchEvent(event));
123
+
124
+ if (this.currentSnapshot) {
125
+ this.handleSnapshot(this.currentSnapshot.text);
126
+ }
127
+
128
+ if (this.autoHeight) {
129
+ this.listenContentHeight(editor);
130
+ }
131
+ }
132
+
133
+ private catchEvent(event: editor.IPasteEvent): void {
134
+ const data = this.editor.getModel()?.getValueInRange(event.range);
135
+ if (data) {
136
+ this.pasteEvent.emit({ type: 'paste', data });
137
+ }
138
+ }
139
+
140
+ publishCode(code): void {
141
+ if (code !== this.codePublished) {
142
+ this.codePublished = code;
143
+ this.codeChange.emit(this.codePublished);
144
+ }
145
+ }
146
+
147
+ private updateIDEConfig(): void {
148
+ this.configurationsService.config$
149
+ .pipe(distinctUntilChanged(), untilDestroyed(this))
150
+ .subscribe((config: Config) => {
151
+ let configObject = {};
152
+ // eslint-disable-next-line guard-for-in
153
+ for (const key in config) {
154
+ if (config[key] !== this.previousConfig[key]) {
155
+ switch (key as FormattingOptions | CodingAssistanceOptions) {
156
+ case FormattingOptions.ColorTheme:
157
+ configObject = {
158
+ theme:
159
+ config[FormattingOptions.ColorTheme] === Themes.Default
160
+ ? MonacoColorTheme.Light
161
+ : MonacoColorTheme.Dark,
162
+ };
163
+
164
+ break;
165
+ case CodingAssistanceOptions.AutoComplete:
166
+ configObject = {
167
+ quickSuggestions: {
168
+ other: config[CodingAssistanceOptions.AutoComplete],
169
+ },
170
+ };
171
+
172
+ break;
173
+ case CodingAssistanceOptions.AutoBrackets:
174
+ configObject = {
175
+ autoClosingBrackets: config[CodingAssistanceOptions.AutoBrackets] ? 'always' : 'never',
176
+ };
177
+
178
+ break;
179
+ case FormattingOptions.TabSize:
180
+ configObject = {
181
+ tabSize: config[FormattingOptions.TabSize],
182
+ };
183
+
184
+ break;
185
+ case FormattingOptions.FontSize:
186
+ configObject = {
187
+ fontSize: config[FormattingOptions.FontSize],
188
+ };
189
+
190
+ break;
191
+ case CodingAssistanceOptions.ErrorHighlighting:
192
+ this.libCodingTestService.currentLanguage$.pipe(take(1)).subscribe(language => {
193
+ if (language === CodeEditorLanguages.Javascript) {
194
+ window['monaco'].languages.typescript.javascriptDefaults.setDiagnosticsOptions({
195
+ noSemanticValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
196
+ noSyntaxValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
197
+ });
198
+ } else if (language === CodeEditorLanguages.Typescript) {
199
+ window['monaco'].languages.typescript.typescriptDefaults.setDiagnosticsOptions({
200
+ noSemanticValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
201
+ noSyntaxValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
202
+ });
203
+ }
204
+ });
205
+ break;
206
+ }
207
+ this.editorOptions = Object.assign({}, this.editorOptions, configObject);
208
+ }
209
+ }
210
+ this.changeDetectorRef.detectChanges();
211
+ this.previousConfig = config;
212
+ });
213
+
214
+ this.editorOptions = Object.assign({}, this.editorOptions, {
215
+ automaticLayout: true,
216
+ });
217
+ }
218
+
219
+ private listenContentHeight(editor: editor.ICodeEditor): void {
220
+ const updateHeight = () => {
221
+ window.requestAnimationFrame(() => {
222
+ const { width } = this.codeEditorElRef.nativeElement.getBoundingClientRect();
223
+ const contentHeight = Math.min(1000, editor.getContentHeight());
224
+ this.codeEditorElRef.nativeElement.style.height = `${contentHeight}px`;
225
+ editor.layout({ width, height: contentHeight });
226
+ });
227
+ };
228
+
229
+ this.contentChangedSub = editor.onDidContentSizeChange(updateHeight);
230
+ updateHeight();
231
+ }
232
+
233
+ private setupConfigs(): void {
234
+ const { functionParams: params, functionName: name, returnType } = this;
235
+
236
+ const config = this.previousConfig;
237
+
238
+ const wordWrap = this.currentSnapshot ? 'on' : 'off';
239
+ const theme = this.getInitialColorTheme(config, this.currentSnapshot);
240
+
241
+ this.libCodingTestService.currentLanguage$.pipe(untilDestroyed(this)).subscribe((language: CodeEditorLanguages) => {
242
+ if (this.shouldGenerateInitCode) {
243
+ this.codeEditorService.setHelper(language, { params, name, returnType });
244
+ this.codeLanguage = this.codeEditorService.getEditorLang();
245
+ } else {
246
+ if (language === CodeEditorLanguages.Cpp) {
247
+ this.codeLanguage = CodeEditorLanguages.CppEditor;
248
+ } else if (language === CodeEditorLanguages.CSharp || language === CodeEditorLanguages.CSharpLegacy) {
249
+ this.codeLanguage = CodeEditorLanguages.CSharpEditor;
250
+ } else if (language === CodeEditorLanguages.Sqlite) {
251
+ this.codeLanguage = CodeEditorLanguages.Sql;
252
+ } else {
253
+ this.codeLanguage = language;
254
+ }
255
+ }
256
+ this.editorOptions = Object.assign({}, this.editorOptions, {
257
+ language: this.codeLanguage,
258
+ });
259
+ });
260
+
261
+ this.editorOptions = Object.assign({}, this.editorOptions, {
262
+ readOnly: this.isReadonly,
263
+ quickSuggestions: {
264
+ other: config[CodingAssistanceOptions.AutoComplete],
265
+ },
266
+ autoClosingBrackets: config[CodingAssistanceOptions.AutoBrackets] ? 'always' : 'never',
267
+ tabSize: config[FormattingOptions.TabSize],
268
+ fontSize: config[FormattingOptions.FontSize],
269
+ theme,
270
+ wordWrap,
271
+ });
272
+ const monacoLibraryReady$ = interval(100).pipe(
273
+ map(() => window.monaco),
274
+ filter(Boolean),
275
+ first()
276
+ );
277
+ combineLatest({
278
+ monaco: monacoLibraryReady$,
279
+ language: this.libCodingTestService.currentLanguage$,
280
+ })
281
+ .pipe(take(1))
282
+ .subscribe(result => {
283
+ if (result.language === CodeEditorLanguages.Javascript) {
284
+ result.monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
285
+ noSemanticValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
286
+ noSyntaxValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
287
+ });
288
+ } else if (result.language === CodeEditorLanguages.Typescript) {
289
+ result.monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
290
+ noSemanticValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
291
+ noSyntaxValidation: !config[CodingAssistanceOptions.ErrorHighlighting],
292
+ });
293
+ }
294
+ result.monaco.languages.register({ id: CodeEditorLanguages.Erlang });
295
+ result.monaco.languages.setMonarchTokensProvider(CodeEditorLanguages.Erlang, erlangLanguage);
296
+ });
297
+ }
298
+
299
+ private getInitialColorTheme(config: Config, snapshot: CodingSnapshot): string {
300
+ let theme: MonacoColorTheme;
301
+
302
+ if (snapshot) {
303
+ theme = MonacoColorTheme.Dark;
304
+ } else {
305
+ theme = config[FormattingOptions.ColorTheme] === Themes.Default ? MonacoColorTheme.Light : MonacoColorTheme.Dark;
306
+ }
307
+
308
+ return theme;
309
+ }
310
+
311
+ private setupCode(calledFromReset = false) {
312
+ this.handleInitCode(calledFromReset);
313
+ this.publishCode(this.code);
314
+ }
315
+
316
+ private handleInitCode(calledFromReset = false): void {
317
+ if (calledFromReset) {
318
+ this.code = this.libCodingTestService.getInitCode();
319
+ } else if (this.initCode) {
320
+ this.code = this.initCode;
321
+ } else if (this.currentSnapshot) {
322
+ return;
323
+ } else if (this.shouldGenerateInitCode) {
324
+ this.code = this.codeEditorService.getInitCode();
325
+ }
326
+ }
327
+
328
+ private listenUpdates(): void {
329
+ this.libCodingTestService.codeReset$
330
+ .pipe(
331
+ filter(val => val === ResetCodeAction.Confirm),
332
+ untilDestroyed(this)
333
+ )
334
+ .subscribe(() => {
335
+ this.setupCode(true);
336
+ });
337
+ }
338
+
339
+ private handleSnapshot(code: string): void {
340
+ this.code = code;
341
+
342
+ if (!this.editor) {
343
+ return;
344
+ }
345
+
346
+ this.editor.setValue(code); // make sure editor model has the latest value of the code
347
+
348
+ if (this.previousMonacoDecoration) {
349
+ this.previousMonacoDecoration.clear();
350
+ }
351
+
352
+ if (this.currentSnapshot.copied_text) {
353
+ // if the snapshot contains copyPasted code, find it's range in monaco
354
+ const matches = this.editor
355
+ .getModel()
356
+ .findMatches(this.currentSnapshot.copied_text, false, false, true, null, true);
357
+
358
+ if (matches.length) {
359
+ // if the range is found in editor model, decorate it with additional class to highlight on the ui
360
+ this.previousMonacoDecoration = this.editor.createDecorationsCollection([
361
+ {
362
+ range: matches[0].range,
363
+ options: { inlineClassName: 'copy-pasted-code' },
364
+ },
365
+ ]);
366
+ }
367
+ }
368
+ }
369
+ }
@@ -0,0 +1,28 @@
1
+ import { CodeEditorFuncParam } from './helpers/code-editor-helper.model';
2
+ import { editor } from 'monaco-editor';
3
+
4
+ export const functionParamsMock: CodeEditorFuncParam[] = [
5
+ {
6
+ name: 'param1',
7
+ type: 'boolean',
8
+ },
9
+ {
10
+ name: 'param2',
11
+ type: 'int',
12
+ },
13
+ ];
14
+
15
+ export const initCodeMock = 'export function mock(param1, param2) { return param1; }';
16
+
17
+ export const editorMock = {
18
+ onDidPaste: () => ({
19
+ dispose: () => {
20
+ // Mock implementation - no-op
21
+ },
22
+ }),
23
+ } as unknown as editor.ICodeEditor;
24
+
25
+ export const pasteEventMock = {
26
+ type: 'paste',
27
+ data: 'text',
28
+ };
@@ -0,0 +1,160 @@
1
+ import { functionParamsMock } from './code-editor.mocks';
2
+ import { CodeEditorService } from './code-editor.service';
3
+ import { CodeEditorJavascriptHelper } from './helpers';
4
+ import { CodeEditorFuncOptions, CodeEditorLanguages } from './helpers/code-editor-helper.model';
5
+
6
+ describe('CodeEditorService', () => {
7
+ let service: CodeEditorService;
8
+
9
+ const defaultLang = CodeEditorLanguages.Javascript;
10
+ const defaultParams: CodeEditorFuncOptions = {
11
+ params: [...functionParamsMock],
12
+ name: 'mockFunction',
13
+ returnType: 'boolean',
14
+ };
15
+ const jsHelper = new CodeEditorJavascriptHelper(defaultParams);
16
+
17
+ beforeEach(() => {
18
+ service = new CodeEditorService();
19
+ service.setHelper(defaultLang, defaultParams);
20
+ });
21
+
22
+ describe('when setting helper', () => {
23
+ describe('and passing Javascript', () => {
24
+ it('should create helper for corresponding language', () => {
25
+ expect(service.getEditorLang()).toEqual(defaultLang);
26
+ });
27
+ });
28
+
29
+ describe('and passing Typescript', () => {
30
+ it('should create helper for corresponding language', () => {
31
+ service.setHelper(CodeEditorLanguages.Typescript, defaultParams);
32
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Typescript);
33
+ });
34
+ });
35
+
36
+ describe('and passing Php', () => {
37
+ it('should create helper for corresponding language', () => {
38
+ service.setHelper(CodeEditorLanguages.Php, defaultParams);
39
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Php);
40
+ });
41
+ });
42
+
43
+ describe('and passing Python', () => {
44
+ it('should create helper for corresponding language', () => {
45
+ service.setHelper(CodeEditorLanguages.Python, defaultParams);
46
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Python);
47
+ });
48
+ });
49
+
50
+ describe('and passing Java', () => {
51
+ it('should create helper for corresponding language', () => {
52
+ service.setHelper(CodeEditorLanguages.Java, defaultParams);
53
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Java);
54
+ });
55
+ });
56
+
57
+ describe('and passing Ruby', () => {
58
+ it('should create helper for corresponding language', () => {
59
+ service.setHelper(CodeEditorLanguages.Ruby, defaultParams);
60
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Ruby);
61
+ });
62
+ });
63
+
64
+ describe('and passing C', () => {
65
+ it('should create helper for corresponding language', () => {
66
+ service.setHelper(CodeEditorLanguages.C, defaultParams);
67
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.C);
68
+ });
69
+ });
70
+
71
+ describe('and passing C++', () => {
72
+ it('should create helper for corresponding language', () => {
73
+ service.setHelper(CodeEditorLanguages.Cpp, defaultParams);
74
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.CppEditor);
75
+ });
76
+ });
77
+
78
+ describe('and passing R', () => {
79
+ it('should create helper for corresponding language', () => {
80
+ service.setHelper(CodeEditorLanguages.R, defaultParams);
81
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.R);
82
+ });
83
+ });
84
+
85
+ describe('and passing Scala', () => {
86
+ it('should create helper for corresponding language', () => {
87
+ service.setHelper(CodeEditorLanguages.Scala, defaultParams);
88
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Scala);
89
+ });
90
+ });
91
+
92
+ describe('and passing C#', () => {
93
+ it('should create helper for corresponding language', () => {
94
+ service.setHelper(CodeEditorLanguages.CSharp, defaultParams);
95
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.CSharpEditor);
96
+ });
97
+ });
98
+
99
+ describe('and passing C#_legacy', () => {
100
+ it('should create helper for corresponding language', () => {
101
+ service.setHelper(CodeEditorLanguages.CSharpLegacy, defaultParams);
102
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.CSharpEditor);
103
+ });
104
+ });
105
+
106
+ describe('and passing Go', () => {
107
+ it('should create helper for corresponding language', () => {
108
+ service.setHelper(CodeEditorLanguages.Go, defaultParams);
109
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Go);
110
+ });
111
+ });
112
+
113
+ describe('and passing SQL', () => {
114
+ it('should create helper for corresponding language', () => {
115
+ service.setHelper(CodeEditorLanguages.Sql, defaultParams);
116
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Sql);
117
+ });
118
+ });
119
+
120
+ describe('and passing sqlite3', () => {
121
+ it('should create helper for SQL language', () => {
122
+ service.setHelper(CodeEditorLanguages.Sqlite, defaultParams);
123
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Sql);
124
+ });
125
+ });
126
+
127
+ describe('and passing Kotlin', () => {
128
+ it('should create helper for corresponding language', () => {
129
+ service.setHelper(`${CodeEditorLanguages.Kotlin}_1.10`, defaultParams);
130
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Kotlin);
131
+ });
132
+ });
133
+
134
+ describe('and passing Swift', () => {
135
+ it('should create helper for corresponding language', () => {
136
+ service.setHelper(`${CodeEditorLanguages.Swift}_1.10`, defaultParams);
137
+ expect(service.getEditorLang()).toEqual(CodeEditorLanguages.Swift);
138
+ });
139
+ });
140
+
141
+ describe('and passing unknown language', () => {
142
+ it('should throw an error', () => {
143
+ const randomLang = 'randomLang';
144
+ try {
145
+ service.setHelper(randomLang, defaultParams);
146
+ } catch (e) {
147
+ expect(e.message).toEqual(`language ${randomLang} is not supported`);
148
+ }
149
+ });
150
+ });
151
+ });
152
+
153
+ describe('when getting init code', () => {
154
+ it('should return init code', () => {
155
+ const result = service.getInitCode();
156
+
157
+ expect(result).toEqual(jsHelper.getInitCode());
158
+ });
159
+ });
160
+ });