Skip to content

Commit df2d27d

Browse files
committed
Fix editorDipose triggered to often, fixed and extended react tests
1 parent 9328570 commit df2d27d

File tree

4 files changed

+171
-31
lines changed

4 files changed

+171
-31
lines changed

packages/examples/src/langium/statemachine/main-react.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ export const runStatemachineReact = async (noControls: boolean) => {
5252
return (
5353
<>
5454
<div>
55-
<button
56-
style={{background: 'purple'}} onClick={() => setTestStateButton(testStateButton + '\n// comment')}
57-
>Change Text</button>
55+
<button style={{background: 'purple'}} onClick={() => setTestStateButton(testStateButton + '\n// comment')}>Change Text</button>
5856
<MonacoEditorReactComp
5957
style={{ 'height': '50vh' }}
6058
vscodeApiConfig={appConfig.vscodeApiConfig}

packages/wrapper-react/src/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,11 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
222222
}, [editorAppConfig]);
223223

224224
const handleEditorDispose = async () => {
225-
await editorAppRef.current?.dispose();
226-
editorAppRef.current = undefined;
227-
onDisposeEditor?.();
225+
if (editorAppRef.current !== undefined) {
226+
await editorAppRef.current.dispose();
227+
editorAppRef.current = undefined;
228+
onDisposeEditor?.();
229+
}
228230
};
229231

230232
useEffect(() => {

packages/wrapper-react/test/index.strictmode.test.tsx

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { LogLevel } from '@codingame/monaco-vscode-api';
77
import { render, type RenderResult } from '@testing-library/react';
88
import { MonacoEditorReactComp } from '@typefox/monaco-editor-react';
99
import { delayExecution } from 'monaco-languageclient/common';
10+
import type { EditorApp, TextContents } from 'monaco-languageclient/editorApp';
1011
import { type LanguageClientManager } from 'monaco-languageclient/lcwrapper';
1112
import { type MonacoVscodeApiConfig } from 'monaco-languageclient/vscodeApiWrapper';
12-
import React, { StrictMode } from 'react';
13+
import React, { StrictMode, useState } from 'react';
1314
import { describe, expect, test } from 'vitest';
1415
import { cleanHtmlBody, createDefaultEditorAppConfig, createDefaultLanguageClientConfig, Deferred, unmountDelayMs } from './support/helper.js';
1516

@@ -218,5 +219,113 @@ describe('Test MonacoEditorReactComp', () => {
218219
cleanHtmlBody();
219220
});
220221

222+
test.sequential('srict mode: test render, modifiedTextValue', async () => {
223+
const code = 'const text = "Hello World!";';
224+
const codeUpdated = 'const text = "Goodbye World!";';
225+
226+
let editorApp: EditorApp | undefined;
227+
228+
const deferredStart = new Deferred();
229+
const deferredChanged = new Deferred();
230+
let modified;
231+
let count = 0;
232+
233+
const App = () => {
234+
const [testState, setTestState] = useState<string>(code);
235+
236+
const editorAppConfig = createDefaultEditorAppConfig({
237+
modified: {
238+
text: code,
239+
uri: `/workspace/${expect.getState().testPath}.js`
240+
}
241+
});
242+
243+
return (
244+
<StrictMode>
245+
<button id='change-button' style={{background: 'purple'}} onClick={() => setTestState(codeUpdated)}>Change Text</button>
246+
<MonacoEditorReactComp
247+
vscodeApiConfig={vscodeApiConfig}
248+
editorAppConfig={editorAppConfig}
249+
style={{ 'height': '800px' }}
250+
onTextChanged={async (textChanges: TextContents) => {
251+
modified = textChanges.modified;
252+
count++;
253+
if (codeUpdated === modified) {
254+
deferredChanged.resolve();
255+
}
256+
}}
257+
onEditorStartDone={(editorAppPassed?: EditorApp) => {
258+
editorApp = editorAppPassed;
259+
deferredStart.resolve();
260+
}}
261+
modifiedTextValue={testState}
262+
/>
263+
</StrictMode>
264+
);
265+
};
266+
const renderResult = render(<App />);
267+
await expect(await deferredStart.promise).toBeUndefined();
268+
269+
// delay execute/click, so await below is already awaiting the deferredDispose
270+
setTimeout(() => {
271+
document.getElementById('change-button')?.click();
272+
}, unmountDelayMs);
273+
274+
await expect(await deferredChanged.promise).toBeUndefined();
275+
await expect(count).toBe(2);
276+
await expect(editorApp?.getEditor()?.getValue()).toBe(codeUpdated);
277+
278+
renderResult.unmount();
279+
280+
cleanHtmlBody();
281+
});
282+
283+
test.sequential('strictmode: test state change effects editorconfig', async () => {
284+
const deferredStart = new Deferred();
285+
const deferredDispose = new Deferred();
286+
const App = () => {
287+
const code = 'const text = "Hello World!";';
288+
const [testState, setTestState] = useState<string>(code);
289+
290+
const editorAppConfig = createDefaultEditorAppConfig({
291+
modified: {
292+
text: testState,
293+
uri: `/workspace/${expect.getState().testPath}.js`
294+
}
295+
});
296+
297+
return (
298+
<StrictMode>
299+
<button id='change-button' style={{background: 'purple'}} onClick={() => setTestState(testState + '\n// comment')}>Change Text</button>
300+
<MonacoEditorReactComp
301+
vscodeApiConfig={vscodeApiConfig}
302+
editorAppConfig={editorAppConfig}
303+
style={{ 'height': '800px' }}
304+
onEditorStartDone={() => deferredStart.resolve()}
305+
onDisposeEditor={() => {
306+
console.log('Editor disposed');
307+
deferredDispose.resolve();
308+
}}
309+
/>
310+
</StrictMode>
311+
);
312+
};
313+
314+
const renderResult = render(<App />);
315+
316+
await expect(await deferredStart.promise).toBeUndefined();
317+
expect(document.getElementsByClassName('monaco-editor').length).toBe(1);
318+
319+
// delay execute/click, so await below is already awaiting the deferredDispose
320+
setTimeout(() => {
321+
document.getElementById('change-button')?.click();
322+
}, unmountDelayMs);
323+
await expect(await deferredDispose.promise).toBeUndefined();
324+
325+
renderResult.unmount();
326+
327+
cleanHtmlBody();
328+
});
329+
221330
});
222331

packages/wrapper-react/test/index.test.tsx

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -324,34 +324,60 @@ describe('Test MonacoEditorReactComp', () => {
324324
test.sequential('test render, modifiedTextValue', async () => {
325325
const code = 'const text = "Hello World!";';
326326
const codeUpdated = 'const text = "Goodbye World!";';
327-
const editorAppConfig = createDefaultEditorAppConfig({
328-
modified: {
329-
text: code,
330-
uri: `/workspace/${expect.getState().testPath}.js`
331-
}
332-
});
333327

334328
let editorApp: EditorApp | undefined;
335329

336-
const deferred = new Deferred();
337-
render(<MonacoEditorReactComp
338-
vscodeApiConfig={vscodeApiConfig}
339-
editorAppConfig={editorAppConfig}
340-
style={{ 'height': '800px' }}
341-
onTextChanged={async (textChanges: TextContents) => {
342-
const modified = textChanges.modified;
330+
const deferredStart = new Deferred();
331+
const deferredChanged = new Deferred();
332+
let modified;
333+
let count = 0;
343334

344-
await expect(modified).toBeOneOf([code, codeUpdated]);
345-
if (editorApp !== undefined) {
346-
await expect(editorApp.getEditor()?.getValue()).toBeOneOf([code, codeUpdated]);
335+
const App = () => {
336+
const [testState, setTestState] = useState<string>(code);
337+
338+
const editorAppConfig = createDefaultEditorAppConfig({
339+
modified: {
340+
text: code,
341+
uri: `/workspace/${expect.getState().testPath}.js`
347342
}
348-
}}
349-
onEditorStartDone={(editorAppPassed?: EditorApp) => {
350-
editorApp = editorAppPassed;
351-
deferred.resolve();
352-
}}
353-
modifiedTextValue={codeUpdated} />);
354-
await expect(await deferred.promise).toBeUndefined();
343+
});
344+
345+
return (
346+
<>
347+
<button id='change-button' style={{background: 'purple'}} onClick={() => setTestState(codeUpdated)}>Change Text</button>
348+
<MonacoEditorReactComp
349+
vscodeApiConfig={vscodeApiConfig}
350+
editorAppConfig={editorAppConfig}
351+
style={{ 'height': '800px' }}
352+
onTextChanged={async (textChanges: TextContents) => {
353+
modified = textChanges.modified;
354+
count++;
355+
if (codeUpdated === modified) {
356+
deferredChanged.resolve();
357+
}
358+
}}
359+
onEditorStartDone={(editorAppPassed?: EditorApp) => {
360+
editorApp = editorAppPassed;
361+
deferredStart.resolve();
362+
}}
363+
modifiedTextValue={testState}
364+
/>
365+
</>
366+
);
367+
};
368+
const renderResult = render(<App />);
369+
await expect(await deferredStart.promise).toBeUndefined();
370+
371+
// delay execute/click, so await below is already awaiting the deferredDispose
372+
setTimeout(() => {
373+
document.getElementById('change-button')?.click();
374+
}, unmountDelayMs);
375+
376+
await expect(await deferredChanged.promise).toBeUndefined();
377+
await expect(count).toBe(2);
378+
await expect(editorApp?.getEditor()?.getValue()).toBe(codeUpdated);
379+
380+
renderResult.unmount();
355381

356382
cleanHtmlBody();
357383
});
@@ -378,14 +404,19 @@ describe('Test MonacoEditorReactComp', () => {
378404
editorAppConfig={editorAppConfig}
379405
style={{ 'height': '800px' }}
380406
onEditorStartDone={() => deferredStart.resolve()}
381-
onDisposeEditor={() => deferredDispose.resolve()}
407+
onDisposeEditor={() => {
408+
console.log('Editor disposed');
409+
deferredDispose.resolve();
410+
}}
382411
/>
383412
</>
384413
);
385414
};
386415

387416
const renderResult = render(<App />);
417+
388418
await expect(await deferredStart.promise).toBeUndefined();
419+
expect(document.getElementsByClassName('monaco-editor').length).toBe(1);
389420

390421
// delay execute/click, so await below is already awaiting the deferredDispose
391422
setTimeout(() => {

0 commit comments

Comments
 (0)