From cbcb0afa7d1fa72f775b5a31784f3b1b12e72602 Mon Sep 17 00:00:00 2001 From: Simon Entholzer <33342534+SimonEntholzer@users.noreply.github.com> Date: Sun, 13 Oct 2024 17:09:29 +0200 Subject: [PATCH 1/3] Development: Fix exam assessment e2e tests failing (#9462) --- src/test/playwright/e2e/exam/ExamAssessment.spec.ts | 2 +- src/test/playwright/e2e/exam/ExamDateVerification.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/playwright/e2e/exam/ExamAssessment.spec.ts b/src/test/playwright/e2e/exam/ExamAssessment.spec.ts index 3bfee439d546..4cd492e86daa 100644 --- a/src/test/playwright/e2e/exam/ExamAssessment.spec.ts +++ b/src/test/playwright/e2e/exam/ExamAssessment.spec.ts @@ -332,7 +332,6 @@ export async function prepareExam(course: Course, end: dayjs.Dayjs, exerciseType gracePeriod: 10, }; exam = await examAPIRequests.createExam(examConfig); - await examAPIRequests.registerStudentForExam(exam, studentOne); let additionalData = {}; switch (exerciseType) { case ExerciseType.PROGRAMMING: @@ -347,6 +346,7 @@ export async function prepareExam(course: Course, end: dayjs.Dayjs, exerciseType } const exercise = await examExerciseGroupCreation.addGroupWithExercise(exam, exerciseType, additionalData); + await examAPIRequests.registerStudentForExam(exam, studentOne); await examAPIRequests.generateMissingIndividualExams(exam); await examAPIRequests.prepareExerciseStartForExam(exam); exercise.additionalData = additionalData; diff --git a/src/test/playwright/e2e/exam/ExamDateVerification.spec.ts b/src/test/playwright/e2e/exam/ExamDateVerification.spec.ts index bf3f981640b0..48d80de67065 100644 --- a/src/test/playwright/e2e/exam/ExamDateVerification.spec.ts +++ b/src/test/playwright/e2e/exam/ExamDateVerification.spec.ts @@ -68,9 +68,9 @@ test.describe('Exam date verification', () => { endDate: dayjs().add(3, 'days'), }; const exam = await examAPIRequests.createExam(examConfig); - await examAPIRequests.registerStudentForExam(exam, studentOne); const exerciseGroup = await examAPIRequests.addExerciseGroupForExam(exam); const exercise = await exerciseAPIRequests.createTextExercise({ exerciseGroup }); + await examAPIRequests.registerStudentForExam(exam, studentOne); await examAPIRequests.generateMissingIndividualExams(exam); await examAPIRequests.prepareExerciseStartForExam(exam); await login(studentOne); @@ -105,9 +105,9 @@ test.describe('Exam date verification', () => { endDate: examEnd, }; const exam = await examAPIRequests.createExam(examConfig); - await examAPIRequests.registerStudentForExam(exam, studentOne); const exerciseGroup = await examAPIRequests.addExerciseGroupForExam(exam); const exercise = await exerciseAPIRequests.createTextExercise({ exerciseGroup }); + await examAPIRequests.registerStudentForExam(exam, studentOne); await examAPIRequests.generateMissingIndividualExams(exam); await examAPIRequests.prepareExerciseStartForExam(exam); await login(studentOne); From f01378426daaac15443a1844409f2cc8ba773d68 Mon Sep 17 00:00:00 2001 From: Paul Rangger <48455539+PaRangger@users.noreply.github.com> Date: Sun, 13 Oct 2024 17:23:34 +0200 Subject: [PATCH 2/3] Communication: Fix user interface reload on channel selection (#9464) --- .../course-conversations.component.ts | 8 +------- .../conversation-messages.component.html | 3 ++- .../conversation-messages.component.scss | 4 ++++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index 159818511aa8..2d56484f8a46 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -156,11 +156,11 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.subscribeToIsCodeOfConductAccepted(); this.subscribeToIsCodeOfConductPresented(); this.subscribeToConversationsOfUser(); - this.subscribeToLoading(); this.updateQueryParameters(); this.prepareSidebarData(); this.metisConversationService.checkIsCodeOfConductAccepted(this.course!); this.isServiceSetUp = true; + this.isLoading = false; } }); @@ -224,12 +224,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } - private subscribeToLoading() { - this.metisConversationService.isLoading$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((isLoading: boolean) => { - this.isLoading = isLoading; - }); - } - acceptCodeOfConduct() { if (this.course) { this.metisConversationService.acceptCodeOfConduct(this.course); diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html index 373d7b8d9694..f2931fa40d90 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html @@ -67,8 +67,9 @@
diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.scss b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.scss index 01f09f46008e..d1beda749f53 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.scss +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.scss @@ -59,4 +59,8 @@ padding-top: 100px; padding-bottom: 100px; } + + .conversation-messages-message-list.is-fetching-posts { + display: none; + } } From e072169c67c82f338a20ae76c7abb0e7f352eca6 Mon Sep 17 00:00:00 2001 From: Patrik Zander <38403547+pzdr7@users.noreply.github.com> Date: Sun, 13 Oct 2024 17:26:27 +0200 Subject: [PATCH 3/3] Programming exercises: Add custom themes for the Monaco editor (#9463) --- .../model/themes/editor-colors.interface.ts | 86 +++++++++++++++++++ ...nguage-token-style-definition.interface.ts | 27 ++++++ .../model/themes/monaco-dark.theme.ts | 47 ++++++++++ .../model/themes/monaco-editor-theme.model.ts | 66 ++++++++++++++ .../model/themes/monaco-light.theme.ts | 46 ++++++++++ .../monaco-theme-definition.interface.ts | 9 ++ .../monaco-editor.component.scss | 5 ++ .../monaco-editor/monaco-editor.service.ts | 33 +++++-- .../monaco-editor/monaco-editor-theme.spec.ts | 67 +++++++++++++++ .../monaco-editor.service.spec.ts | 8 +- 10 files changed, 384 insertions(+), 10 deletions(-) create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/editor-colors.interface.ts create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/language-token-style-definition.interface.ts create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/monaco-dark.theme.ts create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/monaco-editor-theme.model.ts create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/monaco-light.theme.ts create mode 100644 src/main/webapp/app/shared/monaco-editor/model/themes/monaco-theme-definition.interface.ts create mode 100644 src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-theme.spec.ts diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/editor-colors.interface.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/editor-colors.interface.ts new file mode 100644 index 000000000000..a89139f285ee --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/editor-colors.interface.ts @@ -0,0 +1,86 @@ +/** + * Interface for the colors of the editor. + * See https://code.visualstudio.com/api/references/theme-color + * All colors must be in the format '#RRGGBB' or '#RRGGBBAA'. + */ +export interface EditorColors { + /** + * The background color of the editor. + */ + backgroundColor?: string; + /** + * The default color of all text in the editor, not including syntax highlighting. + */ + foregroundColor?: string; + /** + * Colors for line numbers in the editor. + */ + lineNumbers?: { + /** + * The color of the line numbers. + */ + foregroundColor?: string; + /** + * The color of the line number of the line that the cursor is on. + */ + activeForegroundColor?: string; + /** + * The color of the line numbers for dimmed lines. This is used for the final newline of the code. + */ + dimmedForegroundColor?: string; + }; + /** + * Colors for the active line highlight in the editor. + */ + lineHighlight?: { + /** + * The color used as the background color for the cursor's current line. + */ + backgroundColor?: string; + /** + * The color used for the border of the cursor's current line. + */ + borderColor?: string; + }; + /** + * Colors for the diff editor. + */ + diff?: { + /** + * The background color for inserted lines in the diff editor. + */ + insertedLineBackgroundColor?: string; + /** + * The background color for inserted text in the diff editor. + * This will overlap with the `insertedLineBackgroundColor`. + */ + insertedTextBackgroundColor?: string; + /** + * The background color for removed lines in the diff editor. + */ + removedTextBackgroundColor?: string; + /** + * The background color for removed text in the diff editor. + * This will overlap with the `removedLineBackgroundColor`. + */ + removedLineBackgroundColor?: string; + /** + * The color used for the diagonal fill in the diff editor. + * This is used when the diff editor pads the length of the files to align the lines of the original and modified files. + */ + diagonalFillColor?: string; + /** + * Colors for the diff editor gutter. This is the area to the left of the editor that shows the line numbers. + */ + gutter?: { + /** + * The background color for inserted lines in the diff editor gutter. + */ + insertedLineBackgroundColor?: string; + /** + * The background color for removed lines in the diff editor gutter. + */ + removedLineBackgroundColor?: string; + }; + }; +} diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/language-token-style-definition.interface.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/language-token-style-definition.interface.ts new file mode 100644 index 000000000000..d811b868ad58 --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/language-token-style-definition.interface.ts @@ -0,0 +1,27 @@ +/** + * Interface for the style of a token in a language. + * The editor applies these styles to the tokens in the specified language (or all languages), e.g. identifiers, keywords, etc. + */ +export interface LanguageTokenStyleDefinition { + /** + * The token to style, e.g. identifier + */ + token: string; + /** + * The language ID for which the token style should be applied. + * If not specified, the style is applied to all languages. + */ + languageId?: string; + /** + * The color of the text that should be applied to the token. + */ + foregroundColor?: string; + /** + * The background color that should be applied to the token. + */ + backgroundColor?: string; + /** + * The font style that should be applied to the token. + */ + fontStyle?: 'italic' | 'bold' | 'underline'; +} diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-dark.theme.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-dark.theme.ts new file mode 100644 index 000000000000..24dc4969284e --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-dark.theme.ts @@ -0,0 +1,47 @@ +import { MonacoThemeDefinition } from 'app/shared/monaco-editor/model/themes/monaco-theme-definition.interface'; + +export const MONACO_DARK_THEME_DEFINITION: MonacoThemeDefinition = { + id: 'custom-dark', + baseTheme: 'vs-dark', + tokenStyles: [ + { + token: 'keyword', + foregroundColor: '#ff7b72', + }, + { + token: 'comment', + foregroundColor: '#9198a1', + }, + { + token: 'string', + foregroundColor: '#a5d6ff', + }, + { + token: 'number', + foregroundColor: '#79c0ff', + }, + ], + editorColors: { + backgroundColor: '#181a18', + lineHighlight: { + borderColor: '#00000000', + backgroundColor: '#282a2e', + }, + lineNumbers: { + foregroundColor: '#ffffff', + activeForegroundColor: '#ffffff', + dimmedForegroundColor: '#ffffff', + }, + diff: { + insertedLineBackgroundColor: '#2ea04326', + insertedTextBackgroundColor: '#2ea04326', + removedLineBackgroundColor: '#f8514926', + removedTextBackgroundColor: '#f8514946', + diagonalFillColor: '#00000000', + gutter: { + insertedLineBackgroundColor: '#3fb9504d', + removedLineBackgroundColor: '#f851494d', + }, + }, + }, +}; diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-editor-theme.model.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-editor-theme.model.ts new file mode 100644 index 000000000000..c9f34f06b3de --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-editor-theme.model.ts @@ -0,0 +1,66 @@ +import { MonacoThemeDefinition } from 'app/shared/monaco-editor/model/themes/monaco-theme-definition.interface'; +import * as monaco from 'monaco-editor'; + +export class MonacoEditorTheme { + constructor(private readonly themeDefinition: MonacoThemeDefinition) {} + + getId(): string { + return this.themeDefinition.id; + } + + /** + * Creates a new record without any entries that have a value of `undefined`. + * @param record The record whose keys to filter. + * @returns The new record, only containing keys with defined values. + * @private + */ + private getRecordWithoutUndefinedEntries(record: Record): Record { + const result: Record = {}; + for (const [key, value] of Object.entries(record)) { + if (value !== undefined) { + result[key] = value; + } + } + return result; + } + + register(): void { + const colorDefinitions = this.themeDefinition.editorColors; + // The color keys are available here: https://code.visualstudio.com/api/references/theme-color + const colors = { + 'editor.background': colorDefinitions.backgroundColor, + 'editor.foreground': colorDefinitions.foregroundColor, + 'editorLineNumber.foreground': colorDefinitions.lineNumbers?.foregroundColor, + 'editorLineNumber.activeForeground': colorDefinitions.lineNumbers?.activeForegroundColor, + 'editorLineNumber.dimmedForeground': colorDefinitions.lineNumbers?.dimmedForegroundColor, + 'editor.lineHighlightBackground': colorDefinitions.lineHighlight?.backgroundColor, + 'editor.lineHighlightBorder': colorDefinitions.lineHighlight?.borderColor, + 'diffEditor.insertedLineBackground': colorDefinitions.diff?.insertedLineBackgroundColor, + 'diffEditor.insertedTextBackground': colorDefinitions.diff?.insertedTextBackgroundColor, + 'diffEditor.removedTextBackground': colorDefinitions.diff?.removedTextBackgroundColor, + 'diffEditor.removedLineBackground': colorDefinitions.diff?.removedLineBackgroundColor, + 'diffEditor.diagonalFill': colorDefinitions.diff?.diagonalFillColor, + 'diffEditorGutter.insertedLineBackground': colorDefinitions.diff?.gutter?.insertedLineBackgroundColor, + 'diffEditorGutter.removedLineBackground': colorDefinitions.diff?.gutter?.removedLineBackgroundColor, + }; + + const tokenStyleDefinitions = this.themeDefinition.tokenStyles; + const rules = tokenStyleDefinitions.map((tokenDefinition) => { + // Language-specific tokens have the key `token.languageId`, e.g. keyword.custom-md + return { + token: `${tokenDefinition.token}${tokenDefinition.languageId ? '.' + tokenDefinition.languageId : ''}`, + foreground: tokenDefinition.foregroundColor, + background: tokenDefinition.backgroundColor, + fontStyle: tokenDefinition.fontStyle, + }; + }); + + // We cannot pass undefined colors to Monaco, so we filter them out to preserve the default values. + monaco.editor.defineTheme(this.getId(), { + base: this.themeDefinition.baseTheme, + inherit: true, + rules: rules, + colors: this.getRecordWithoutUndefinedEntries(colors), + }); + } +} diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-light.theme.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-light.theme.ts new file mode 100644 index 000000000000..53d265ab5766 --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-light.theme.ts @@ -0,0 +1,46 @@ +import { MonacoThemeDefinition } from 'app/shared/monaco-editor/model/themes/monaco-theme-definition.interface'; + +export const MONACO_LIGHT_THEME_DEFINITION: MonacoThemeDefinition = { + id: 'custom-light', + baseTheme: 'vs', + tokenStyles: [ + { + token: 'keyword', + foregroundColor: '#cf222e', + }, + { + token: 'comment', + foregroundColor: '#59636e', + }, + { + token: 'string', + foregroundColor: '#0a3069', + }, + { + token: 'number', + foregroundColor: '#0550ae', + }, + ], + editorColors: { + lineHighlight: { + borderColor: '#00000000', + backgroundColor: '#e8e8e8', + }, + lineNumbers: { + foregroundColor: '#000000', + activeForegroundColor: '#000000', + dimmedForegroundColor: '#000000', + }, + diff: { + insertedLineBackgroundColor: '#dafbe1e6', + insertedTextBackgroundColor: '#aceebbe6', + removedLineBackgroundColor: '#ffebe9ef', + removedTextBackgroundColor: '#ff818250', + diagonalFillColor: '#00000000', + gutter: { + insertedLineBackgroundColor: '#d1f8d9', + removedLineBackgroundColor: '#ffcecb', + }, + }, + }, +}; diff --git a/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-theme-definition.interface.ts b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-theme-definition.interface.ts new file mode 100644 index 000000000000..0de6563077a1 --- /dev/null +++ b/src/main/webapp/app/shared/monaco-editor/model/themes/monaco-theme-definition.interface.ts @@ -0,0 +1,9 @@ +import { LanguageTokenStyleDefinition } from 'app/shared/monaco-editor/model/themes/language-token-style-definition.interface'; +import { EditorColors } from 'app/shared/monaco-editor/model/themes/editor-colors.interface'; + +export interface MonacoThemeDefinition { + id: string; + baseTheme: 'vs' | 'vs-dark' | 'hc-light' | 'hc-black'; + tokenStyles: LanguageTokenStyleDefinition[]; + editorColors: EditorColors; +} diff --git a/src/main/webapp/app/shared/monaco-editor/monaco-editor.component.scss b/src/main/webapp/app/shared/monaco-editor/monaco-editor.component.scss index 96f11784298f..6886fcfcc0dc 100644 --- a/src/main/webapp/app/shared/monaco-editor/monaco-editor.component.scss +++ b/src/main/webapp/app/shared/monaco-editor/monaco-editor.component.scss @@ -3,6 +3,11 @@ .monaco-editor-container { width: 100%; height: 100%; + + .monaco-editor { + // Disables the focus border around the editor. + outline: none; + } } /* diff --git a/src/main/webapp/app/shared/monaco-editor/monaco-editor.service.ts b/src/main/webapp/app/shared/monaco-editor/monaco-editor.service.ts index b16cb7cf6b18..c83734732223 100644 --- a/src/main/webapp/app/shared/monaco-editor/monaco-editor.service.ts +++ b/src/main/webapp/app/shared/monaco-editor/monaco-editor.service.ts @@ -3,6 +3,9 @@ import * as monaco from 'monaco-editor'; import { CUSTOM_MARKDOWN_CONFIG, CUSTOM_MARKDOWN_LANGUAGE, CUSTOM_MARKDOWN_LANGUAGE_ID } from 'app/shared/monaco-editor/model/languages/monaco-custom-markdown.language'; import { Theme, ThemeService } from 'app/core/theme/theme.service'; import { toSignal } from '@angular/core/rxjs-interop'; +import { MONACO_LIGHT_THEME_DEFINITION } from 'app/shared/monaco-editor/model/themes/monaco-light.theme'; +import { MonacoEditorTheme } from 'app/shared/monaco-editor/model/themes/monaco-editor-theme.model'; +import { MONACO_DARK_THEME_DEFINITION } from 'app/shared/monaco-editor/model/themes/monaco-dark.theme'; /** * Service providing shared functionality for the Monaco editor. @@ -11,29 +14,41 @@ import { toSignal } from '@angular/core/rxjs-interop'; */ @Injectable({ providedIn: 'root' }) export class MonacoEditorService { - static readonly LIGHT_THEME_ID = 'vs'; - static readonly DARK_THEME_ID = 'vs-dark'; - private readonly themeService: ThemeService = inject(ThemeService); private readonly currentTheme = toSignal(this.themeService.getCurrentThemeObservable(), { requireSync: true }); + private lightTheme: MonacoEditorTheme; + private darkTheme: MonacoEditorTheme; + constructor() { - monaco.languages.register({ id: CUSTOM_MARKDOWN_LANGUAGE_ID }); - monaco.languages.setLanguageConfiguration(CUSTOM_MARKDOWN_LANGUAGE_ID, CUSTOM_MARKDOWN_CONFIG); - monaco.languages.setMonarchTokensProvider(CUSTOM_MARKDOWN_LANGUAGE_ID, CUSTOM_MARKDOWN_LANGUAGE); + this.registerCustomThemes(); + this.registerCustomMarkdownLanguage(); effect(() => { this.applyTheme(this.currentTheme()); }); } + private registerCustomThemes(): void { + this.lightTheme = new MonacoEditorTheme(MONACO_LIGHT_THEME_DEFINITION); + this.darkTheme = new MonacoEditorTheme(MONACO_DARK_THEME_DEFINITION); + this.lightTheme.register(); + this.darkTheme.register(); + } + + private registerCustomMarkdownLanguage(): void { + monaco.languages.register({ id: CUSTOM_MARKDOWN_LANGUAGE_ID }); + monaco.languages.setLanguageConfiguration(CUSTOM_MARKDOWN_LANGUAGE_ID, CUSTOM_MARKDOWN_CONFIG); + monaco.languages.setMonarchTokensProvider(CUSTOM_MARKDOWN_LANGUAGE_ID, CUSTOM_MARKDOWN_LANGUAGE); + } + /** * Applies the given theme to the Monaco editor. * @param artemisTheme The theme to apply. * @private */ private applyTheme(artemisTheme: Theme): void { - monaco.editor.setTheme(artemisTheme === Theme.LIGHT ? MonacoEditorService.LIGHT_THEME_ID : MonacoEditorService.DARK_THEME_ID); + monaco.editor.setTheme(artemisTheme === Theme.LIGHT ? this.lightTheme.getId() : this.darkTheme.getId()); } /** @@ -79,6 +94,10 @@ export class MonacoEditorService { hideUnchangedRegions: { enabled: true, }, + guides: { + indentation: false, + }, + renderLineHighlight: 'none', fontSize: 12, }); } diff --git a/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-theme.spec.ts b/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-theme.spec.ts new file mode 100644 index 000000000000..da4a35cbdf18 --- /dev/null +++ b/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-theme.spec.ts @@ -0,0 +1,67 @@ +import * as monaco from 'monaco-editor'; +import { EditorColors } from 'app/shared/monaco-editor/model/themes/editor-colors.interface'; +import { LanguageTokenStyleDefinition } from 'app/shared/monaco-editor/model/themes/language-token-style-definition.interface'; +import { MonacoThemeDefinition } from 'app/shared/monaco-editor/model/themes/monaco-theme-definition.interface'; +import { MonacoEditorTheme } from 'app/shared/monaco-editor/model/themes/monaco-editor-theme.model'; + +describe('MonacoEditorTheme', () => { + const colorDefinitions: EditorColors = { + backgroundColor: '#181a18', + foregroundColor: '#ffffff', + diff: { + insertedLineBackgroundColor: '#2ea04326', + insertedTextBackgroundColor: '#2ea04326', + removedLineBackgroundColor: undefined, // Explicit undefined to test that it is removed before being passed to Monaco + removedTextBackgroundColor: undefined, + }, + }; + + const tokenStyleDefinitions: LanguageTokenStyleDefinition[] = [ + { + token: 'keyword', + foregroundColor: '#ff7b72', + }, + { + token: 'keyword', + languageId: 'custom-language-id', + foregroundColor: '#ffffff', + }, + ]; + + const themeDefinition: MonacoThemeDefinition = { + id: 'test-theme', + baseTheme: 'vs', + tokenStyles: tokenStyleDefinitions, + editorColors: colorDefinitions, + }; + + it('should correctly register a theme', () => { + const theme = new MonacoEditorTheme(themeDefinition); + const defineThemeSpy = jest.spyOn(monaco.editor, 'defineTheme'); + theme.register(); + expect(defineThemeSpy).toHaveBeenCalledExactlyOnceWith('test-theme', { + base: 'vs', + inherit: true, + rules: [ + { + token: 'keyword', + foreground: '#ff7b72', + background: undefined, + fontStyle: undefined, + }, + { + token: 'keyword.custom-language-id', + foreground: '#ffffff', + background: undefined, + fontStyle: undefined, + }, + ], + colors: { + 'editor.background': '#181a18', + 'editor.foreground': '#ffffff', + 'diffEditor.insertedLineBackground': '#2ea04326', + 'diffEditor.insertedTextBackground': '#2ea04326', + }, + }); + }); +}); diff --git a/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.service.spec.ts b/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.service.spec.ts index 82ffd4e2b8b3..1a5a59e69436 100644 --- a/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.service.spec.ts +++ b/src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.service.spec.ts @@ -6,6 +6,8 @@ import { ArtemisTestModule } from '../../../test.module'; import { CUSTOM_MARKDOWN_LANGUAGE_ID } from 'app/shared/monaco-editor/model/languages/monaco-custom-markdown.language'; import { BehaviorSubject } from 'rxjs'; import { MockResizeObserver } from '../../../helpers/mocks/service/mock-resize-observer'; +import { MONACO_LIGHT_THEME_DEFINITION } from 'app/shared/monaco-editor/model/themes/monaco-light.theme'; +import { MONACO_DARK_THEME_DEFINITION } from 'app/shared/monaco-editor/model/themes/monaco-dark.theme'; describe('MonacoEditorService', () => { let monacoEditorService: MonacoEditorService; @@ -40,17 +42,17 @@ describe('MonacoEditorService', () => { it('should correctly handle themes', () => { // Initialization: The editor should be in light mode since that is what we initialized the themeSubject with - expect(setThemeSpy).toHaveBeenCalledExactlyOnceWith(MonacoEditorService.LIGHT_THEME_ID); + expect(setThemeSpy).toHaveBeenCalledExactlyOnceWith(MONACO_LIGHT_THEME_DEFINITION.id); // Switch to dark theme themeSubject.next(Theme.DARK); TestBed.flushEffects(); expect(setThemeSpy).toHaveBeenCalledTimes(2); - expect(setThemeSpy).toHaveBeenNthCalledWith(2, MonacoEditorService.DARK_THEME_ID); + expect(setThemeSpy).toHaveBeenNthCalledWith(2, MONACO_DARK_THEME_DEFINITION.id); // Switch back to light theme themeSubject.next(Theme.LIGHT); TestBed.flushEffects(); expect(setThemeSpy).toHaveBeenCalledTimes(3); - expect(setThemeSpy).toHaveBeenNthCalledWith(3, MonacoEditorService.LIGHT_THEME_ID); + expect(setThemeSpy).toHaveBeenNthCalledWith(3, MONACO_LIGHT_THEME_DEFINITION.id); }); it.each([