UNPKG

graphql-language-service-server

Version:
202 lines (194 loc) 5.11 kB
import mockfs from 'mock-fs'; import { MessageProcessor } from '../../MessageProcessor'; import { Logger as VSCodeLogger } from 'vscode-jsonrpc'; import { URI } from 'vscode-uri'; import { FileChangeType } from 'vscode-languageserver'; import { FileChangeTypeKind } from 'graphql-language-service'; export type MockFile = [filename: string, text: string]; export class MockLogger implements VSCodeLogger { error = jest.fn(); warn = jest.fn(); info = jest.fn(); log = jest.fn(); } // when using mockfs with cosmic-config, a dynamic inline // require of parse-json creates the necessity for loading in the actual // modules to the mocked filesystem const modules = [ 'parse-json', 'error-ex', 'is-arrayish', 'json-parse-even-better-errors', 'lines-and-columns', '@babel/code-frame', '@babel/highlight', // these i think are just required by jest when you console log from a test 'jest-message-util', 'stack-utils', 'pretty-format', 'ansi-regex', 'js-tokens', 'escape-string-regexp', 'jest-worker', 'jiti', 'cosmiconfig', 'minimatch', 'tslib', ]; const defaultMocks = modules.reduce((acc, module) => { acc[`node_modules/${module}`] = mockfs.load(`node_modules/${module}`); return acc; }, {}); type File = [filename: string, text: string]; type Files = File[]; export class MockProject { private root: string; private fileCache: Map<string, string>; private messageProcessor: MessageProcessor; constructor({ files = [], root = '/tmp/test', settings, }: { files: Files; root?: string; settings?: Record<string, any>; }) { this.root = root; this.fileCache = new Map(files); this.mockFiles(); this.messageProcessor = new MessageProcessor({ connection: { get workspace() { return { async getConfiguration() { return settings; }, }; }, }, logger: new MockLogger(), loadConfigOptions: { rootDir: root, }, }); } public async init(filename?: string, fileText?: string) { await this.lsp.handleInitializeRequest({ rootPath: this.root, rootUri: this.root, capabilities: {}, processId: 200, workspaceFolders: null, }); return this.lsp.handleDidOpenOrSaveNotification({ textDocument: { uri: this.uri(filename || 'query.graphql'), version: 1, text: this.fileCache.get('query.graphql') || (filename && this.fileCache.get(filename)) || fileText, }, }); } private mockFiles() { const mockFiles = { ...defaultMocks, // without this, the generated schema file may not be cleaned up by previous tests '/tmp/graphql-language-service': mockfs.directory(), }; for (const [filename, text] of this.fileCache) { mockFiles[this.filePath(filename)] = text; } mockfs(mockFiles); } public filePath(filename: string) { return `${this.root}/${filename}`; } public uri(filename: string) { return URI.file(this.filePath(filename)).toString(); } changeFile(filename: string, text: string) { this.fileCache.set(filename, text); this.mockFiles(); } async addFile(filename: string, text: string, watched = false) { this.fileCache.set(filename, text); this.mockFiles(); if (watched) { await this.lsp.handleWatchedFilesChangedNotification({ changes: [ { uri: this.uri(filename), type: FileChangeTypeKind.Created, }, ], }); } await this.lsp.handleDidChangeNotification({ contentChanges: [ { type: FileChangeTypeKind.Created, text, }, ], textDocument: { uri: this.uri(filename), version: 2, }, }); } async changeWatchedFile(filename: string, text: string) { this.changeFile(filename, text); await this.lsp.handleWatchedFilesChangedNotification({ changes: [ { uri: this.uri(filename), type: FileChangeType.Changed, }, ], }); } async saveOpenFile(filename: string, text: string) { this.changeFile(filename, text); await this.lsp.handleDidOpenOrSaveNotification({ textDocument: { uri: this.uri(filename), version: 2, text, }, }); } async addWatchedFile(filename: string, text: string) { this.changeFile(filename, text); await this.lsp.handleDidChangeNotification({ contentChanges: [ { type: FileChangeTypeKind.Created, text, }, ], textDocument: { uri: this.uri(filename), version: 2, }, }); } async deleteFile(filename: string) { mockfs.restore(); this.fileCache.delete(filename); this.mockFiles(); await this.lsp.handleWatchedFilesChangedNotification({ changes: [ { type: FileChangeType.Deleted, uri: this.uri(filename), }, ], }); } get lsp() { return this.messageProcessor; } }