• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import { expect } from "chai";
2
3import * as ts from "../../_namespaces/ts";
4import * as Harness from "../../_namespaces/Harness";
5import * as Utils from "../../_namespaces/Utils";
6
7let lastWrittenToHost: string;
8const noopFileWatcher: ts.FileWatcher = { close: ts.noop };
9const mockHost: ts.server.ServerHost = {
10    args: [],
11    newLine: "\n",
12    useCaseSensitiveFileNames: true,
13    write(s): void { lastWrittenToHost = s; },
14    readFile: ts.returnUndefined,
15    writeFile: ts.noop,
16    resolvePath(): string { return undefined!; }, // TODO: GH#18217
17    fileExists: () => false,
18    directoryExists: () => false,
19    getDirectories: () => [],
20    createDirectory: ts.noop,
21    getExecutingFilePath(): string { return ""; },
22    getCurrentDirectory(): string { return ""; },
23    getEnvironmentVariable(): string { return ""; },
24    readDirectory() { return []; },
25    exit: ts.noop,
26    setTimeout() { return 0; },
27    clearTimeout: ts.noop,
28    setImmediate: () => 0,
29    clearImmediate: ts.noop,
30    createHash: Harness.mockHash,
31    watchFile: () => noopFileWatcher,
32    watchDirectory: () => noopFileWatcher
33};
34
35class TestSession extends ts.server.Session {
36    getProjectService() {
37        return this.projectService;
38    }
39}
40
41describe("unittests:: tsserver:: Session:: General functionality", () => {
42    let session: TestSession;
43    let lastSent: ts.server.protocol.Message;
44
45    function createSession(): TestSession {
46        const opts: ts.server.SessionOptions = {
47            host: mockHost,
48            cancellationToken: ts.server.nullCancellationToken,
49            useSingleInferredProject: false,
50            useInferredProjectPerProjectRoot: false,
51            typingsInstaller: undefined!, // TODO: GH#18217
52            byteLength: Utils.byteLength,
53            hrtime: process.hrtime,
54            logger: ts.projectSystem.nullLogger(),
55            canUseEvents: true
56        };
57        return new TestSession(opts);
58    }
59
60    // Disable sourcemap support for the duration of the test, as sourcemapping the errors generated during this test is slow and not something we care to test
61    let oldPrepare: ts.AnyFunction;
62    before(() => {
63        oldPrepare = (Error as any).prepareStackTrace;
64        delete (Error as any).prepareStackTrace;
65    });
66
67    after(() => {
68        (Error as any).prepareStackTrace = oldPrepare;
69    });
70
71    beforeEach(() => {
72        session = createSession();
73        session.send = (msg: ts.server.protocol.Message) => {
74            lastSent = msg;
75        };
76    });
77
78    describe("executeCommand", () => {
79        it("should throw when commands are executed with invalid arguments", () => {
80            const req: ts.server.protocol.FileRequest = {
81                command: ts.server.CommandNames.Open,
82                seq: 0,
83                type: "request",
84                arguments: {
85                    file: undefined! // TODO: GH#18217
86                }
87            };
88
89            expect(() => session.executeCommand(req)).to.throw();
90        });
91        it("should output an error response when a command does not exist", () => {
92            const req: ts.server.protocol.Request = {
93                command: "foobar",
94                seq: 0,
95                type: "request"
96            };
97
98            session.executeCommand(req);
99
100            const expected: ts.server.protocol.Response = {
101                command: ts.server.CommandNames.Unknown,
102                type: "response",
103                seq: 0,
104                message: "Unrecognized JSON command: foobar",
105                request_seq: 0,
106                success: false,
107                performanceData: undefined,
108            };
109            expect(lastSent).to.deep.equal(expected);
110        });
111        it("should return a tuple containing the response and if a response is required on success", () => {
112            const req: ts.server.protocol.ConfigureRequest = {
113                command: ts.server.CommandNames.Configure,
114                seq: 0,
115                type: "request",
116                arguments: {
117                    hostInfo: "unit test",
118                    formatOptions: {
119                        newLineCharacter: "`n"
120                    }
121                }
122            };
123
124            expect(session.executeCommand(req)).to.deep.equal({
125                responseRequired: false
126            });
127            expect(lastSent).to.deep.equal({
128                command: ts.server.CommandNames.Configure,
129                type: "response",
130                success: true,
131                request_seq: 0,
132                seq: 0,
133                body: undefined,
134                performanceData: undefined,
135            });
136        });
137        it("should handle literal types in request", () => {
138            const configureRequest: ts.server.protocol.ConfigureRequest = {
139                command: ts.server.CommandNames.Configure,
140                seq: 0,
141                type: "request",
142                arguments: {
143                    formatOptions: {
144                        indentStyle: ts.server.protocol.IndentStyle.Block,
145                    }
146                }
147            };
148
149            session.onMessage(JSON.stringify(configureRequest));
150
151            assert.equal(session.getProjectService().getFormatCodeOptions("" as ts.server.NormalizedPath).indentStyle, ts.IndentStyle.Block);
152
153            const setOptionsRequest: ts.server.protocol.SetCompilerOptionsForInferredProjectsRequest = {
154                command: ts.server.CommandNames.CompilerOptionsForInferredProjects,
155                seq: 1,
156                type: "request",
157                arguments: {
158                    options: {
159                        module: ts.server.protocol.ModuleKind.System,
160                        target: ts.server.protocol.ScriptTarget.ES5,
161                        jsx: ts.server.protocol.JsxEmit.React,
162                        newLine: ts.server.protocol.NewLineKind.Lf,
163                        moduleResolution: ts.server.protocol.ModuleResolutionKind.Node,
164                    }
165                }
166            };
167            session.onMessage(JSON.stringify(setOptionsRequest));
168            assert.deepEqual(
169                session.getProjectService().getCompilerOptionsForInferredProjects(),
170                {
171                    module: ts.ModuleKind.System,
172                    target: ts.ScriptTarget.ES5,
173                    jsx: ts.JsxEmit.React,
174                    newLine: ts.NewLineKind.LineFeed,
175                    moduleResolution: ts.ModuleResolutionKind.NodeJs,
176                    allowNonTsExtensions: true // injected by tsserver
177                } as ts.CompilerOptions);
178        });
179
180        it("Status request gives ts.version", () => {
181            const req: ts.server.protocol.StatusRequest = {
182                command: ts.server.CommandNames.Status,
183                seq: 0,
184                type: "request"
185            };
186
187            const expected: ts.server.protocol.StatusResponseBody = {
188                version: ts.version, // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
189            };
190            assert.deepEqual(session.executeCommand(req).response, expected);
191        });
192    });
193
194    describe("onMessage", () => {
195        const allCommandNames: ts.server.CommandNames[] = [
196            ts.server.CommandNames.Brace,
197            ts.server.CommandNames.BraceFull,
198            ts.server.CommandNames.BraceCompletion,
199            ts.server.CommandNames.Change,
200            ts.server.CommandNames.Close,
201            ts.server.CommandNames.Completions,
202            ts.server.CommandNames.CompletionsFull,
203            ts.server.CommandNames.CompletionDetails,
204            ts.server.CommandNames.CompileOnSaveAffectedFileList,
205            ts.server.CommandNames.Configure,
206            ts.server.CommandNames.Definition,
207            ts.server.CommandNames.DefinitionFull,
208            ts.server.CommandNames.DefinitionAndBoundSpan,
209            ts.server.CommandNames.DefinitionAndBoundSpanFull,
210            ts.server.CommandNames.Implementation,
211            ts.server.CommandNames.ImplementationFull,
212            ts.server.CommandNames.Exit,
213            ts.server.CommandNames.FileReferences,
214            ts.server.CommandNames.FileReferencesFull,
215            ts.server.CommandNames.Format,
216            ts.server.CommandNames.Formatonkey,
217            ts.server.CommandNames.FormatFull,
218            ts.server.CommandNames.FormatonkeyFull,
219            ts.server.CommandNames.FormatRangeFull,
220            ts.server.CommandNames.Geterr,
221            ts.server.CommandNames.GeterrForProject,
222            ts.server.CommandNames.SemanticDiagnosticsSync,
223            ts.server.CommandNames.SyntacticDiagnosticsSync,
224            ts.server.CommandNames.SuggestionDiagnosticsSync,
225            ts.server.CommandNames.NavBar,
226            ts.server.CommandNames.NavBarFull,
227            ts.server.CommandNames.Navto,
228            ts.server.CommandNames.NavtoFull,
229            ts.server.CommandNames.NavTree,
230            ts.server.CommandNames.NavTreeFull,
231            ts.server.CommandNames.Occurrences,
232            ts.server.CommandNames.DocumentHighlights,
233            ts.server.CommandNames.DocumentHighlightsFull,
234            ts.server.CommandNames.JsxClosingTag,
235            ts.server.CommandNames.Open,
236            ts.server.CommandNames.Quickinfo,
237            ts.server.CommandNames.QuickinfoFull,
238            ts.server.CommandNames.References,
239            ts.server.CommandNames.ReferencesFull,
240            ts.server.CommandNames.Reload,
241            ts.server.CommandNames.Rename,
242            ts.server.CommandNames.RenameInfoFull,
243            ts.server.CommandNames.RenameLocationsFull,
244            ts.server.CommandNames.Saveto,
245            ts.server.CommandNames.SignatureHelp,
246            ts.server.CommandNames.SignatureHelpFull,
247            ts.server.CommandNames.Status,
248            ts.server.CommandNames.TypeDefinition,
249            ts.server.CommandNames.ProjectInfo,
250            ts.server.CommandNames.ReloadProjects,
251            ts.server.CommandNames.Unknown,
252            ts.server.CommandNames.OpenExternalProject,
253            ts.server.CommandNames.CloseExternalProject,
254            ts.server.CommandNames.SynchronizeProjectList,
255            ts.server.CommandNames.ApplyChangedToOpenFiles,
256            ts.server.CommandNames.EncodedSemanticClassificationsFull,
257            ts.server.CommandNames.Cleanup,
258            ts.server.CommandNames.OutliningSpans,
259            ts.server.CommandNames.TodoComments,
260            ts.server.CommandNames.Indentation,
261            ts.server.CommandNames.DocCommentTemplate,
262            ts.server.CommandNames.CompilerOptionsDiagnosticsFull,
263            ts.server.CommandNames.NameOrDottedNameSpan,
264            ts.server.CommandNames.BreakpointStatement,
265            ts.server.CommandNames.CompilerOptionsForInferredProjects,
266            ts.server.CommandNames.GetCodeFixes,
267            ts.server.CommandNames.GetCodeFixesFull,
268            ts.server.CommandNames.GetSupportedCodeFixes,
269            ts.server.CommandNames.GetApplicableRefactors,
270            ts.server.CommandNames.GetEditsForRefactor,
271            ts.server.CommandNames.GetEditsForRefactorFull,
272            ts.server.CommandNames.OrganizeImports,
273            ts.server.CommandNames.OrganizeImportsFull,
274            ts.server.CommandNames.GetEditsForFileRename,
275            ts.server.CommandNames.GetEditsForFileRenameFull,
276            ts.server.CommandNames.SelectionRange,
277            ts.server.CommandNames.PrepareCallHierarchy,
278            ts.server.CommandNames.ProvideCallHierarchyIncomingCalls,
279            ts.server.CommandNames.ProvideCallHierarchyOutgoingCalls,
280            ts.server.CommandNames.ToggleLineComment,
281            ts.server.CommandNames.ToggleMultilineComment,
282            ts.server.CommandNames.CommentSelection,
283            ts.server.CommandNames.UncommentSelection,
284            ts.server.CommandNames.ProvideInlayHints
285        ];
286
287        it("should not throw when commands are executed with invalid arguments", () => {
288            let i = 0;
289            for (const name of allCommandNames) {
290                const req: ts.server.protocol.Request = {
291                    command: name,
292                    seq: i,
293                    type: "request"
294                };
295                i++;
296                session.onMessage(JSON.stringify(req));
297                req.seq = i;
298                i++;
299                req.arguments = {};
300                session.onMessage(JSON.stringify(req));
301                req.seq = i;
302                i++;
303                req.arguments = null; // eslint-disable-line no-null/no-null
304                session.onMessage(JSON.stringify(req));
305                req.seq = i;
306                i++;
307                req.arguments = "";
308                session.onMessage(JSON.stringify(req));
309                req.seq = i;
310                i++;
311                req.arguments = 0;
312                session.onMessage(JSON.stringify(req));
313                req.seq = i;
314                i++;
315                req.arguments = [];
316                session.onMessage(JSON.stringify(req));
317            }
318            session.onMessage("GARBAGE NON_JSON DATA");
319        });
320        it("should output the response for a correctly handled message", () => {
321            const req: ts.server.protocol.ConfigureRequest = {
322                command: ts.server.CommandNames.Configure,
323                seq: 0,
324                type: "request",
325                arguments: {
326                    hostInfo: "unit test",
327                    formatOptions: {
328                        newLineCharacter: "`n"
329                    }
330                }
331            };
332
333            session.onMessage(JSON.stringify(req));
334
335            expect(lastSent).to.deep.equal({
336                command: ts.server.CommandNames.Configure,
337                type: "response",
338                success: true,
339                request_seq: 0,
340                seq: 0,
341                body: undefined,
342                performanceData: undefined,
343            } as ts.server.protocol.ConfigureResponse);
344        });
345    });
346
347    describe("send", () => {
348        it("is an overrideable handle which sends protocol messages over the wire", () => {
349            const msg: ts.server.protocol.Request = { seq: 0, type: "request", command: "" };
350            const strmsg = JSON.stringify(msg);
351            const len = 1 + Utils.byteLength(strmsg, "utf8");
352            const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`;
353
354            session.send = ts.server.Session.prototype.send;
355            assert(session.send);
356            expect(session.send(msg)).to.not.exist; // eslint-disable-line @typescript-eslint/no-unused-expressions
357            expect(lastWrittenToHost).to.equal(resultMsg);
358        });
359    });
360
361    describe("addProtocolHandler", () => {
362        it("can add protocol handlers", () => {
363            const respBody = {
364                item: false
365            };
366            const command = "newhandle";
367            const result: ts.server.HandlerResponse = {
368                response: respBody,
369                responseRequired: true
370            };
371
372            session.addProtocolHandler(command, () => result);
373
374            expect(session.executeCommand({
375                command,
376                seq: 0,
377                type: "request"
378            })).to.deep.equal(result);
379        });
380        it("throws when a duplicate handler is passed", () => {
381            const respBody = {
382                item: false
383            };
384            const resp: ts.server.HandlerResponse = {
385                response: respBody,
386                responseRequired: true
387            };
388            const command = "newhandle";
389
390            session.addProtocolHandler(command, () => resp);
391
392            expect(() => session.addProtocolHandler(command, () => resp))
393                .to.throw(`Protocol handler already exists for command "${command}"`);
394        });
395    });
396
397    describe("event", () => {
398        it("can format event responses and send them", () => {
399            const evt = "notify-test";
400            const info = {
401                test: true
402            };
403
404            session.event(info, evt);
405
406            expect(lastSent).to.deep.equal({
407                type: "event",
408                seq: 0,
409                event: evt,
410                body: info
411            });
412        });
413    });
414
415    describe("output", () => {
416        it("can format command responses and send them", () => {
417            const body = {
418                block: {
419                    key: "value"
420                }
421            };
422            const command = "test";
423
424            session.output(body, command, /*reqSeq*/ 0);
425
426            expect(lastSent).to.deep.equal({
427                seq: 0,
428                request_seq: 0,
429                type: "response",
430                command,
431                body,
432                success: true,
433                performanceData: undefined,
434            });
435        });
436    });
437});
438
439describe("unittests:: tsserver:: Session:: exceptions", () => {
440
441    // Disable sourcemap support for the duration of the test, as sourcemapping the errors generated during this test is slow and not something we care to test
442    let oldPrepare: ts.AnyFunction;
443    let oldStackTraceLimit: number;
444    before(() => {
445        oldStackTraceLimit = (Error as any).stackTraceLimit;
446        oldPrepare = (Error as any).prepareStackTrace;
447        delete (Error as any).prepareStackTrace;
448        (Error as any).stackTraceLimit = 10;
449    });
450
451    after(() => {
452        (Error as any).prepareStackTrace = oldPrepare;
453        (Error as any).stackTraceLimit = oldStackTraceLimit;
454    });
455
456    const command = "testhandler";
457    class TestSession extends ts.server.Session {
458        lastSent: ts.server.protocol.Message | undefined;
459        private exceptionRaisingHandler(_request: ts.server.protocol.Request): { response?: any, responseRequired: boolean } {
460            f1();
461            return ts.Debug.fail(); // unreachable, throw to make compiler happy
462            function f1() {
463                throw new Error("myMessage");
464            }
465        }
466
467        constructor() {
468            super({
469                host: mockHost,
470                cancellationToken: ts.server.nullCancellationToken,
471                useSingleInferredProject: false,
472                useInferredProjectPerProjectRoot: false,
473                typingsInstaller: undefined!, // TODO: GH#18217
474                byteLength: Utils.byteLength,
475                hrtime: process.hrtime,
476                logger: ts.projectSystem.nullLogger(),
477                canUseEvents: true
478            });
479            this.addProtocolHandler(command, this.exceptionRaisingHandler);
480        }
481        send(msg: ts.server.protocol.Message) {
482            this.lastSent = msg;
483        }
484    }
485
486    it("raised in a protocol handler generate an event", () => {
487
488        const session = new TestSession();
489
490        const request = {
491            command,
492            seq: 0,
493            type: "request"
494        };
495
496        session.onMessage(JSON.stringify(request));
497        const lastSent = session.lastSent as ts.server.protocol.Response;
498
499        expect(lastSent).to.contain({
500            seq: 0,
501            type: "response",
502            command,
503            success: false
504        });
505
506        expect(lastSent.message).has.string("myMessage").and.has.string("f1");
507    });
508});
509
510describe("unittests:: tsserver:: Session:: how Session is extendable via subclassing", () => {
511    class TestSession extends ts.server.Session {
512        lastSent: ts.server.protocol.Message | undefined;
513        customHandler = "testhandler";
514        constructor() {
515            super({
516                host: mockHost,
517                cancellationToken: ts.server.nullCancellationToken,
518                useSingleInferredProject: false,
519                useInferredProjectPerProjectRoot: false,
520                typingsInstaller: undefined!, // TODO: GH#18217
521                byteLength: Utils.byteLength,
522                hrtime: process.hrtime,
523                logger: ts.projectSystem.createHasErrorMessageLogger(),
524                canUseEvents: true
525            });
526            this.addProtocolHandler(this.customHandler, () => {
527                return { response: undefined, responseRequired: true };
528            });
529        }
530        send(msg: ts.server.protocol.Message) {
531            this.lastSent = msg;
532        }
533    }
534
535    it("can override methods such as send", () => {
536        const session = new TestSession();
537        const body = {
538            block: {
539                key: "value"
540            }
541        };
542        const command = "test";
543
544        session.output(body, command, /*reqSeq*/ 0);
545
546        expect(session.lastSent).to.deep.equal({
547            seq: 0,
548            request_seq: 0,
549            type: "response",
550            command,
551            body,
552            success: true,
553            performanceData: undefined,
554        });
555    });
556    it("can add and respond to new protocol handlers", () => {
557        const session = new TestSession();
558
559        expect(session.executeCommand({
560            seq: 0,
561            type: "request",
562            command: session.customHandler
563        })).to.deep.equal({
564            response: undefined,
565            responseRequired: true
566        });
567    });
568    it("has access to the project service", () => {
569        new class extends TestSession {
570            constructor() {
571                super();
572                assert(this.projectService);
573                expect(this.projectService).to.be.instanceOf(ts.server.ProjectService);
574            }
575        }();
576    });
577});
578
579describe("unittests:: tsserver:: Session:: an example of using the Session API to create an in-process server", () => {
580    class InProcSession extends ts.server.Session {
581        private queue: ts.server.protocol.Request[] = [];
582        constructor(private client: InProcClient) {
583            super({
584                host: mockHost,
585                cancellationToken: ts.server.nullCancellationToken,
586                useSingleInferredProject: false,
587                useInferredProjectPerProjectRoot: false,
588                typingsInstaller: undefined!, // TODO: GH#18217
589                byteLength: Utils.byteLength,
590                hrtime: process.hrtime,
591                logger: ts.projectSystem.createHasErrorMessageLogger(),
592                canUseEvents: true
593            });
594            this.addProtocolHandler("echo", (req: ts.server.protocol.Request) => ({
595                response: req.arguments,
596                responseRequired: true
597            }));
598        }
599
600        send(msg: ts.server.protocol.Message) {
601            this.client.handle(msg);
602        }
603
604        enqueue(msg: ts.server.protocol.Request) {
605            this.queue.unshift(msg);
606        }
607
608        handleRequest(msg: ts.server.protocol.Request) {
609            let response: ts.server.protocol.Response;
610            try {
611                response = this.executeCommand(msg).response as ts.server.protocol.Response;
612            }
613            catch (e) {
614                this.output(undefined, msg.command, msg.seq, e.toString());
615                return;
616            }
617            if (response) {
618                this.output(response, msg.command, msg.seq);
619            }
620        }
621
622        consumeQueue() {
623            while (this.queue.length > 0) {
624                const elem = this.queue.pop()!;
625                this.handleRequest(elem);
626            }
627        }
628    }
629
630    class InProcClient {
631        private server: InProcSession | undefined;
632        private seq = 0;
633        private callbacks: ((resp: ts.server.protocol.Response) => void)[] = [];
634        private eventHandlers = new ts.Map<string, (args: any) => void>();
635
636        handle(msg: ts.server.protocol.Message): void {
637            if (msg.type === "response") {
638                const response = msg as ts.server.protocol.Response;
639                const handler = this.callbacks[response.request_seq];
640                if (handler) {
641                    handler(response);
642                    delete this.callbacks[response.request_seq];
643                }
644            }
645            else if (msg.type === "event") {
646                const event = msg as ts.server.protocol.Event;
647                this.emit(event.event, event.body);
648            }
649        }
650
651        emit(name: string, args: any): void {
652            const handler = this.eventHandlers.get(name);
653            if (handler) {
654                handler(args);
655            }
656        }
657
658        on(name: string, handler: (args: any) => void): void {
659            this.eventHandlers.set(name, handler);
660        }
661
662        connect(session: InProcSession): void {
663            this.server = session;
664        }
665
666        execute(command: string, args: any, callback: (resp: ts.server.protocol.Response) => void): void {
667            if (!this.server) {
668                return;
669            }
670            this.seq++;
671            this.server.enqueue({
672                seq: this.seq,
673                type: "request",
674                command,
675                arguments: args
676            });
677            this.callbacks[this.seq] = callback;
678        }
679    }
680
681    it("can be constructed and respond to commands", (done) => {
682        const cli = new InProcClient();
683        const session = new InProcSession(cli);
684        const toEcho = {
685            data: true
686        };
687        const toEvent = {
688            data: false
689        };
690        let responses = 0;
691
692        // Connect the client
693        cli.connect(session);
694
695        // Add an event handler
696        cli.on("testevent", (eventinfo) => {
697            expect(eventinfo).to.equal(toEvent);
698            responses++;
699            expect(responses).to.equal(1);
700        });
701
702        // Trigger said event from the server
703        session.event(toEvent, "testevent");
704
705        // Queue an echo command
706        cli.execute("echo", toEcho, (resp) => {
707            assert(resp.success, resp.message);
708            responses++;
709            expect(responses).to.equal(2);
710            expect(resp.body).to.deep.equal(toEcho);
711        });
712
713        // Queue a configure command
714        cli.execute("configure", {
715            hostInfo: "unit test",
716            formatOptions: {
717                newLineCharacter: "`n"
718            }
719        }, (resp) => {
720            assert(resp.success, resp.message);
721            responses++;
722            expect(responses).to.equal(3);
723            done();
724        });
725
726        // Consume the queue and trigger the callbacks
727        session.consumeQueue();
728    });
729});
730
731describe("unittests:: tsserver:: Session:: helpers", () => {
732    it(ts.server.getLocationInNewDocument.name, () => {
733        const text = `// blank line\nconst x = 0;`;
734        const renameLocationInOldText = text.indexOf("0");
735        const fileName = "/a.ts";
736        const edits: ts.FileTextChanges = {
737            fileName,
738            textChanges: [
739                {
740                    span: { start: 0, length: 0 },
741                    newText: "const newLocal = 0;\n\n",
742                },
743                {
744                    span: { start: renameLocationInOldText, length: 1 },
745                    newText: "newLocal",
746                },
747            ],
748        };
749        const renameLocationInNewText = renameLocationInOldText + edits.textChanges[0].newText.length;
750        const res = ts.server.getLocationInNewDocument(text, fileName, renameLocationInNewText, [edits]);
751        assert.deepEqual(res, { line: 4, offset: 11 });
752    });
753});
754