diff --git a/packages/frontend/editor-ui/src/plugins/codemirror/typescript/worker/typescript.worker.test.ts b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/worker/typescript.worker.test.ts index 74fb55d816d..4c36385252c 100644 --- a/packages/frontend/editor-ui/src/plugins/codemirror/typescript/worker/typescript.worker.test.ts +++ b/packages/frontend/editor-ui/src/plugins/codemirror/typescript/worker/typescript.worker.test.ts @@ -1,6 +1,12 @@ +import { type ChangeSet, EditorState } from '@codemirror/state'; +import * as tsvfs from '@typescript/vfs'; +import ts from 'typescript'; +import { mock } from 'vitest-mock-extended'; import type { WorkerInitOptions } from '../types'; import { worker } from './typescript.worker'; -import { type ChangeSet, EditorState } from '@codemirror/state'; + +vi.mock('@typescript/vfs'); +vi.mock('typescript'); async function createWorker({ doc, @@ -16,6 +22,14 @@ function myFunction(){ return $input.all();`; const state = EditorState.create({ doc: doc ?? defaultDoc }); + const mockEnv = mock({ + getSourceFile: vi.fn(() => mock({ getText: () => state.doc.toString() })), + languageService: mock(), + }); + + vi.spyOn(tsvfs, 'createDefaultMapFromCDN').mockResolvedValue(new Map()); + vi.spyOn(tsvfs, 'createVirtualTypeScriptEnvironment').mockResolvedValue(mockEnv); + const tsWorker = worker.init( { allNodeNames: [], @@ -32,12 +46,32 @@ return $input.all();`; params: { path: '', type: 'string', value: '' }, }), ); - return await tsWorker; + return { tsWorker: await tsWorker, mockEnv }; } describe('Typescript Worker', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + it('should return diagnostics', async () => { - const tsWorker = await createWorker(); + const { tsWorker, mockEnv } = await createWorker(); + + vi.mocked(mockEnv.languageService.getSemanticDiagnostics).mockReturnValue([ + mock({ + start: 56, + length: 10, + code: 6133, + messageText: "'myFunction' is declared but its value is never read.", + }), + mock({ + start: 93, + length: 5, + code: 6133, + messageText: "'myObj' is declared but its value is never read.", + }), + ]); + vi.mocked(mockEnv.languageService.getSyntacticDiagnostics).mockReturnValue([]); expect(tsWorker.getDiagnostics()).toEqual([ { @@ -55,10 +89,13 @@ describe('Typescript Worker', () => { to: 52, }, ]); + + expect(mockEnv.languageService.getSemanticDiagnostics).toHaveBeenCalledWith('id.js'); + expect(mockEnv.languageService.getSyntacticDiagnostics).toHaveBeenCalledWith('id.js'); }); it('should accept updates from the client and buffer them', async () => { - const tsWorker = await createWorker(); + const { tsWorker, mockEnv } = await createWorker(); // Add if statement and remove indentation const changes = [ [75, [0, '', ''], 22], @@ -68,49 +105,60 @@ describe('Typescript Worker', () => { ]; vi.useFakeTimers({ toFake: ['setTimeout', 'queueMicrotask', 'nextTick'] }); - + vi.mocked(mockEnv.updateFile).mockReset(); for (const change of changes) { tsWorker.updateFile(change as unknown as ChangeSet); } - expect(tsWorker.getDiagnostics()).toHaveLength(2); + expect(mockEnv.updateFile).not.toHaveBeenCalled(); vi.advanceTimersByTime(1000); vi.runAllTicks(); - expect(tsWorker.getDiagnostics()).toHaveLength(3); - expect(tsWorker.getDiagnostics()).toEqual([ + expect(mockEnv.updateFile).toHaveBeenCalledTimes(1); + expect(mockEnv.updateFile).toHaveBeenCalledWith( + 'id.js', + '\n\nif (true){\n const myObj = {test: "value"}\n}', { - from: 10, - markClass: 'cm-faded', - message: "'myFunction' is declared but its value is never read.", - severity: 'warning', - to: 20, + length: 0, + start: 121, }, - { - from: 47, - markClass: 'cm-faded', - message: "'myObj' is declared but its value is never read.", - severity: 'warning', - to: 52, - }, - { - from: 96, - markClass: 'cm-faded', - message: "'myObj' is declared but its value is never read.", - severity: 'warning', - to: 101, - }, - ]); + ); }); it('should return completions', async () => { const doc = 'return $input.'; - const tsWorker = await createWorker({ doc }); + const { tsWorker, mockEnv } = await createWorker({ doc }); + + vi.mocked(mockEnv.languageService.getCompletionsAtPosition).mockReturnValue( + mock({ + isGlobalCompletion: false, + optionalReplacementSpan: '', + entries: [ + mock({ + name: 'all', + kind: ts.ScriptElementKind.functionElement, + sortText: '10', + }), + mock({ + name: 'first', + kind: ts.ScriptElementKind.functionElement, + sortText: '10', + }), + ], + }), + ); const completionResult = await tsWorker.getCompletionsAtPos(doc.length); assert(completionResult !== null); + expect(completionResult).toEqual({ + isGlobal: false, + result: expect.objectContaining({ + from: 60, + }), + }); + const completionLabels = completionResult.result.options.map((c) => c.label); expect(completionLabels).toContain('all()'); expect(completionLabels).toContain('first()');