diff --git a/src/components/code/AdvancedCodeEditor.tsx b/src/components/code/AdvancedCodeEditor.tsx index 91fef810..99f08fbe 100644 --- a/src/components/code/AdvancedCodeEditor.tsx +++ b/src/components/code/AdvancedCodeEditor.tsx @@ -1,7 +1,8 @@ 'use client'; -import React, { useEffect } from 'react'; +import React, { useMemo } from 'react'; import dynamic from 'next/dynamic'; +import type { EditorProps } from '@monaco-editor/react'; import { Play, RotateCcw, @@ -128,6 +129,30 @@ export const AdvancedCodeEditor: React.FC = ({ }; const isDark = theme === 'vs-dark'; + const editorOptions = useMemo>( + () => ({ + fontSize, + minimap: { enabled: true }, + wordWrap: 'on', + lineNumbers: 'on', + renderLineHighlight: 'all', + scrollBeyondLastLine: false, + automaticLayout: true, + padding: { top: 12, bottom: 12 }, + suggestOnTriggerCharacters: autoCompleteEnabled, + quickSuggestions: autoCompleteEnabled, + tabSize: languageConfig.id === 'python' ? 4 : 2, + detectIndentation: false, + formatOnPaste: true, + smoothScrolling: true, + cursorBlinking: 'expand', + cursorSmoothCaretAnimation: 'on', + bracketPairColorization: { enabled: true }, + fontFamily: '"Fira Code", "Cascadia Code", "Consolas", monospace', + fontLigatures: true, + }), + [autoCompleteEnabled, fontSize, languageConfig.id], + ); return (
= ({ value={code} onChange={(val) => setCode(val ?? '')} onMount={handleEditorMount} - options={{ - fontSize, - minimap: { enabled: true }, - wordWrap: 'on', - lineNumbers: 'on', - renderLineHighlight: 'all', - scrollBeyondLastLine: false, - automaticLayout: true, - padding: { top: 12, bottom: 12 }, - suggestOnTriggerCharacters: autoCompleteEnabled, - quickSuggestions: autoCompleteEnabled, - tabSize: languageConfig.id === 'python' ? 4 : 2, - detectIndentation: false, - formatOnPaste: true, - smoothScrolling: true, - cursorBlinking: 'expand', - cursorSmoothCaretAnimation: 'on', - bracketPairColorization: { enabled: true }, - fontFamily: '"Fira Code", "Cascadia Code", "Consolas", monospace', - fontLigatures: true, - }} + options={editorOptions} height="100%" width="100%" /> diff --git a/src/components/code/__tests__/AdvancedCodeEditor.test.tsx b/src/components/code/__tests__/AdvancedCodeEditor.test.tsx new file mode 100644 index 00000000..bc544b17 --- /dev/null +++ b/src/components/code/__tests__/AdvancedCodeEditor.test.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { fireEvent, render, screen } from '@testing-library/react'; + +const { editorRenders, useCodeEditorMock } = vi.hoisted(() => ({ + editorRenders: [] as Array<{ options: unknown; value?: string }>, + useCodeEditorMock: vi.fn(), +})); + +vi.mock('next/dynamic', () => ({ + default: + () => + ({ + onChange, + options, + value, + }: { + onChange?: (value?: string) => void; + options?: unknown; + value?: string; + }) => { + editorRenders.push({ options, value }); + + return ( + + ); + }, +})); + +vi.mock('@/hooks/useCodeEditor', () => ({ + useCodeEditor: useCodeEditorMock, +})); + +vi.mock('../SyntaxHighlighter', () => ({ + SyntaxHighlighter: () => null, +})); + +vi.mock('../AutoCompletion', () => ({ + AutoCompletion: () => null, +})); + +vi.mock('../CollaborativeEditing', () => ({ + CollaborativeEditing: () => null, +})); + +import { AdvancedCodeEditor } from '../AdvancedCodeEditor'; + +const languageConfig = { + id: 'javascript', + label: 'JavaScript', + extension: 'js', + monacoLanguage: 'javascript', +}; + +describe('AdvancedCodeEditor', () => { + beforeEach(() => { + editorRenders.length = 0; + useCodeEditorMock.mockImplementation(() => { + const [code, setCode] = React.useState('const value = 1;'); + const [fontSize, setFontSize] = React.useState(14); + + return { + code, + language: 'javascript', + theme: 'vs-dark', + fontSize, + isRunning: false, + output: null, + validationErrors: [], + collaborators: [], + isCollaborationConnected: false, + autoCompleteEnabled: true, + currentWord: '', + languages: [languageConfig], + languageConfig, + setCode, + setLanguage: vi.fn(), + toggleTheme: vi.fn(), + increaseFontSize: () => setFontSize((size) => size + 1), + decreaseFontSize: () => setFontSize((size) => size - 1), + runCode: vi.fn(), + handleFormat: vi.fn(), + resetCode: vi.fn(), + clearOutput: vi.fn(), + toggleAutoComplete: vi.fn(), + handleEditorMount: vi.fn(), + }; + }); + }); + + it('keeps options stable for content changes and updates them for editor settings', () => { + render(); + + const initialOptions = editorRenders.at(-1)?.options; + + fireEvent.click(screen.getByTestId('monaco-editor')); + + expect(editorRenders.at(-1)?.value).toBe('const value = 1; updated'); + expect(editorRenders.at(-1)?.options).toBe(initialOptions); + + fireEvent.click(screen.getByTitle('Increase font size')); + + const resizedOptions = editorRenders.at(-1)?.options as { fontSize?: number }; + expect(resizedOptions).not.toBe(initialOptions); + expect(resizedOptions.fontSize).toBe(15); + }); +});