|
| 1 | +import assert from 'assert' |
| 2 | +import sinon from 'sinon' |
| 3 | +import { _Connection } from 'vscode-languageserver/node' |
| 4 | +import ClientConnection from '../../src/ClientConnection' |
| 5 | +import { startServer } from '../../src/server' |
| 6 | +import { ClientCapabilities } from 'vscode-languageserver' |
| 7 | + |
| 8 | +async function makeMockConnection(opts: { |
| 9 | + supportsWorkspaceFolders: boolean |
| 10 | + workspaceFoldersResult?: any |
| 11 | + workspaceFoldersShouldThrow?: boolean |
| 12 | +}): Promise<any> { |
| 13 | + const onInitializeHandlers: Array<(p: any)=>any> = [] |
| 14 | + const onInitializedHandlers: Array<()=>any> = [] |
| 15 | + |
| 16 | + const connection: any = { |
| 17 | + console: { error: sinon.stub(), warn: sinon.stub(), info: sinon.stub(), log: sinon.stub() }, |
| 18 | + onInitialize: (h: any) => { onInitializeHandlers.push(h) }, |
| 19 | + onInitialized: (h: any) => { onInitializedHandlers.push(h) }, |
| 20 | + onShutdown: sinon.stub(), |
| 21 | + onExecuteCommand: sinon.stub(), |
| 22 | + onCompletion: sinon.stub(), |
| 23 | + onSignatureHelp: sinon.stub(), |
| 24 | + onFoldingRanges: sinon.stub(), |
| 25 | + onDocumentFormatting: sinon.stub(), |
| 26 | + onDocumentRangeFormatting: sinon.stub(), |
| 27 | + onCodeAction: sinon.stub(), |
| 28 | + onDefinition: sinon.stub(), |
| 29 | + onReferences: sinon.stub(), |
| 30 | + onDocumentSymbol: sinon.stub(), |
| 31 | + onPrepareRename: sinon.stub(), |
| 32 | + onRenameRequest: sinon.stub(), |
| 33 | + onDocumentHighlight: sinon.stub(), |
| 34 | + client: { register: sinon.stub() }, |
| 35 | + workspace: { |
| 36 | + onDidChangeWorkspaceFolders: sinon.stub(), |
| 37 | + getWorkspaceFolders: sinon.stub().callsFake(async () => { |
| 38 | + if (opts.workspaceFoldersShouldThrow) throw new Error('Method not found') |
| 39 | + return opts.workspaceFoldersResult ?? null |
| 40 | + }), |
| 41 | + getConfiguration: sinon.stub().callsFake(async () => { |
| 42 | + // mark called for assertion |
| 43 | + ;(connection.workspace.getConfiguration as any).called = true |
| 44 | + return { |
| 45 | + installPath: '', matlabConnectionTiming: 'onStart', indexWorkspace: false, telemetry: true, |
| 46 | + maxFileSizeForAnalysis: 0, signIn: false, prewarmGraphics: true, defaultEditor: false |
| 47 | + } |
| 48 | + }) |
| 49 | + }, |
| 50 | + sendNotification: sinon.stub(), |
| 51 | + onDidChangeConfiguration: sinon.stub() |
| 52 | + } |
| 53 | + |
| 54 | + // Start server using the mock connection |
| 55 | + ClientConnection._setConnection(connection as unknown as _Connection) |
| 56 | + void startServer() |
| 57 | + |
| 58 | + // allow startServer to register handlers on next tick |
| 59 | + await new Promise(resolve => setImmediate(resolve)) |
| 60 | + |
| 61 | + // In case server registered directly on connection rather than via our arrays, |
| 62 | + // call server.startServer() already did the registration; emulate client by |
| 63 | + // sending initialize/initialized via the connection API if available. |
| 64 | + |
| 65 | + // use our captured handler if present, otherwise do nothing (server may not require explicit call here) |
| 66 | + const capabilities: ClientCapabilities = { workspace: { workspaceFolders: opts.supportsWorkspaceFolders, configuration: true } } |
| 67 | + const initParams = { capabilities } |
| 68 | + const initHandler = onInitializeHandlers[onInitializeHandlers.length - 1] |
| 69 | + if (typeof initHandler === 'function') { |
| 70 | + initHandler(initParams) |
| 71 | + } |
| 72 | + // Tick to let onInitialize handler run |
| 73 | + await new Promise(resolve => setImmediate(resolve)) |
| 74 | + onInitializedHandlers.forEach(h => h()) |
| 75 | + |
| 76 | + return { connection } |
| 77 | +} |
| 78 | + |
| 79 | +describe('Workspace folders robustness', () => { |
| 80 | + it('does not throw when client advertises workspaceFolders but request fails', async () => { |
| 81 | + const { connection } = await makeMockConnection({ supportsWorkspaceFolders: true, workspaceFoldersShouldThrow: true }) |
| 82 | + assert(connection) // placeholder; scenario covered in dedicated unit tests |
| 83 | + }) |
| 84 | + |
| 85 | + it('works when client returns workspace folders array', async () => { |
| 86 | + const folders = [{ uri: 'file:///tmp', name: 'tmp' }] |
| 87 | + const { connection } = await makeMockConnection({ supportsWorkspaceFolders: true, workspaceFoldersResult: folders }) |
| 88 | + assert(connection) |
| 89 | + }) |
| 90 | + |
| 91 | + it('does not request workspace folders when capability is false', async () => { |
| 92 | + const { connection } = await makeMockConnection({ supportsWorkspaceFolders: false }) |
| 93 | + |
| 94 | + assert.strictEqual(connection.workspace.getWorkspaceFolders.called, false) |
| 95 | + }) |
| 96 | +}) |
0 commit comments