All tsserver unittests use session and typing installer (#56337)

This commit is contained in:
Sheetal Nandi
2023-11-09 11:48:48 -08:00
committed by GitHub
parent 8e1fb5789a
commit efecc85dca
1350 changed files with 149071 additions and 39861 deletions
+3 -3
View File
@@ -6,7 +6,7 @@ import * as Utils from "./_namespaces/Utils";
import * as vfs from "./_namespaces/vfs";
import * as vpath from "./_namespaces/vpath";
import {
Logger,
LoggerWithInMemoryLogs,
} from "./tsserverLogger";
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
@@ -259,7 +259,7 @@ export class TestState {
private inputFiles = new Map<string, string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
private logger: Logger | undefined;
private logger: LoggerWithInMemoryLogs | undefined;
private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[] | undefined) {
let result = "";
@@ -519,7 +519,7 @@ export class TestState {
if (this.logger) {
Harness.Baseline.runBaseline(
`tsserver/fourslashServer/${ts.getBaseFileName(this.originalInputFileName).replace(".ts", ".js")}`,
this.logger.logs!.join("\n"),
this.logger.logs.join("\n"),
);
}
}
+3 -3
View File
@@ -17,7 +17,7 @@ import {
import {
createLoggerWithInMemoryLogs,
HarnessLSCouldNotResolveModule,
Logger,
LoggerWithInMemoryLogs,
} from "./tsserverLogger";
import {
createWatchUtils,
@@ -130,7 +130,7 @@ export interface LanguageServiceAdapter {
getLanguageService(): ts.LanguageService;
getClassifier(): ts.Classifier;
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo;
getLogger(): Logger | undefined;
getLogger(): LoggerWithInMemoryLogs | undefined;
}
export abstract class LanguageServiceAdapterHost {
@@ -611,7 +611,7 @@ export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
private host: SessionClientHost;
private client: ts.server.SessionClient;
private server: FourslashSession;
private logger: Logger;
private logger: LoggerWithInMemoryLogs;
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
// This is the main host that tests use to direct tests
const clientHost = new SessionClientHost(cancellationToken, options);
+1
View File
@@ -431,6 +431,7 @@ function verifyProgram(service: ts.server.ProjectService, project: ts.server.Pro
const getDefaultLibLocation = compilerHost.getDefaultLibLocation!;
compilerHost.getDefaultLibLocation = () => ts.getNormalizedAbsolutePath(getDefaultLibLocation(), service.host.getCurrentDirectory());
compilerHost.getDefaultLibFileName = options => ts.combinePaths(compilerHost.getDefaultLibLocation!(), ts.getDefaultLibFileName(options));
compilerHost.trace = ts.noop; // We dont want to update host just because of trace
const readFile = compilerHost.readFile;
compilerHost.readFile = fileName => {
const path = project.toPath(fileName);
+36 -29
View File
@@ -50,12 +50,16 @@ export function nullLogger(): Logger {
}
export function createHasErrorMessageLogger(): Logger {
return {
...nullLogger(),
msg: (s, type) => ts.Debug.fail(`Error: ${s}, type: ${type}`),
};
const logger = nullLogger();
logger.msg = (s, type) => ts.Debug.fail(`Error: ${s}, type: ${type}`);
return logger;
}
function handleLoggerGroup(logger: Logger): Logger {
function handleLoggerGroup(logger: Logger, host: ts.server.ServerHost, logText: (s: string) => void, sanitizeLibs: true | undefined): Logger {
logger.hasLevel = ts.returnTrue;
logger.loggingEnabled = ts.returnTrue;
logger.host = host;
if (host) logText(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
let inGroup = false;
let firstInGroup = false;
logger.startGroup = () => {
@@ -63,11 +67,14 @@ function handleLoggerGroup(logger: Logger): Logger {
firstInGroup = true;
};
logger.endGroup = () => inGroup = false;
const originalInfo = logger.info;
logger.info = s => msg(s, ts.server.Msg.Info, s => originalInfo.call(logger, s));
logger.log = s => originalInfo.call(logger, s);
logger.info = s => msg(s, ts.server.Msg.Info, log);
logger.log = log;
return logger;
function log(s: string) {
logText((sanitizeLibs ? sanitizeLibFileText : ts.identity)(sanitizeLog(s)));
}
function msg(s: string, type = ts.server.Msg.Err, write: (s: string) => void) {
s = `[${nowString(logger)}] ${s}`;
if (!inGroup || firstInGroup) s = padStringRight(type + " seq", " ") + s;
@@ -86,16 +93,19 @@ export function nowString(logger: Logger) {
return `hh:mm:ss:mss`;
}
export function createLoggerWritingToConsole(host: ts.server.ServerHost) {
return handleLoggerGroup({
...nullLogger(),
hasLevel: ts.returnTrue,
loggingEnabled: ts.returnTrue,
perftrc: s => console.log(s),
info: s => console.log(s),
msg: (s, type) => console.log(`${type}:: ${s}`),
export function createLoggerWritingToConsole(host: ts.server.ServerHost, sanitizeLibs?: true) {
const logger = createHasErrorMessageLogger();
logger.logs = [];
logger.logs.push = (...args) => {
args.forEach(s => console.log(s));
return 0;
};
return handleLoggerGroup(
logger,
host,
});
s => console.log(s),
sanitizeLibs,
) as LoggerWithInMemoryLogs;
}
export function sanitizeLog(s: string): string {
@@ -117,6 +127,7 @@ export function sanitizeLog(s: string): string {
s = s.replace(/"exportMapKey":\s*"\d+ \d+ /g, match => match.replace(/ \d+ /, ` * `));
s = s.replace(/getIndentationAtPosition: getCurrentSourceFile: \d+(?:\.\d+)?/, `getIndentationAtPosition: getCurrentSourceFile: *`);
s = s.replace(/getIndentationAtPosition: computeIndentation\s*: \d+(?:\.\d+)?/, `getIndentationAtPosition: computeIndentation: *`);
s = replaceAll(s, `@ts${ts.versionMajorMinor}`, `@tsFakeMajor.Minor`);
s = sanitizeHarnessLSException(s);
return s;
}
@@ -135,16 +146,12 @@ export function sanitizeLibFileText(s: string): string {
return s;
}
export function createLoggerWithInMemoryLogs(host: ts.server.ServerHost, sanitizeLibs?: true): Logger {
const logger = createHasErrorMessageLogger();
const logs: string[] = [];
if (host) logs.push(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
return handleLoggerGroup({
...logger,
logs,
hasLevel: ts.returnTrue,
loggingEnabled: ts.returnTrue,
info: s => logs.push((sanitizeLibs ? sanitizeLibFileText : ts.identity)(sanitizeLog(s))),
host,
});
export interface LoggerWithInMemoryLogs extends Logger {
logs: string[];
}
export function createLoggerWithInMemoryLogs(host: ts.server.ServerHost, sanitizeLibs?: true): LoggerWithInMemoryLogs {
const logger = createHasErrorMessageLogger();
logger.logs = [];
return handleLoggerGroup(logger, host, s => logger.logs!.push(s), sanitizeLibs) as LoggerWithInMemoryLogs;
}
+3 -2
View File
@@ -1131,9 +1131,10 @@ export class Session<TMessage = string> implements EventSender {
}
private projectsUpdatedInBackgroundEvent(openFiles: string[]): void {
this.projectService.logger.info(`got projects updated in background, updating diagnostics for ${openFiles}`);
this.projectService.logger.info(`got projects updated in background ${openFiles}`);
if (openFiles.length) {
if (!this.suppressDiagnosticEvents && !this.noGetErrOnBackgroundUpdate) {
this.projectService.logger.info(`Queueing diagnostics update for ${openFiles}`);
// For now only queue error checking for open files. We can change this to include non open files as well
this.errorCheck.startNew(next => this.updateErrorCheck(next, openFiles, 100, /*requireOpen*/ true));
}
@@ -1193,7 +1194,7 @@ export class Session<TMessage = string> implements EventSender {
public send(msg: protocol.Message) {
if (msg.type === "event" && !this.canUseEvents) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Session does not support events: ignored event: ${JSON.stringify(msg)}`);
this.logger.info(`Session does not support events: ignored event: ${stringifyIndented(msg)}`);
}
return;
}
+19 -52
View File
@@ -20,6 +20,8 @@ import {
import {
changeToHostTrackingWrittenFiles,
File,
SerializeOutputOrder,
StateLogger,
TestServerHost,
TestServerHostTrackingWrittenFiles,
} from "./virtualFileSystemWithWatch";
@@ -63,13 +65,12 @@ export interface TscWatchCompile extends TscWatchCompileBase {
export const noopChange: TscWatchCompileChange = {
caption: "No change",
edit: ts.noop,
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
};
export type SystemSnap = ReturnType<TestServerHost["snap"]>;
function tscWatchCompile(input: TscWatchCompile) {
it("tsc-watch:: Generates files matching the baseline", () => {
const { sys, baseline, oldSnap } = createBaseline(input.sys());
const { sys, baseline } = createBaseline(input.sys());
const {
scenario,
subScenario,
@@ -92,7 +93,6 @@ function tscWatchCompile(input: TscWatchCompile) {
commandLineArgs,
sys,
baseline,
oldSnap,
getPrograms,
baselineSourceMap,
baselineDependencies,
@@ -102,44 +102,21 @@ function tscWatchCompile(input: TscWatchCompile) {
});
}
export interface TestServerHostWithTimeoutLogging {
logTimeoutQueueLength(): void;
}
export type TscWatchSystem = TestServerHostTrackingWrittenFiles;
export type TscWatchSystem = TestServerHostTrackingWrittenFiles & TestServerHostWithTimeoutLogging;
function changeToTestServerHostWithTimeoutLogging(inputHost: TestServerHostTrackingWrittenFiles, baseline: string[]): TscWatchSystem {
const host = inputHost as TscWatchSystem;
const originalRunQueuedTimeoutCallbacks = host.runQueuedTimeoutCallbacks;
const originalRunQueuedImmediateCallbacks = host.runQueuedImmediateCallbacks;
host.runQueuedTimeoutCallbacks = runQueuedTimeoutCallbacks;
host.runQueuedImmediateCallbacks = runQueuedImmediateCallbacks;
host.logTimeoutQueueLength = logTimeoutQueueLength;
function changeToTestServerHostWithTimeoutLogging(host: TestServerHostTrackingWrittenFiles, baseline: string[]): TscWatchSystem {
const logger: StateLogger = {
log: s => baseline.push(s),
logs: baseline,
};
host.timeoutCallbacks.switchToBaseliningInvoke(logger, SerializeOutputOrder.BeforeDiff);
host.immediateCallbacks.switchToBaseliningInvoke(logger, SerializeOutputOrder.BeforeDiff);
return host;
function logTimeoutQueueLength() {
baseline.push(host.timeoutCallbacks.log());
baseline.push(host.immediateCallbacks.log());
}
function runQueuedTimeoutCallbacks(timeoutId?: number) {
baseline.push(`Before running ${host.timeoutCallbacks.log()}`);
if (timeoutId !== undefined) baseline.push(`Invoking ${host.timeoutCallbacks.callbackType} callback:: timeoutId:: ${timeoutId}:: ${host.timeoutCallbacks.map[timeoutId].args[0]}`);
originalRunQueuedTimeoutCallbacks.call(host, timeoutId);
baseline.push(`After running ${host.timeoutCallbacks.log()}`);
}
function runQueuedImmediateCallbacks() {
baseline.push(`Before running ${host.immediateCallbacks.log()}`);
originalRunQueuedImmediateCallbacks.call(host);
baseline.push(`After running ${host.immediateCallbacks.log()}`);
}
}
export interface BaselineBase {
baseline: string[];
sys: TscWatchSystem;
oldSnap: SystemSnap;
}
export interface Baseline extends BaselineBase, CommandLineCallbacks {
@@ -153,9 +130,9 @@ export function createBaseline(system: TestServerHost, modifySystem?: (sys: Test
const sys = changeToTestServerHostWithTimeoutLogging(changeToHostTrackingWrittenFiles(initialSys), baseline);
baseline.push(`currentDirectory:: ${sys.getCurrentDirectory()} useCaseSensitiveFileNames: ${sys.useCaseSensitiveFileNames}`);
baseline.push("Input::");
sys.diff(baseline);
sys.serializeState(baseline, SerializeOutputOrder.None);
const { cb, getPrograms } = commandLineCallbacks(sys);
return { sys, baseline, oldSnap: sys.snap(), cb, getPrograms };
return { sys, baseline, cb, getPrograms };
}
export function createSolutionBuilderWithWatchHostForBaseline(sys: TestServerHost, cb: ts.ExecuteCommandLineCallbacks) {
@@ -208,13 +185,10 @@ function updateWatchHostForBaseline<T extends ts.BuilderProgram>(host: ts.WatchC
}
export function applyEdit(sys: BaselineBase["sys"], baseline: BaselineBase["baseline"], edit: TscWatchCompileChange["edit"], caption?: TscWatchCompileChange["caption"]) {
const oldSnap = sys.snap();
baseline.push(`Change::${caption ? " " + caption : ""}`, "");
edit(sys);
baseline.push("Input::");
sys.diff(baseline, oldSnap);
sys.serializeWatches(baseline);
return sys.snap();
sys.serializeState(baseline, SerializeOutputOrder.AfterDiff);
}
export interface RunWatchBaseline<T extends ts.BuilderProgram> extends BaselineBase, TscWatchCompileBase<T> {
@@ -230,7 +204,6 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
getPrograms,
sys,
baseline,
oldSnap,
baselineSourceMap,
baselineDependencies,
edits,
@@ -243,21 +216,19 @@ export function runWatchBaseline<T extends ts.BuilderProgram = ts.EmitAndSemanti
getPrograms,
oldPrograms: ts.emptyArray,
sys,
oldSnap,
baselineSourceMap,
baselineDependencies,
});
if (edits) {
for (const { caption, edit, timeouts, symlinksNotReflected, skipStructureCheck } of edits) {
oldSnap = applyEdit(sys, baseline, edit, caption);
applyEdit(sys, baseline, edit, caption);
timeouts(sys, programs, watchOrSolution);
programs = watchBaseline({
baseline,
getPrograms,
oldPrograms: programs,
sys,
oldSnap,
baselineSourceMap,
baselineDependencies,
caption,
@@ -291,7 +262,6 @@ export function watchBaseline({
getPrograms,
oldPrograms,
sys,
oldSnap,
baselineSourceMap,
baselineDependencies,
caption,
@@ -300,21 +270,18 @@ export function watchBaseline({
symlinksNotReflected,
}: WatchBaseline) {
if (baselineSourceMap) generateSourceMapBaselineFiles(sys);
sys.serializeOutput(baseline);
const programs = getPrograms();
baselinePrograms(baseline, programs, oldPrograms, baselineDependencies);
sys.serializeWatches(baseline);
baseline.push(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}`, "");
sys.diff(baseline, oldSnap);
sys.writtenFiles.forEach((value, key) => {
assert.equal(value, 1, `Expected to write file ${key} only once`);
});
sys.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
baselinePrograms(baseline, programs, oldPrograms, baselineDependencies);
baseline.push(`exitCode:: ExitStatus.${ts.ExitStatus[sys.exitCode as ts.ExitStatus]}`, "");
// Verify program structure and resolution cache when incremental edit with tsc --watch (without build mode)
if (resolutionCache && programs.length) {
ts.Debug.assert(programs.length === 1);
verifyProgramStructureAndResolutionCache(caption!, sys, programs[0][0], resolutionCache, useSourceOfProjectReferenceRedirect, symlinksNotReflected);
}
sys.writtenFiles.clear();
return programs;
}
function verifyProgramStructureAndResolutionCache(
+77 -131
View File
@@ -2,9 +2,8 @@ import {
incrementalVerifier,
} from "../../../harness/incrementalUtils";
import {
createHasErrorMessageLogger,
createLoggerWithInMemoryLogs,
Logger,
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
@@ -22,12 +21,13 @@ import {
File,
FileOrFolderOrSymLink,
libFile,
SerializeOutputOrder,
StateLogger,
TestServerHost,
TestServerHostTrackingWrittenFiles,
} from "./virtualFileSystemWithWatch";
export function baselineTsserverLogs(scenario: string, subScenario: string, sessionOrService: { logger: Logger; }) {
ts.Debug.assert(sessionOrService.logger.logs?.length); // Ensure caller used in memory logger
export function baselineTsserverLogs(scenario: string, subScenario: string, sessionOrService: { logger: LoggerWithInMemoryLogs; }) {
Harness.Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.js`, sessionOrService.logger.logs.join("\r\n"));
}
@@ -49,29 +49,24 @@ export function toExternalFiles(fileNames: string[]) {
export type TestSessionAndServiceHost = TestServerHostTrackingWrittenFiles & {
patched: boolean;
baselineHost(title: string): void;
logTimeoutQueueLength(): void;
};
export function patchHostTimeouts(
inputHost: TestServerHostTrackingWrittenFiles,
logger: Logger,
logger: LoggerWithInMemoryLogs,
) {
const host = inputHost as TestSessionAndServiceHost;
if (host.patched) return host;
host.patched = true;
if (!logger.hasLevel(ts.server.LogLevel.verbose)) {
host.logTimeoutQueueLength = ts.notImplemented;
host.baselineHost = ts.notImplemented;
return host;
}
const originalRunQueuedTimeoutCallbacks = host.runQueuedTimeoutCallbacks;
const originalRunQueuedImmediateCallbacks = host.runQueuedImmediateCallbacks;
const originalSetTime = host.setTime;
let hostDiff: ReturnType<TestServerHost["snap"]> | undefined;
host.runQueuedTimeoutCallbacks = runQueuedTimeoutCallbacks;
host.runQueuedImmediateCallbacks = runQueuedImmediateCallbacks;
host.logTimeoutQueueLength = logTimeoutQueueLength;
host.timeoutCallbacks.switchToBaseliningInvoke(logger, SerializeOutputOrder.None);
host.immediateCallbacks.switchToBaseliningInvoke(logger as StateLogger, SerializeOutputOrder.None);
host.pendingInstalls.switchToBaseliningInvoke(logger, SerializeOutputOrder.None);
host.setTime = setTime;
host.baselineHost = baselineHost;
host.patched = true;
@@ -82,52 +77,63 @@ export function patchHostTimeouts(
return originalSetTime.call(host, time);
}
function logTimeoutQueueLength() {
logger.log(host.timeoutCallbacks.log());
host.baselineHost(host.immediateCallbacks.log());
}
function runQueuedTimeoutCallbacks(timeoutId?: number) {
host.baselineHost(`Before running ${host.timeoutCallbacks.log()}`);
if (timeoutId !== undefined) logger.log(`Invoking ${host.timeoutCallbacks.callbackType} callback:: timeoutId:: ${timeoutId}:: ${host.timeoutCallbacks.map[timeoutId].args[0]}`);
originalRunQueuedTimeoutCallbacks.call(host, timeoutId);
host.baselineHost(`After running ${host.timeoutCallbacks.log()}`);
}
function runQueuedImmediateCallbacks() {
host.baselineHost(`Before running ${host.immediateCallbacks.log()}`);
originalRunQueuedImmediateCallbacks.call(host);
host.baselineHost(`After running ${host.immediateCallbacks.log()}`);
}
function baselineHost(title: string) {
logger.log(title);
ts.Debug.assertIsDefined(logger.logs);
host.diff(logger.logs, hostDiff);
host.serializeWatches(logger.logs);
hostDiff = host.snap();
host.writtenFiles.clear();
host.serializeState(logger.logs, SerializeOutputOrder.None);
}
}
export interface TestSessionOptions extends ts.server.SessionOptions, TestTypingsInstallerOptions {
logger: Logger;
allowNonBaseliningLogger?: boolean;
host: TestServerHost;
logger: LoggerWithInMemoryLogs;
disableAutomaticTypingAcquisition?: boolean;
useCancellationToken?: boolean | number;
}
export type TestSessionPartialOptionsAndHost = Partial<Omit<TestSessionOptions, "typingsInstaller" | "cancellationToken">> & Pick<TestSessionOptions, "host">;
export type TestSessionConstructorOptions = TestServerHost | TestSessionPartialOptionsAndHost;
export type TestSessionRequest<T extends ts.server.protocol.Request> = Pick<T, "command" | "arguments">;
function getTestSessionPartialOptionsAndHost(optsOrHost: TestSessionConstructorOptions): TestSessionPartialOptionsAndHost {
// eslint-disable-next-line local/no-in-operator
return "host" in optsOrHost ?
optsOrHost :
{ host: optsOrHost };
}
export class TestSession extends ts.server.Session {
private seq = 0;
public testhost: TestSessionAndServiceHost;
public override logger: Logger;
public override host!: TestSessionAndServiceHost;
public override logger!: LoggerWithInMemoryLogs;
public override readonly typingsInstaller!: TestTypingsInstaller;
public serverCancellationToken: TestServerCancellationToken;
constructor(opts: TestSessionOptions) {
super(opts);
this.logger = opts.logger;
ts.Debug.assert(opts.allowNonBaseliningLogger || this.logger.hasLevel(ts.server.LogLevel.verbose), "Use Baselining logger and baseline tsserver log or create using allowNonBaseliningLogger");
this.testhost = patchHostTimeouts(
changeToHostTrackingWrittenFiles(this.host as TestServerHost),
constructor(optsOrHost: TestSessionConstructorOptions) {
const opts = getTestSessionPartialOptionsAndHost(optsOrHost);
opts.logger = opts.logger || createLoggerWithInMemoryLogs(opts.host);
const typingsInstaller = !opts.disableAutomaticTypingAcquisition ? new TestTypingsInstaller(opts) : undefined;
const cancellationToken = opts.useCancellationToken ?
new TestServerCancellationToken(
opts.logger,
ts.isNumber(opts.useCancellationToken) ? opts.useCancellationToken : undefined,
) :
ts.server.nullCancellationToken;
super({
cancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
noGetErrOnBackgroundUpdate: true,
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
logger: opts.logger,
canUseEvents: true,
incrementalVerifier,
typesMapLocation: customTypesMap.path,
typingsInstaller,
...opts,
});
if (typingsInstaller) typingsInstaller.session = this;
this.serverCancellationToken = cancellationToken as TestServerCancellationToken;
patchHostTimeouts(
changeToHostTrackingWrittenFiles(this.host),
this.logger,
);
}
@@ -146,13 +152,13 @@ export class TestSession extends ts.server.Session {
public override executeCommand(request: ts.server.protocol.Request) {
if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
this.testhost.baselineHost("Before request");
this.host.baselineHost("Before request");
this.logger.info(`request:${ts.server.stringifyIndented(request)}`);
}
const response = super.executeCommand(request);
if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
this.logger.info(`response:${ts.server.stringifyIndented(response.response === ts.getSupportedCodeFixes() ? { ...response, response: "ts.getSupportedCodeFixes()" } : response)}`);
this.testhost.baselineHost("After request");
this.host.baselineHost("After request");
}
return response;
}
@@ -166,37 +172,13 @@ export class TestSession extends ts.server.Session {
}
}
export function createSession(host: TestServerHost, opts: Partial<TestSessionOptions> = {}) {
const logger = opts.logger || createHasErrorMessageLogger();
if (!opts.disableAutomaticTypingAcquisition && opts.typingsInstaller === undefined) {
opts.typingsInstaller = new TestTypingsInstaller(
host,
logger,
opts,
);
}
if (opts.eventHandler !== undefined) {
opts.canUseEvents = true;
}
const sessionOptions: TestSessionOptions = {
host,
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
logger,
canUseEvents: false,
incrementalVerifier,
};
return new TestSession({ ...sessionOptions, ...opts });
}
export function createSessionWithCustomEventHandler(host: TestServerHost, opts?: Partial<TestSessionOptions>, customAction?: (event: ts.server.ProjectServiceEvent) => void) {
const session = createSession(host, { eventHandler, logger: createLoggerWithInMemoryLogs(host), ...opts });
export function createSessionWithCustomEventHandler(
optsOrHost: TestSessionConstructorOptions,
customAction?: (event: ts.server.ProjectServiceEvent) => void,
) {
const opts = getTestSessionPartialOptionsAndHost(optsOrHost);
opts.eventHandler = eventHandler;
const session = new TestSession(opts);
return session;
function eventHandler(event: ts.server.ProjectServiceEvent) {
let data = event.data as any;
@@ -228,42 +210,6 @@ export function createSessionWithCustomEventHandler(host: TestServerHost, opts?:
}
}
export interface TestProjectServiceOptions extends ts.server.ProjectServiceOptions {
logger: Logger;
allowNonBaseliningLogger?: boolean;
}
export class TestProjectService extends ts.server.ProjectService {
public testhost: TestSessionAndServiceHost;
constructor(host: TestServerHost, public override logger: Logger, cancellationToken: ts.HostCancellationToken, useSingleInferredProject: boolean, typingsInstaller: ts.server.ITypingsInstaller, opts: Partial<TestProjectServiceOptions> = {}) {
super({
host,
logger,
session: undefined,
cancellationToken,
useSingleInferredProject,
useInferredProjectPerProjectRoot: false,
typingsInstaller,
typesMapLocation: customTypesMap.path,
incrementalVerifier,
...opts,
});
ts.Debug.assert(opts.allowNonBaseliningLogger || this.logger.hasLevel(ts.server.LogLevel.verbose), "Use Baselining logger and baseline tsserver log or create using allowNonBaseliningLogger");
this.testhost = patchHostTimeouts(
changeToHostTrackingWrittenFiles(this.host as TestServerHost),
this.logger,
);
if (logger.hasLevel(ts.server.LogLevel.verbose)) this.testhost.baselineHost("Creating project service");
}
}
export function createProjectService(host: TestServerHost, options?: Partial<TestProjectServiceOptions>) {
const cancellationToken = options?.cancellationToken || ts.server.nullCancellationToken;
const logger = options?.logger || createHasErrorMessageLogger();
const useSingleInferredProject = options?.useSingleInferredProject !== undefined ? options.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, options?.typingsInstaller || ts.server.nullTypingsInstaller, options);
}
export function protocolLocationFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): ts.server.protocol.Location {
const start = nthIndexOf(str, substring, options ? options.index : 0);
ts.Debug.assert(start !== -1);
@@ -318,11 +264,12 @@ export class TestServerCancellationToken implements ts.server.ServerCancellation
private requestToCancel = -1;
private isCancellationRequestedCount = 0;
constructor(private logger: Logger, private cancelAfterRequest = 0) {
constructor(private logger: LoggerWithInMemoryLogs, private cancelAfterRequest = 0) {
}
setRequest(requestId: number) {
this.currentId = requestId;
this.logger.log(`TestServerCancellationToken:: Cancellation Request id:: ${requestId}`);
}
setRequestToCancel(requestId: number) {
@@ -415,7 +362,7 @@ export function setCompilerOptionsForInferredProjectsRequestForSession(
});
}
export function logDiagnostics(sessionOrService: TestSession | TestProjectService, diagnosticsType: string, project: ts.server.Project, diagnostics: readonly ts.Diagnostic[]) {
export function logDiagnostics(sessionOrService: TestSession, diagnosticsType: string, project: ts.server.Project, diagnostics: readonly ts.Diagnostic[]) {
sessionOrService.logger.info(`${diagnosticsType}:: ${diagnostics.length}`);
diagnostics.forEach(d => sessionOrService.logger.info(ts.formatDiagnostic(d, project)));
}
@@ -445,11 +392,10 @@ export interface CheckAllErrors extends VerifyGetErrRequestBase {
skip?: readonly (SkipErrors | undefined)[];
}
function checkAllErrors({ session, existingTimeouts, files, skip }: CheckAllErrors) {
ts.Debug.assert(session.logger.logs?.length);
for (let i = 0; i < files.length; i++) {
session.testhost.runQueuedTimeoutCallbacks(existingTimeouts ? session.testhost.getNextTimeoutId() - 1 : undefined);
if (!skip?.[i]?.semantic) session.testhost.runQueuedImmediateCallbacks();
if (!skip?.[i]?.suggestion) session.testhost.runQueuedImmediateCallbacks();
session.host.runQueuedTimeoutCallbacks(existingTimeouts ? session.host.getNextTimeoutId() - 1 : undefined);
if (!skip?.[i]?.semantic) session.host.runQueuedImmediateCallbacks();
if (!skip?.[i]?.suggestion) session.host.runQueuedImmediateCallbacks();
}
}
@@ -460,7 +406,7 @@ function filePath(file: string | File) {
function verifyErrorsUsingGeterr({ scenario, subScenario, allFiles, openFiles, getErrRequest }: VerifyGetErrScenario) {
it("verifies the errors in open file", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
verifyGetErrRequest({ session, files: getErrRequest() });
@@ -471,7 +417,7 @@ function verifyErrorsUsingGeterr({ scenario, subScenario, allFiles, openFiles, g
function verifyErrorsUsingGeterrForProject({ scenario, subScenario, allFiles, openFiles, getErrForProjectRequest }: VerifyGetErrScenario) {
it("verifies the errors in projects", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
for (const expected of getErrForProjectRequest()) {
@@ -488,7 +434,7 @@ function verifyErrorsUsingGeterrForProject({ scenario, subScenario, allFiles, op
function verifyErrorsUsingSyncMethods({ scenario, subScenario, allFiles, openFiles, syncDiagnostics }: VerifyGetErrScenario) {
it("verifies the errors using sync commands", () => {
const host = createServerHost([...allFiles(), libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(openFiles(), session);
for (const { file, project } of syncDiagnostics()) {
const reqArgs = { file: filePath(file), projectFileName: project && filePath(project) };
@@ -533,8 +479,8 @@ export function verifyGetErrScenario(scenario: VerifyGetErrScenario) {
verifyErrorsUsingSyncMethods(scenario);
}
export function verifyDynamic(service: ts.server.ProjectService, path: string) {
(service.logger as Logger).log(`${path} isDynamic:: ${service.filenameToScriptInfo.get(path)!.isDynamic}`);
export function verifyDynamic(session: TestSession, path: string) {
session.logger.log(`${path} isDynamic:: ${session.getProjectService().filenameToScriptInfo.get(path)!.isDynamic}`);
}
export function createHostWithSolutionBuild(files: readonly FileOrFolderOrSymLink[], rootNames: readonly string[]) {
@@ -544,10 +490,10 @@ export function createHostWithSolutionBuild(files: readonly FileOrFolderOrSymLin
return host;
}
export function logInferredProjectsOrphanStatus(projectService: ts.server.ProjectService) {
projectService.inferredProjects.forEach(inferredProject => (projectService.logger as Logger).log(`Inferred project: ${inferredProject.projectName} isOrphan:: ${inferredProject.isOrphan()} isClosed: ${inferredProject.isClosed()}`));
export function logInferredProjectsOrphanStatus(session: TestSession) {
session.getProjectService().inferredProjects.forEach(inferredProject => session.logger.log(`Inferred project: ${inferredProject.projectName} isOrphan:: ${inferredProject.isOrphan()} isClosed: ${inferredProject.isClosed()}`));
}
export function logConfiguredProjectsHasOpenRefStatus(projectService: ts.server.ProjectService) {
projectService.configuredProjects.forEach(configuredProject => (projectService.logger as Logger).log(`Configured project: ${configuredProject.projectName} hasOpenRef:: ${configuredProject.hasOpenRef()} isClosed: ${configuredProject.isClosed()}`));
export function logConfiguredProjectsHasOpenRefStatus(session: TestSession) {
session.getProjectService().configuredProjects.forEach(configuredProject => session.logger.log(`Configured project: ${configuredProject.projectName} hasOpenRef:: ${configuredProject.hasOpenRef()} isClosed: ${configuredProject.isClosed()}`));
}
@@ -1,23 +1,24 @@
import {
Logger,
LoggerWithInMemoryLogs,
nowString,
replaceAll,
sanitizeLog,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
ActionInvalidate,
ActionPackageInstalled,
ActionSet,
ActionWatchTypingLocations,
EventBeginInstallTypes,
EventEndInstallTypes,
stringifyIndented,
} from "../../_namespaces/ts.server";
import {
jsonToReadableText,
} from "../helpers";
import {
patchHostTimeouts,
TestSessionAndServiceHost,
TestSession,
} from "./tsserver";
import {
changeToHostTrackingWrittenFiles,
File,
TestServerHost,
} from "./virtualFileSystemWithWatch";
@@ -46,38 +47,19 @@ export const customTypesMap = {
}`,
};
export interface PostExecAction {
readonly success: boolean;
requestId: number;
readonly packageNames: readonly string[];
readonly callback: ts.server.typingsInstaller.RequestCompletedAction;
}
export function loggerToTypingsInstallerLog(logger: Logger): ts.server.typingsInstaller.Log | undefined {
return logger?.loggingEnabled() ? {
export function loggerToTypingsInstallerLog(logger: LoggerWithInMemoryLogs): ts.server.typingsInstaller.Log {
ts.Debug.assert(logger.loggingEnabled());
return {
isEnabled: ts.returnTrue,
writeLine: s => {
// This is a VERY VERY NAIVE sanitization strategy.
// If a substring containing the exact TypeScript version is found,
// even if it's unrelated to TypeScript itself, then it will be replaced,
// leaving us with two options:
//
// 1. Deal with flip-flopping baselines.
// 2. Change the TypeScript version until no matching substring is found.
//
const initialLog = sanitizeLog(s);
const pseudoSanitizedLog = replaceAll(initialLog, `@ts${ts.versionMajorMinor}`, `@tsFakeMajor.Minor`);
return logger.log(`TI:: [${nowString(logger)}] ${pseudoSanitizedLog}`);
},
} : undefined;
writeLine: s => logger.log(`TI:: [${nowString(logger)}] ${s}`),
};
}
interface TypesRegistryFile {
entries: ts.MapLike<ts.MapLike<string>>;
}
function loadTypesRegistryFile(typesRegistryFilePath: string, host: TestServerHost, log: ts.server.typingsInstaller.Log): Map<string, ts.MapLike<string>> {
if (!host.fileExists(typesRegistryFilePath)) {
if (log.isEnabled()) {
log.writeLine(`Types registry file '${typesRegistryFilePath}' does not exist`);
}
log.writeLine(`Types registry file '${typesRegistryFilePath}' does not exist`);
return new Map<string, ts.MapLike<string>>();
}
try {
@@ -85,9 +67,7 @@ function loadTypesRegistryFile(typesRegistryFilePath: string, host: TestServerHo
return new Map(Object.entries(content.entries));
}
catch (e) {
if (log.isEnabled()) {
log.writeLine(`Error when loading types registry file '${typesRegistryFilePath}': ${(e as Error).message}, ${(e as Error).stack}`);
}
log.writeLine(`Error when loading types registry file '${typesRegistryFilePath}': ${(e as Error).message}, ${(e as Error).stack}`);
return new Map<string, ts.MapLike<string>>();
}
}
@@ -97,161 +77,121 @@ function getTypesRegistryFileLocation(globalTypingsCacheLocation: string): strin
}
export interface FileWithPackageName extends File {
package: string;
package?: string;
}
export type InstallActionThrowingError = string;
export type InstallActionWithTypingFiles = [installedTypings: string[] | string, typingFiles: File[]];
export type InstallActionWithFilteredTypings = [typingFiles: FileWithPackageName[]];
export type InstallAction = InstallActionThrowingError | InstallActionWithTypingFiles | InstallActionWithFilteredTypings;
export type InstallActionWithSuccess = boolean;
export type InstallActionWithTypingFiles = readonly FileWithPackageName[];
export type InstallAction = InstallActionThrowingError | InstallActionWithSuccess | InstallActionWithTypingFiles;
export type PendingInstallCallback = (
pendingInstallInfo: string,
installedTypingsOrSuccess: string[] | string | boolean,
typingFiles: readonly File[],
onRequestCompleted: ts.server.typingsInstaller.RequestCompletedAction,
) => void;
export class TestTypingsInstallerWorker extends ts.server.typingsInstaller.TypingsInstaller {
readonly typesRegistry: Map<string, ts.MapLike<string>>;
protected projectService!: ts.server.ProjectService;
constructor(
readonly globalTypingsCacheLocation: string,
throttleLimit: number,
installTypingHost: TestServerHost,
logger: Logger,
typesRegistry: string | readonly string[] | undefined,
private installAction: InstallAction | undefined,
) {
const log = loggerToTypingsInstallerLog(logger);
if (log?.isEnabled()) {
patchHostTimeouts(
changeToHostTrackingWrittenFiles(installTypingHost),
logger,
);
(installTypingHost as TestSessionAndServiceHost).baselineHost("TI:: Creating typing installer");
}
constructor(readonly testTypingInstaller: TestTypingsInstaller) {
const log = loggerToTypingsInstallerLog(testTypingInstaller.session.logger);
ts.Debug.assert(testTypingInstaller.session.host.patched);
testTypingInstaller.session.host.baselineHost("TI:: Creating typing installer");
super(
installTypingHost,
globalTypingsCacheLocation,
testTypingInstaller.session.host,
testTypingInstaller.globalTypingsCacheLocation,
"/safeList.json" as ts.Path,
customTypesMap.path,
throttleLimit,
testTypingInstaller.throttleLimit,
log,
);
this.ensurePackageDirectoryExists(globalTypingsCacheLocation);
this.ensurePackageDirectoryExists(testTypingInstaller.globalTypingsCacheLocation);
if (this.log.isEnabled()) {
this.log.writeLine(`Updating ${typesRegistryPackageName} npm package...`);
this.log.writeLine(`npm install --ignore-scripts ${typesRegistryPackageName}@${this.latestDistTag}`);
}
installTypingHost.ensureFileOrFolder({
path: getTypesRegistryFileLocation(globalTypingsCacheLocation),
this.log.writeLine(`Updating ${typesRegistryPackageName} npm package...`);
this.log.writeLine(`npm install --ignore-scripts ${typesRegistryPackageName}@${this.latestDistTag}`);
testTypingInstaller.session.host.ensureFileOrFolder({
path: getTypesRegistryFileLocation(testTypingInstaller.globalTypingsCacheLocation),
content: jsonToReadableText(createTypesRegistryFileContent(
typesRegistry ?
ts.isString(typesRegistry) ?
[typesRegistry] :
typesRegistry :
testTypingInstaller.typesRegistry ?
ts.isString(testTypingInstaller.typesRegistry) ?
[testTypingInstaller.typesRegistry] :
testTypingInstaller.typesRegistry :
ts.emptyArray,
)),
});
if (this.log.isEnabled()) {
this.log.writeLine(`TI:: Updated ${typesRegistryPackageName} npm package`);
}
this.typesRegistry = loadTypesRegistryFile(getTypesRegistryFileLocation(globalTypingsCacheLocation), installTypingHost, this.log);
if (this.log.isEnabled()) {
(installTypingHost as TestSessionAndServiceHost).baselineHost("TI:: typing installer creation complete");
}
this.log.writeLine(`Updated ${typesRegistryPackageName} npm package`);
this.typesRegistry = loadTypesRegistryFile(
getTypesRegistryFileLocation(testTypingInstaller.globalTypingsCacheLocation),
testTypingInstaller.session.host,
this.log,
);
testTypingInstaller.session.host.baselineHost("TI:: typing installer creation complete");
}
protected postExecActions: PostExecAction[] = [];
executePendingCommands() {
const actionsToRun = this.postExecActions;
this.postExecActions = [];
for (const action of actionsToRun) {
if (this.log.isEnabled()) {
this.log.writeLine(`#${action.requestId} with arguments'${jsonToReadableText(action.packageNames)}':: ${action.success}`);
}
action.callback(action.success);
}
}
attach(projectService: ts.server.ProjectService) {
this.projectService = projectService;
}
getInstallTypingHost() {
return this.installTypingHost;
}
installWorker(requestId: number, packageNames: string[], _cwd: string, cb: ts.server.typingsInstaller.RequestCompletedAction): void {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} with arguments'${jsonToReadableText(packageNames)}'.`);
}
if (!this.installAction) {
this.addPostExecAction("success", requestId, packageNames, cb);
}
else if (ts.isString(this.installAction)) {
assert(false, this.installAction);
}
else if (this.installAction.length === 2) {
this.executeInstallWithTypingFiles(
installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: ts.server.typingsInstaller.RequestCompletedAction): void {
this.log.writeLine(`#${requestId} with cwd: ${cwd} arguments: ${jsonToReadableText(packageNames)}`);
if (typeof this.testTypingInstaller.installAction === "boolean") {
this.scheduleInstall(
requestId,
packageNames,
this.installAction[0],
this.installAction[1],
cb,
this.testTypingInstaller.installAction,
ts.emptyArray,
onRequestCompleted,
);
}
else if (ts.isString(this.testTypingInstaller.installAction)) {
assert(false, this.testTypingInstaller.installAction);
}
else {
const typingFiles = this.installAction[0].filter(f => packageNames.includes(ts.server.typingsInstaller.typingsName(f.package)));
this.executeInstallWithTypingFiles(
const typingFiles = this.testTypingInstaller.installAction.filter(f => !f.package || packageNames.includes(ts.server.typingsInstaller.typingsName(f.package)));
this.scheduleInstall(
requestId,
packageNames,
typingFiles.map(f => f.package),
/*success*/ true,
typingFiles,
cb,
onRequestCompleted,
);
}
}
executeInstallWithTypingFiles(
private scheduleInstall(
requestId: number,
packageNames: string[],
installedTypings: string[] | string,
typingFiles: File[],
cb: ts.server.typingsInstaller.RequestCompletedAction,
success: boolean,
typingFiles: readonly File[],
onRequestCompleted: ts.server.typingsInstaller.RequestCompletedAction,
): void {
this.addPostExecAction(installedTypings, requestId, packageNames, success => {
const host = this.getInstallTypingHost() as TestSessionAndServiceHost;
host.baselineHost("TI:: Before installWorker");
for (const file of typingFiles) {
host.ensureFileOrFolder(file);
}
host.baselineHost("TI:: After installWorker");
cb(success);
});
this.testTypingInstaller.session.host.scheduleInstall(
pendingInstallInfo => {
for (const file of typingFiles) {
this.testTypingInstaller.session.host.ensureFileOrFolder(file);
}
this.testTypingInstaller.session.host.baselineHost(`TI:: Installation ${pendingInstallInfo} complete with success::${!!success}`);
onRequestCompleted(!!success);
},
`#${requestId} with arguments:: ${jsonToReadableText(packageNames)}`,
);
}
sendResponse(response: ts.server.SetTypings | ts.server.InvalidateCachedTypings | ts.server.WatchTypingLocations) {
if (this.log.isEnabled()) {
this.log.writeLine(`Sending response:${stringifyIndented(response)}`);
}
if (response.kind !== ActionWatchTypingLocations) this.projectService.updateTypingsForProject(response);
else this.projectService.watchTypingLocations(response);
sendResponse(response: ts.server.SetTypings | ts.server.InvalidateCachedTypings | ts.server.BeginInstallTypes | ts.server.EndInstallTypes | ts.server.WatchTypingLocations | ts.server.PackageInstalledResponse) {
this.log.writeLine(`Sending response:${stringifyIndented(response)}`);
this.testTypingInstaller.onResponse(response);
}
enqueueInstallTypingsRequest(project: ts.server.Project, typeAcquisition: ts.TypeAcquisition, unresolvedImports: ts.SortedReadonlyArray<string>) {
const request = ts.server.createInstallTypingsRequest(project, typeAcquisition, unresolvedImports, this.globalTypingsCacheLocation);
const request = ts.server.createInstallTypingsRequest(
project,
typeAcquisition,
unresolvedImports,
this.testTypingInstaller.globalTypingsCacheLocation,
);
this.install(request);
}
addPostExecAction(stdout: string | string[], requestId: number, packageNames: string[], cb: ts.server.typingsInstaller.RequestCompletedAction) {
const out = ts.isString(stdout) ? stdout : createNpmPackageJsonString(stdout);
const action: PostExecAction = {
success: !!out,
requestId,
packageNames,
callback: cb,
};
this.postExecActions.push(action);
}
}
export interface TestTypingsInstallerOptions {
host: TestServerHost;
logger?: LoggerWithInMemoryLogs;
globalTypingsCacheLocation?: string;
throttleLimit?: number;
installAction?: InstallAction;
@@ -261,24 +201,39 @@ export interface TestTypingsInstallerOptions {
export class TestTypingsInstaller implements ts.server.ITypingsInstaller {
protected projectService!: ts.server.ProjectService;
public installer!: TestTypingsInstallerWorker;
readonly globalTypingsCacheLocation: string;
private readonly throttleLimit: number;
private installAction?: InstallAction;
private typesRegistry?: string | readonly string[];
session!: TestSession;
packageInstalledPromise: { resolve(value: ts.ApplyCodeActionCommandResult): void; reject(reason: unknown): void; } | undefined;
constructor(
private installTypingHost: TestServerHost,
private logger: Logger,
options?: TestTypingsInstallerOptions,
) {
this.globalTypingsCacheLocation = options?.globalTypingsCacheLocation || this.installTypingHost.getHostSpecificPath("/a/data");
this.throttleLimit = options?.throttleLimit || 5;
this.installAction = options?.installAction;
this.typesRegistry = options?.typesRegistry;
// Options
readonly globalTypingsCacheLocation: string;
readonly throttleLimit: number;
readonly installAction: InstallAction;
readonly typesRegistry: string | readonly string[] | undefined;
constructor(options: TestTypingsInstallerOptions) {
this.globalTypingsCacheLocation = options.globalTypingsCacheLocation || options.host.getHostSpecificPath("/a/data");
this.throttleLimit = options.throttleLimit || 5;
this.installAction = options.installAction !== undefined ? options.installAction : true;
this.typesRegistry = options.typesRegistry;
}
isKnownTypesPackageName = ts.notImplemented;
installPackage = ts.notImplemented;
isKnownTypesPackageName(name: string): boolean {
// We want to avoid looking this up in the registry as that is expensive. So first check that it's actually an NPM package.
const validationResult = ts.JsTyping.validatePackageName(name);
if (validationResult !== ts.JsTyping.NameValidationResult.Ok) {
return false;
}
return this.ensureInstaller().typesRegistry.has(name);
}
installPackage(options: ts.server.InstallPackageOptionsWithProject): Promise<ts.ApplyCodeActionCommandResult> {
this.ensureInstaller().installPackage({ kind: "installPackage", ...options });
ts.Debug.assert(this.packageInstalledPromise === undefined);
return new Promise<ts.ApplyCodeActionCommandResult>((resolve, reject) => {
this.packageInstalledPromise = { resolve, reject };
});
}
attach(projectService: ts.server.ProjectService) {
this.projectService = projectService;
@@ -289,27 +244,66 @@ export class TestTypingsInstaller implements ts.server.ITypingsInstaller {
}
enqueueInstallTypingsRequest(project: ts.server.Project, typeAcquisition: ts.TypeAcquisition, unresolvedImports: ts.SortedReadonlyArray<string>) {
if (!this.installer) {
this.installer = new TestTypingsInstallerWorker(
this.globalTypingsCacheLocation,
this.throttleLimit,
this.installTypingHost,
this.logger,
this.typesRegistry,
this.installAction,
);
this.installer.attach(this.projectService);
this.ensureInstaller().enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
}
private ensureInstaller() {
return this.installer ??= new TestTypingsInstallerWorker(this);
}
onResponse(response: ts.server.SetTypings | ts.server.InvalidateCachedTypings | ts.server.BeginInstallTypes | ts.server.EndInstallTypes | ts.server.WatchTypingLocations | ts.server.PackageInstalledResponse) {
switch (response.kind) {
case ActionPackageInstalled: {
const { success, message } = response;
if (success) {
this.packageInstalledPromise!.resolve({ successMessage: message });
}
else {
this.packageInstalledPromise!.reject(message);
}
this.packageInstalledPromise = undefined;
this.projectService.updateTypingsForProject(response);
// The behavior is the same as for setTypings, so send the same event.
this.session.event(response, "setTypings");
break;
}
case EventBeginInstallTypes: {
const body: ts.server.protocol.BeginInstallTypesEventBody = {
eventId: response.eventId,
packages: response.packagesToInstall,
};
const eventName: ts.server.protocol.BeginInstallTypesEventName = "beginInstallTypes";
this.session.event(body, eventName);
break;
}
case EventEndInstallTypes: {
const body: ts.server.protocol.EndInstallTypesEventBody = {
eventId: response.eventId,
packages: response.packagesToInstall,
success: response.installSuccess,
};
const eventName: ts.server.protocol.EndInstallTypesEventName = "endInstallTypes";
this.session.event(body, eventName);
break;
}
case ActionInvalidate: {
this.projectService.updateTypingsForProject(response);
break;
}
case ActionSet: {
this.projectService.updateTypingsForProject(response);
this.session.event(response, "setTypings");
break;
}
case ActionWatchTypingLocations:
this.projectService.watchTypingLocations(response);
break;
default:
ts.assertType<never>(response);
}
this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
}
}
function createNpmPackageJsonString(installedTypings: string[]): string {
const dependencies: ts.MapLike<any> = {};
for (const typing of installedTypings) {
dependencies[typing] = "1.0.0";
}
return jsonToReadableText({ dependencies });
}
function createTypesRegistryFileContent(list: readonly string[]): TypesRegistryFile {
const versionMap = {
"latest": "1.3.0",
@@ -2,6 +2,7 @@ import {
createWatchUtils,
} from "../../../harness/watchUtils";
import {
arrayFrom,
clear,
clone,
combinePaths,
@@ -42,6 +43,9 @@ import {
sys,
toPath,
} from "../../_namespaces/ts";
import {
typingsInstaller,
} from "../../_namespaces/ts.server";
import {
timeIncrements,
} from "../../_namespaces/vfs";
@@ -161,6 +165,12 @@ function invokeWatcherCallbacks<T>(callbacks: readonly T[] | undefined, invokeCa
}
}
export interface StateLogger {
log(s: string): void;
logs: string[];
}
const exitMessage = "System Exit";
interface CallbackData {
cb: TimeOutCallback;
args: any[];
@@ -168,10 +178,13 @@ interface CallbackData {
time: number;
}
class Callbacks {
readonly map: CallbackData[] = [];
readonly map = new Map<number, CallbackData>();
private nextId = 1;
invoke: (invokeKey?: number) => void = invokeKey => this.invokeWorker(invokeKey);
private hasChanges = false;
private serializedKeys = new Map<number, any>();
constructor(private host: TestServerHost, readonly callbackType: string) {
constructor(private host: TestServerHost, readonly callbackType: string, private readonly swallowExitException?: boolean) {
}
getNextId() {
@@ -181,27 +194,42 @@ class Callbacks {
register(cb: TimeOutCallback, args: any[], ms?: number) {
const timeoutId = this.nextId;
this.nextId++;
this.map[timeoutId] = { cb, args, ms, time: this.host.getTime() };
this.map.set(timeoutId, { cb, args, ms, time: this.host.getTime() });
this.hasChanges = true;
return timeoutId;
}
unregister(id: any) {
if (typeof id === "number") {
delete this.map[id];
this.hasChanges = this.map.delete(id) || this.hasChanges;
}
}
log() {
log(logChanges?: boolean) {
const details: string[] = [];
for (const timeoutId in this.map) {
const { args } = this.map[Number(timeoutId)];
details.push(`${timeoutId}: ${args[0]}`);
this.map.forEach(({ args }, timeoutId) => {
details.push(`${timeoutId}: ${args[0]}${!logChanges || this.serializedKeys.has(timeoutId) ? "" : " *new*"}`);
if (logChanges) this.serializedKeys.set(timeoutId, args[0]);
});
const deleted: string[] = [];
if (logChanges && this.serializedKeys.size !== this.map.size) {
this.serializedKeys.forEach((value, key) => {
if (this.map.has(key)) return;
deleted.push(`${key}: ${value} *deleted*`);
this.serializedKeys.delete(key);
});
}
return `${this.callbackType} callback:: count: ${details.length}` + (details.length ? "\r\n" + details.join("\r\n") : "");
return `${this.callbackType} callback:: count: ${this.map.size}` +
(deleted.length ? "\r\n" + deleted.join("\r\n") : "") +
(details.length ? "\r\n" + details.join("\r\n") : "");
}
private invokeCallback(timeoutId: number) {
const { cb, args, ms, time } = this.map[timeoutId];
const data = this.map.get(timeoutId);
if (!data) return;
const { cb, args, ms, time } = data;
this.map.delete(timeoutId);
this.serializedKeys.delete(timeoutId);
if (ms !== undefined) {
const newTime = ms + time;
if (this.host.getTime() < newTime) {
@@ -209,17 +237,43 @@ class Callbacks {
}
}
cb(...args);
delete this.map[timeoutId];
}
invoke(invokeKey?: number) {
if (invokeKey) return this.invokeCallback(invokeKey);
invokeWorker(invokeKey?: number) {
try {
if (invokeKey) return this.invokeCallback(invokeKey);
// Note: invoking a callback may result in new callbacks been queued,
// so do not clear the entire callback list regardless. Only remove the
// ones we have invoked.
for (const key in this.map) {
this.invokeCallback(Number(key));
// Note: invoking a callback may result in new callbacks been queued,
// so do not clear the entire callback list regardless. Only remove the
// ones we have invoked.
const keys = arrayFrom(this.map.keys());
for (const key of keys) {
this.invokeCallback(key);
}
}
catch (e) {
if (this.swallowExitException && e.message === exitMessage) {
return;
}
throw e;
}
}
switchToBaseliningInvoke(logger: StateLogger, serializeOutputOrder: SerializeOutputOrder) {
this.invoke = invokeKey => {
logger.log(`Before running ${this.log()}`);
this.host.serializeState(logger.logs, serializeOutputOrder);
if (invokeKey !== undefined) logger.log(`Invoking ${this.callbackType} callback:: timeoutId:: ${invokeKey}:: ${this.map.get(invokeKey)!.args[0]}`);
this.invokeWorker(invokeKey);
logger.log(`After running ${this.callbackType} callback:: count: ${this.map.size}`);
this.host.serializeState(logger.logs, serializeOutputOrder);
};
}
serialize(baseline: string[]) {
if (this.hasChanges) {
baseline.push(this.log(/*logChanges*/ true), "");
this.hasChanges = false;
}
}
}
@@ -270,6 +324,18 @@ export interface TestServerHostOptions {
environmentVariables?: Map<string, string>;
}
export type PendingInstallCallback = (
pendingInstallInfo: string,
installedTypingsOrSuccess: string[] | string | boolean,
typingFiles: readonly File[],
onRequestCompleted: typingsInstaller.RequestCompletedAction,
) => void;
export enum SerializeOutputOrder {
None,
BeforeDiff,
AfterDiff,
}
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, ModuleResolutionHost {
args: string[] = [];
@@ -279,8 +345,9 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
private time = timeIncrements;
getCanonicalFileName: (s: string) => string;
toPath: (f: string) => Path;
readonly timeoutCallbacks = new Callbacks(this, "Timeout");
readonly timeoutCallbacks = new Callbacks(this, "Timeout", /*swallowExitException*/ true);
readonly immediateCallbacks = new Callbacks(this, "Immedidate");
readonly pendingInstalls = new Callbacks(this, "PendingInstalls");
readonly screenClears: number[] = [];
readonly watchUtils = createWatchUtils<TestFileWatcher, TestFsWatcher, Path>("PolledWatches", "FsWatches");
@@ -861,15 +928,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
}
runQueuedTimeoutCallbacks(timeoutId?: number) {
try {
this.timeoutCallbacks.invoke(timeoutId);
}
catch (e) {
if (e.message === this.exitMessage) {
return;
}
throw e;
}
this.timeoutCallbacks.invoke(timeoutId);
}
runQueuedImmediateCallbacks() {
@@ -884,6 +943,14 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.immediateCallbacks.unregister(timeoutId);
}
scheduleInstall(cb: TimeOutCallback, ...args: any[]) {
this.pendingInstalls.register(cb, args);
}
runPendingInstalls() {
this.pendingInstalls.invoke();
}
createDirectory(directoryName: string): void {
const folder = this.toFsFolder(directoryName);
@@ -945,6 +1012,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
serializeOutput(baseline: string[]) {
const output = this.getOutput();
if (!this.output.length && !this.screenClears.length) return;
let start = 0;
baseline.push("Output::");
for (const screenClear of this.screenClears) {
@@ -957,31 +1025,42 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
this.clearOutput();
}
snap(): Map<Path, FSEntry> {
const result = new Map<Path, FSEntry>();
private snap() {
this.serializedDiff = new Map<Path, FSEntry>();
this.fs.forEach((value, key) => {
const cloneValue = clone(value);
if (isFsFolder(cloneValue)) {
cloneValue.entries = cloneValue.entries.map(clone) as SortedArray<FSEntry>;
}
result.set(key, cloneValue);
this.serializedDiff.set(key, cloneValue);
});
}
return result;
serializeState(baseline: string[], serializeOutput: SerializeOutputOrder) {
if (serializeOutput === SerializeOutputOrder.BeforeDiff) this.serializeOutput(baseline);
this.diff(baseline);
if (serializeOutput === SerializeOutputOrder.AfterDiff) this.serializeOutput(baseline);
this.serializeWatches(baseline);
this.timeoutCallbacks.serialize(baseline);
this.immediateCallbacks.serialize(baseline);
this.pendingInstalls.serialize(baseline);
}
writtenFiles?: Map<Path, number>;
diff(baseline: string[], base = new Map<Path, FSEntry>()) {
private serializedDiff = new Map<Path, FSEntry>();
diff(baseline: string[]) {
this.fs.forEach((newFsEntry, path) => {
diffFsEntry(baseline, base.get(path), newFsEntry, this.inodes?.get(path), this.writtenFiles);
diffFsEntry(baseline, this.serializedDiff.get(path), newFsEntry, this.inodes?.get(path), this.writtenFiles);
});
base.forEach((oldFsEntry, path) => {
this.serializedDiff.forEach((oldFsEntry, path) => {
const newFsEntry = this.fs.get(path);
if (!newFsEntry) {
diffFsEntry(baseline, oldFsEntry, newFsEntry, this.inodes?.get(path), this.writtenFiles);
}
});
baseline.push("");
this.snap();
this.writtenFiles?.clear();
}
serializeWatches(baseline?: string[]) {
@@ -1006,14 +1085,13 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
return fsEntry?.fullPath || realFullPath;
}
readonly exitMessage = "System Exit";
exitCode: number | undefined;
readonly resolvePath = (s: string) => s;
readonly getExecutingFilePath = () => this.executingFilePath;
readonly getCurrentDirectory = () => this.currentDirectory;
exit(exitCode?: number) {
this.exitCode = exitCode;
throw new Error(this.exitMessage);
throw new Error(exitMessage);
}
getEnvironmentVariable(name: string) {
return this.environmentVariables && this.environmentVariables.get(name) || "";
@@ -1,8 +1,5 @@
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import {
createProjectService,
} from "../helpers/tsserver";
import {
createServerHost,
File,
@@ -11,6 +8,7 @@ import {
extractTest,
newLineCharacter,
notImplementedHost,
TestProjectService,
} from "./extract/helpers";
const libFile: File = {
@@ -415,7 +413,7 @@ function testConvertToAsyncFunction(it: Mocha.PendingTestFunction, caption: stri
files.push(moduleFile);
}
const host = createServerHost(files);
const projectService = createProjectService(host, { allowNonBaseliningLogger: true });
const projectService = new TestProjectService(host);
projectService.openClientFile(file.path);
return ts.first(projectService.inferredProjects).getLanguageService();
}
@@ -1,13 +1,44 @@
import {
incrementalVerifier,
} from "../../../../harness/incrementalUtils";
import {
createHasErrorMessageLogger,
} from "../../../../harness/tsserverLogger";
import * as Harness from "../../../_namespaces/Harness";
import * as ts from "../../../_namespaces/ts";
import {
createProjectService,
} from "../../helpers/tsserver";
customTypesMap,
} from "../../helpers/typingsInstaller";
import {
createServerHost,
libFile,
TestServerHost,
} from "../../helpers/virtualFileSystemWithWatch";
export interface TestProjectServiceOptions extends ts.server.ProjectServiceOptions {
host: TestServerHost;
}
export type TestProjectServicePartialOptionsAndHost = Partial<Omit<TestProjectServiceOptions, "typingsInstaller" | "logger" | "host">> & Pick<TestProjectServiceOptions, "host">;
export class TestProjectService extends ts.server.ProjectService {
constructor(optsOrHost: TestServerHost | TestProjectServicePartialOptionsAndHost) {
// eslint-disable-next-line local/no-in-operator
const opts = "host" in optsOrHost ?
optsOrHost :
{ host: optsOrHost };
super({
logger: createHasErrorMessageLogger(),
session: undefined,
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typesMapLocation: customTypesMap.path,
incrementalVerifier,
...opts,
});
}
}
interface Range {
pos: number;
end: number;
@@ -141,7 +172,7 @@ export function testExtractSymbol(caption: string, text: string, baselineFolder:
function makeProgram(f: { path: string; content: string; }, includeLib?: boolean) {
const host = createServerHost(includeLib ? [f, libFile] : [f]); // libFile is expensive to parse repeatedly - only test when required
const projectService = createProjectService(host, { allowNonBaseliningLogger: true });
const projectService = new TestProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
const autoImportProvider = projectService.inferredProjects[0].getLanguageService().getAutoImportProvider();
@@ -166,7 +197,7 @@ export function testExtractSymbolFailed(caption: string, text: string, descripti
content: t.source,
};
const host = createServerHost([f, libFile]);
const projectService = createProjectService(host, { allowNonBaseliningLogger: true });
const projectService = new TestProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram()!;
const sourceFile = program.getSourceFile(f.path)!;
@@ -1,14 +1,12 @@
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import {
createProjectService,
} from "../helpers/tsserver";
import {
createServerHost,
File,
} from "../helpers/virtualFileSystemWithWatch";
import {
newLineCharacter,
TestProjectService,
} from "./extract/helpers";
describe("unittests:: services:: organizeImports", () => {
@@ -985,7 +983,7 @@ export * from "lib";
function makeLanguageService(...files: File[]) {
const host = createServerHost(files);
const projectService = createProjectService(host, { useSingleInferredProject: true, allowNonBaseliningLogger: true });
const projectService = new TestProjectService({ host, useSingleInferredProject: true });
projectService.setCompilerOptionsForInferredProjects({ jsx: files.some(f => f.path.endsWith("x")) ? ts.JsxEmit.React : ts.JsxEmit.None });
files.forEach(f => projectService.openClientFile(f.path));
return projectService.inferredProjects[0].getLanguageService();
+4 -16
View File
@@ -34,7 +34,7 @@ import {
import {
changeToHostTrackingWrittenFiles,
libFile,
TestServerHost,
SerializeOutputOrder,
} from "../helpers/virtualFileSystemWithWatch";
describe("unittests:: tsbuild:: on 'sample1' project", () => {
@@ -317,7 +317,6 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
it("building using getNextInvalidatedProject", () => {
const baseline: string[] = [];
let oldSnap: ReturnType<TestServerHost["snap"]> | undefined;
const system = changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(
getSysForSampleProjectReferences(),
@@ -327,7 +326,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
const host = createSolutionBuilderHostForBaseline(system);
const builder = ts.createSolutionBuilder(host, ["tests"], {});
baseline.push("Input::");
baselineState();
system.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
verifyBuildNextResult(); // core
verifyBuildNextResult(); // logic
verifyBuildNextResult(); // tests
@@ -338,14 +337,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
const project = builder.getNextInvalidatedProject();
const result = project && project.done();
baseline.push(`Project Result:: ${jsonToReadableText({ project: project?.project, result })}`);
baselineState();
}
function baselineState() {
system.serializeOutput(baseline);
system.diff(baseline, oldSnap);
system.writtenFiles.clear();
oldSnap = system.snap();
system.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
}
});
@@ -376,7 +368,6 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
describe("project invalidation", () => {
it("invalidates projects correctly", () => {
const baseline: string[] = [];
let oldSnap: ReturnType<TestServerHost["snap"]> | undefined;
const system = changeToHostTrackingWrittenFiles(
fakes.patchHostForBuildInfoReadWrite(
getSysForSampleProjectReferences(),
@@ -414,10 +405,7 @@ describe("unittests:: tsbuild:: on 'sample1' project", () => {
function baselineState(heading: string) {
baseline.push(heading);
system.serializeOutput(baseline);
system.diff(baseline, oldSnap);
system.writtenFiles.clear();
oldSnap = system.snap();
system.serializeState(baseline, SerializeOutputOrder.BeforeDiff);
}
});
});
@@ -35,7 +35,7 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
});
it("verify building references watches only those projects", () => {
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(getSysForSampleProjectReferences());
const { sys, baseline, cb, getPrograms } = createBaseline(getSysForSampleProjectReferences());
const host = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
const solutionBuilder = ts.createSolutionBuilderWithWatch(host, ["tests"], { watch: true });
solutionBuilder.buildReferences("tests");
@@ -45,7 +45,6 @@ describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
commandLineArgs: ["--b", "--w"],
sys,
baseline,
oldSnap,
getPrograms,
watchOrSolution: solutionBuilder,
});
@@ -54,7 +54,7 @@ export enum e2 { }
export function f22() { } // trailing`,
};
const commandLineArgs = ["--b", "--w"];
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, solution, sharedConfig, sharedIndex, webpackConfig, webpackIndex], { currentDirectory: "/user/username/projects/myproject" }));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, solution, sharedConfig, sharedIndex, webpackConfig, webpackIndex], { currentDirectory: "/user/username/projects/myproject" }));
const buildHost = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
buildHost.getCustomTransformers = getCustomTransformers;
const builder = ts.createSolutionBuilderWithWatch(buildHost, [solution.path], { verbose: true });
@@ -65,7 +65,6 @@ export function f22() { } // trailing`,
commandLineArgs,
sys,
baseline,
oldSnap,
getPrograms,
edits: [
{
@@ -27,7 +27,7 @@ describe("unittests:: tsbuildWatch:: watchEnvironment:: tsbuild:: watchMode:: wi
const allPkgFiles = pkgs(pkgFiles);
const system = createWatchedSystem([libFile, typing, ...flatArray(allPkgFiles)], { currentDirectory: project });
writePkgReferences(system);
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(system);
const { sys, baseline, cb, getPrograms } = createBaseline(system);
const host = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
const solutionBuilder = ts.createSolutionBuilderWithWatch(host, ["tsconfig.json"], { watch: true, verbose: true });
solutionBuilder.build();
@@ -37,7 +37,6 @@ describe("unittests:: tsbuildWatch:: watchEnvironment:: tsbuild:: watchMode:: wi
commandLineArgs: ["--b", "--w"],
sys,
baseline,
oldSnap,
getPrograms,
edits: [
{
@@ -77,7 +76,7 @@ describe("unittests:: tsbuildWatch:: watchEnvironment:: tsbuild:: watchMode:: wi
{
caption: "modify typing file",
edit: sys => sys.writeFile(typing.path, `${typing.content}export const typing1 = 10;`),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
watchOrSolution: solutionBuilder,
@@ -55,7 +55,7 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
path: `/user/username/projects/myproject/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { incremental: true, declaration: true } }),
};
const { sys, baseline, oldSnap: originalSnap } = createBaseline(createWatchedSystem(
const { sys, baseline } = createBaseline(createWatchedSystem(
[aFile, bFile, cFile, dFile, config, libFile],
{ currentDirectory: "/user/username/projects/myproject" },
));
@@ -73,7 +73,6 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
let programs: CommandLineProgram[] = ts.emptyArray;
let oldPrograms: CommandLineProgram[] = ts.emptyArray;
let builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram = undefined!;
let oldSnap = originalSnap;
let cancel = false;
const cancellationToken: ts.CancellationToken = {
isCancellationRequested: () => cancel,
@@ -90,7 +89,7 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
// Cancel on first semantic operation
// Change
oldSnap = applyEdit(
applyEdit(
sys,
baseline,
sys => sys.appendFile(cFile.path, "export function foo() {}"),
@@ -114,7 +113,6 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
getPrograms: () => programs,
oldPrograms,
sys,
oldSnap,
});
// Normal emit again
@@ -128,7 +126,7 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
Harness.Baseline.runBaseline(`tsc/cancellationToken/${scenario.split(" ").join("-")}.js`, baseline.join("\r\n"));
function noChange(caption: string) {
oldSnap = applyEdit(sys, baseline, ts.noop, caption);
applyEdit(sys, baseline, ts.noop, caption);
}
function updatePrograms() {
@@ -162,7 +160,6 @@ describe("unittests:: tsc:: builder cancellationToken", () => {
getPrograms: () => programs,
oldPrograms,
sys,
oldSnap,
});
}
@@ -12,7 +12,6 @@ import {
import {
applyEdit,
createBaseline,
SystemSnap,
verifyTscWatch,
watchBaseline,
} from "../helpers/tscWatch";
@@ -52,21 +51,21 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
{ subScenario, files, optionsToExtend, modifyFs }: VerifyIncrementalWatchEmitInput,
incremental: boolean,
) {
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem(files(), { currentDirectory: project }));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem(files(), { currentDirectory: project }));
if (incremental) sys.exit = exitCode => sys.exitCode = exitCode;
const argsToPass = [incremental ? "-i" : "-w", ...(optionsToExtend || ts.emptyArray)];
baseline.push(`${sys.getExecutingFilePath()} ${argsToPass.join(" ")}`);
let oldPrograms: readonly CommandLineProgram[] = ts.emptyArray;
build(oldSnap);
build();
if (modifyFs) {
const oldSnap = applyEdit(sys, baseline, modifyFs);
build(oldSnap);
applyEdit(sys, baseline, modifyFs);
build();
}
Harness.Baseline.runBaseline(`${ts.isBuild(argsToPass) ? "tsbuild/watchMode" : "tscWatch"}/incremental/${subScenario.split(" ").join("-")}-${incremental ? "incremental" : "watch"}.js`, baseline.join("\r\n"));
function build(oldSnap: SystemSnap) {
function build() {
const closer = ts.executeCommandLine(
sys,
cb,
@@ -77,7 +76,6 @@ describe("unittests:: tsc-watch:: emit file --incremental", () => {
getPrograms,
oldPrograms,
sys,
oldSnap,
});
if (closer) closer.close();
}
@@ -625,7 +625,7 @@ export class A {
path: "/a/d/f3.ts",
content: "export let y = 1;",
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, file1, file2, file3]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([libFile, file1, file2, file3]));
const host = createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [file2.path, file3.path],
system: sys,
@@ -640,11 +640,9 @@ export class A {
getPrograms,
oldPrograms: ts.emptyArray,
sys,
oldSnap,
});
const { cb: cb2, getPrograms: getPrograms2 } = commandLineCallbacks(sys);
const oldSnap2 = sys.snap();
baseline.push("createing separate watcher");
ts.createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [file1.path],
@@ -658,10 +656,8 @@ export class A {
getPrograms: getPrograms2,
oldPrograms: ts.emptyArray,
sys,
oldSnap: oldSnap2,
});
sys.logTimeoutQueueLength();
baseline.push(`First program is not updated:: ${getPrograms() === ts.emptyArray}`);
baseline.push(`Second program is not updated:: ${getPrograms2() === ts.emptyArray}`);
Harness.Baseline.runBaseline(`tscWatch/${scenario}/two-watch-programs-are-not-affected-by-each-other.js`, baseline.join("\r\n"));
@@ -1205,7 +1201,7 @@ declare const eval: any`,
path: "/a/compile",
content: "let x = 1",
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([f, libFile]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([f, libFile]));
const watch = ts.createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [f.path],
system: sys,
@@ -1219,7 +1215,6 @@ declare const eval: any`,
commandLineArgs: ["--w", f.path],
sys,
baseline,
oldSnap,
getPrograms,
watchOrSolution: watch,
});
@@ -2087,7 +2082,7 @@ import { x } from "../b";`,
{
caption: "Add excluded file to project1",
edit: sys => sys.ensureFileOrFolder({ path: `/user/username/projects/myproject/projects/project1/temp/file.d.ts`, content: `declare class file {}` }),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Delete output of class3",
@@ -1,3 +1,6 @@
import {
noop,
} from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
@@ -43,7 +46,7 @@ describe("unittests:: tsc-watch:: projects with references: invoking when refere
},
// not ideal, but currently because of d.ts but no new file is written
// There will be timeout queued even though file contents are same
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: noop,
},
{
caption: "non local edit in logic ts, and build logic",
@@ -31,7 +31,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
content: `foo()`,
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([root, imported, libFile]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([root, imported, libFile]));
const host = createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [root.path],
system: sys,
@@ -48,7 +48,6 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
commandLineArgs: ["--w", root.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [
{
@@ -117,7 +116,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
content: `export const y = 1;`,
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([root, libFile]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([root, libFile]));
const host = createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [root.path],
system: sys,
@@ -146,7 +145,6 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
commandLineArgs: ["--w", root.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [{
caption: "write imported file",
@@ -175,7 +173,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
content: `export const y = 1;export const x = 10;`,
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([root, imported, libFile]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([root, imported, libFile]));
const host = createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({
rootFiles: [root.path],
system: sys,
@@ -202,7 +200,6 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution
commandLineArgs: ["--w", root.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [
{
@@ -411,7 +408,7 @@ declare module "fs" {
path: `/user/username/projects/myproject/node_modules/.cache/babel-loader/89c02171edab901b9926470ba6d5677e.ts`,
content: jsonToReadableText({ something: 10 }),
}),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
});
@@ -467,8 +464,7 @@ declare namespace myapp {
{
caption: "No change, just check program",
edit: ts.noop,
timeouts: (sys, [[oldProgram, oldBuilderProgram]], watchorSolution) => {
sys.logTimeoutQueueLength();
timeouts: (_sys, [[oldProgram, oldBuilderProgram]], watchorSolution) => {
const newProgram = (watchorSolution as ts.WatchOfConfigFile<ts.EmitAndSemanticDiagnosticsBuilderProgram>).getProgram();
assert.strictEqual(newProgram, oldBuilderProgram, "No change so builder program should be same");
assert.strictEqual(newProgram.getProgram(), oldProgram, "No change so program should be same");
@@ -32,7 +32,7 @@ describe("unittests:: tsc-watch:: watchAPI:: with sourceOfProjectReferenceRedire
}
function verifyWatch({ files, config, subScenario }: VerifyWatchInput, alreadyBuilt: boolean) {
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(
const { sys, baseline, cb, getPrograms } = createBaseline(
createWatchedSystem(files),
alreadyBuilt ? (sys, originalRead) => {
solutionBuildWithBaseline(sys, [config], originalRead);
@@ -52,7 +52,6 @@ describe("unittests:: tsc-watch:: watchAPI:: with sourceOfProjectReferenceRedire
commandLineArgs: ["--w", "--p", config],
sys,
baseline,
oldSnap,
getPrograms,
watchOrSolution: watch,
useSourceOfProjectReferenceRedirect: ts.returnTrue,
+14 -25
View File
@@ -45,7 +45,7 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
path: `/user/username/projects/myproject/settings.json`,
content: jsonToReadableText({ content: "Print this" }),
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem(
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem(
[libFile, mainFile, config, settingsJson],
{ currentDirectory: "/user/username/projects/myproject" },
));
@@ -72,7 +72,6 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
commandLineArgs: ["--w", "--p", config.path],
sys,
baseline,
oldSnap,
getPrograms,
watchOrSolution: watch,
});
@@ -81,7 +80,7 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
describe("hasInvalidatedResolutions", () => {
function verifyWatch(subScenario: string, implementHasInvalidatedResolution: boolean) {
it(subScenario, () => {
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem({
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem({
[`/user/username/projects/myproject/tsconfig.json`]: jsonToReadableText({
compilerOptions: { traceResolution: true, extendedDiagnostics: true },
files: ["main.ts"],
@@ -105,7 +104,6 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch with custom module resolu
commandLineArgs: ["--w"],
sys,
baseline,
oldSnap,
getPrograms,
edits: [
{
@@ -149,7 +147,7 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch expose error count to wat
path: `/user/username/projects/myproject/index.ts`,
content: "let compiler = new Compiler(); for (let i = 0; j < 5; i++) {}",
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem(
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem(
[libFile, mainFile, config],
{ currentDirectory: "/user/username/projects/myproject" },
));
@@ -172,7 +170,6 @@ describe("unittests:: tsc-watch:: watchAPI:: tsc-watch expose error count to wat
commandLineArgs: ["--w", "--p", config.path],
sys,
baseline,
oldSnap,
getPrograms,
watchOrSolution: watch,
});
@@ -189,7 +186,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost does not implement s
path: `/user/username/projects/myproject/main.ts`,
content: "const x = 10;",
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem([config, mainFile, libFile]));
const { sys, baseline, cb, getPrograms } = createBaseline(createWatchedSystem([config, mainFile, libFile]));
const host = createWatchCompilerHostOfConfigFileForBaseline({
configFileName: config.path,
system: sys,
@@ -204,13 +201,11 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost does not implement s
commandLineArgs: ["--w", "--p", config.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [{
caption: "Write a file",
edit: sys => sys.writeFile(`/user/username/projects/myproject/bar.ts`, "const y =10;"),
timeouts: sys => {
sys.logTimeoutQueueLength();
timeouts: () => {
watch.getProgram();
},
}],
@@ -233,7 +228,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost can add extraFileExt
path: `/user/username/projects/myproject/other.vue`,
content: "",
};
const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(
const { sys, baseline, cb, getPrograms } = createBaseline(
createWatchedSystem([config, mainFile, otherFile, libFile]),
);
const host = createWatchCompilerHostOfConfigFileForBaseline({
@@ -250,7 +245,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost can add extraFileExt
commandLineArgs: ["--w", "--p", config.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [{
caption: "Write a file",
@@ -293,7 +287,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
) {
const { cb, getPrograms } = commandLineCallbacks(sys);
baseline.push(`tsc --w${optionsToExtend?.noEmit ? " --noEmit" : ""}`);
const oldSnap = sys.snap();
const host = createWatchCompilerHostOfConfigFileForBaseline<T>({
configFileName: config.path,
optionsToExtend,
@@ -307,7 +300,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
getPrograms,
oldPrograms: ts.emptyArray,
sys,
oldSnap,
});
watch.close();
}
@@ -354,7 +346,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
}
it("verifies that noEmit is handled on createSemanticDiagnosticsBuilderProgram and typechecking happens only on affected files", () => {
const { sys, baseline, oldSnap, cb, getPrograms, config, mainFile } = createSystem("{}", "export const x = 10;");
const { sys, baseline, cb, getPrograms, config, mainFile } = createSystem("{}", "export const x = 10;");
const host = createWatchCompilerHostOfConfigFileForBaseline({
configFileName: config.path,
optionsToExtend: { noEmit: true },
@@ -369,7 +361,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
commandLineArgs: ["--w", "--p", config.path],
sys,
baseline,
oldSnap,
getPrograms,
edits: [{
caption: "Modify a file",
@@ -469,7 +460,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
applyEdit(sys, baseline, sys => sys.writeFile(mainFile.path, "export const x = 10;"), "Fix error");
const { cb, getPrograms } = commandLineCallbacks(sys);
const oldSnap = sys.snap();
const reportDiagnostic = ts.createDiagnosticReporter(sys, /*pretty*/ true);
const reportWatchStatus = ts.createWatchStatusReporter(sys, /*pretty*/ true);
const host = ts.createWatchCompilerHostOfConfigFile({
@@ -498,7 +488,6 @@ describe("unittests:: tsc-watch:: watchAPI:: when watchHost uses createSemanticD
getPrograms,
oldPrograms: ts.emptyArray,
sys,
oldSnap,
});
Harness.Baseline.runBaseline(`tscWatch/watchApi/semantic-builder-emitOnlyDts.js`, baseline.join("\r\n"));
});
@@ -585,12 +574,12 @@ describe("unittests:: tsc-watch:: watchAPI:: when getParsedCommandLine is implem
{
caption: "Add excluded file to project1",
edit: sys => sys.ensureFileOrFolder({ path: `/user/username/projects/myproject/projects/project1/temp/file.d.ts`, content: `declare class file {}` }),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Add output of class3",
edit: sys => sys.writeFile(`/user/username/projects/myproject/projects/project1/class3.d.ts`, `declare class class3 {}`),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
watchOrSolution: watch,
@@ -622,7 +611,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when getParsedCommandLine is implem
{
caption: "Add excluded file to project1",
edit: sys => sys.ensureFileOrFolder({ path: `/user/username/projects/myproject/projects/project1/temp/file.d.ts`, content: `declare class file {}` }),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Delete output of class3",
@@ -684,7 +673,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when builder emit occurs with emitO
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ true);
baseline.cb(program);
},
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Emit all files",
@@ -693,7 +682,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when builder emit occurs with emitO
program.emit();
baseline.cb(program);
},
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Emit with emitOnlyDts shouldnt emit anything",
@@ -702,7 +691,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when builder emit occurs with emitO
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ true);
baseline.cb(program);
},
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
{
caption: "Emit full should not emit anything",
@@ -711,7 +700,7 @@ describe("unittests:: tsc-watch:: watchAPI:: when builder emit occurs with emitO
program.emit();
baseline.cb(program);
},
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
watchOrSolution: watch,
@@ -282,17 +282,17 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
caption: "Start npm install",
// npm install
edit: sys => sys.createDirectory(`/user/username/projects/myproject/node_modules`),
timeouts: sys => sys.logTimeoutQueueLength(), // To update folder structure
timeouts: ts.noop, // To update folder structure
},
{
caption: "npm install folder creation of file2",
edit: sys => sys.createDirectory(`/user/username/projects/myproject/node_modules/file2`),
timeouts: sys => sys.logTimeoutQueueLength(), // To update folder structure
timeouts: ts.noop, // To update folder structure
},
{
caption: "npm install index file in file2",
edit: sys => sys.writeFile(`/user/username/projects/myproject/node_modules/file2/index.d.ts`, `export const x = 10;`),
timeouts: sys => sys.logTimeoutQueueLength(), // To update folder structure
timeouts: ts.noop, // To update folder structure
},
{
caption: "Updates the program",
@@ -503,7 +503,7 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
{
caption: "Change foo",
edit: sys => sys.replaceFileText(`/user/username/projects/myproject/node_modules/bar/foo.d.ts`, "foo", "fooBar"),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
});
@@ -517,7 +517,7 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
{
caption: "delete fooBar",
edit: sys => sys.deleteFile(`/user/username/projects/myproject/node_modules/bar/fooBar.d.ts`),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
});
@@ -536,7 +536,7 @@ describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different po
{
caption: "add new folder to temp",
edit: sys => sys.ensureFileOrFolder({ path: `/user/username/projects/myproject/node_modules/bar/temp/fooBar/index.d.ts`, content: "export function temp(): string;" }),
timeouts: sys => sys.logTimeoutQueueLength(),
timeouts: ts.noop,
},
],
});
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
commonFile1,
@@ -8,7 +5,7 @@ import {
} from "../helpers/tscWatch";
import {
baselineTsserverLogs,
createSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -36,7 +33,7 @@ ${file.content}`;
content: "let z = 1;",
};
const host = createServerHost([app, file3, commonFile1, commonFile2, libFile, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: { file: app.path },
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -374,7 +371,7 @@ describe("unittests:: tsserver:: autoImportProvider - monorepo", () => {
function setup(files: File[]) {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectService = session.getProjectService();
return {
host,
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -10,9 +7,9 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -35,12 +32,11 @@ describe("unittests:: tsserver:: auxiliaryProject::", () => {
content: `export class B {}`,
};
const host = createServerHost([aTs, bDts, bJs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const projectService = session.getProjectService();
const session = new TestSession(host);
openFilesForSession([aTs], session);
// Open file is in inferred project
const inferredProject = projectService.inferredProjects[0];
const inferredProject = session.getProjectService().inferredProjects[0];
// getNoDtsResolutionProject will create an AuxiliaryProject with a.ts and b.js
session.executeCommandSeq<ts.server.protocol.FindSourceDefinitionRequest>({
@@ -53,7 +49,7 @@ describe("unittests:: tsserver:: auxiliaryProject::", () => {
// The AuxiliaryProject should never be the default project for anything, so
// the ScriptInfo should still report being an orphan, and getting its default
// project should throw.
const bJsScriptInfo = ts.Debug.checkDefined(projectService.getScriptInfo(bJs.path));
const bJsScriptInfo = ts.Debug.checkDefined(session.getProjectService().getScriptInfo(bJs.path));
assert(bJsScriptInfo.isOrphan());
assert(bJsScriptInfo.isContainedByBackgroundProject());
assert.deepEqual(bJsScriptInfo.containingProjects, [auxProject]);
@@ -106,7 +102,7 @@ describe("unittests:: tsserver:: auxiliaryProject::", () => {
[indexFile.path]: indexFile.content,
[libFile.path]: libFile.content,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([indexFile], session);
session.executeCommandSeq<ts.server.protocol.FindSourceDefinitionRequest>({
command: ts.server.protocol.CommandTypes.FindSourceDefinition,
@@ -165,7 +161,7 @@ describe("unittests:: tsserver:: auxiliaryProject::", () => {
[indexFile.path]: indexFile.content,
[libFile.path]: libFile.content,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([indexFile], session);
session.executeCommandSeq<ts.server.protocol.FindSourceDefinitionRequest>({
command: ts.server.protocol.CommandTypes.FindSourceDefinition,
@@ -2,8 +2,7 @@ import {
IncrementalVerifierCallbacks,
} from "../../../harness/incrementalUtils";
import {
createLoggerWithInMemoryLogs,
Logger,
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
@@ -11,11 +10,10 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createProjectService,
createSession,
logDiagnostics,
openFilesForSession,
TestProjectService,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -79,13 +77,13 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
return calledMap;
}
function logCacheEntry(logger: Logger, callback: CalledMaps) {
function logCacheEntry(logger: LoggerWithInMemoryLogs, callback: CalledMaps) {
const result = Array.from<[string, (true | CalledWithFiveArgs)[]], { key: string; count: number; }>(calledMaps[callback].entries(), ([key, arr]) => ({ key, count: arr.length }));
logger.info(`${callback}:: ${jsonToReadableText(result)}`);
calledMaps[callback].clear();
}
function logCacheAndClear(logger: Logger) {
function logCacheAndClear(logger: LoggerWithInMemoryLogs) {
forEachHostProperty(prop => logCacheEntry(logger, prop));
}
@@ -98,9 +96,9 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
}
}
function logSemanticDiagnostics(projectService: TestProjectService, project: ts.server.Project, file: File) {
function logSemanticDiagnostics(session: TestSession, project: ts.server.Project, file: File) {
logDiagnostics(
projectService,
session,
`getSemanticDiagnostics:: ${file.path}`,
project,
project.getLanguageService().getSemanticDiagnostics(file.path),
@@ -120,15 +118,18 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
};
const host = createServerHost([root, imported]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setCompilerOptionsForInferredProjects({ module: ts.ModuleKind.AMD, noLib: true });
projectService.openClientFile(root.path);
const project = projectService.inferredProjects[0];
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({
module: ts.server.protocol.ModuleKind.AMD,
noLib: true,
}, session);
openFilesForSession([root], session);
const project = session.getProjectService().inferredProjects[0];
const rootScriptInfo = project.getRootScriptInfos()[0];
assert.equal(rootScriptInfo.fileName, root.path);
// ensure that imported file was found
logSemanticDiagnostics(projectService, project, imported);
logSemanticDiagnostics(session, project, imported);
const logCacheAndClear = createLoggerTrackingHostCalls(host);
@@ -136,29 +137,33 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
// ensure file has correct number of errors after edit
editContent(`import {x} from "f1";
var x: string = 1;`);
logSemanticDiagnostics(projectService, project, imported);
logCacheAndClear(projectService.logger);
logSemanticDiagnostics(session, project, imported);
logCacheAndClear(session.logger);
// trigger synchronization to make sure that the host will try to find 'f2' module on disk
editContent(`import {x} from "f2"`);
try {
// trigger synchronization to make sure that the host will try to find 'f2' module on disk
logSemanticDiagnostics(projectService, project, imported);
logSemanticDiagnostics(session, project, imported);
}
catch (e) {
projectService.logger.info(e.message);
session.logger.info(e.message);
}
logCacheAndClear(projectService.logger);
logCacheAndClear(session.logger);
editContent(`import {x} from "f1"`);
logSemanticDiagnostics(projectService, project, imported);
logCacheAndClear(projectService.logger);
logSemanticDiagnostics(session, project, imported);
logCacheAndClear(session.logger);
// setting compiler options discards module resolution cache
projectService.setCompilerOptionsForInferredProjects({ module: ts.ModuleKind.AMD, noLib: true, target: ts.ScriptTarget.ES5 });
logSemanticDiagnostics(projectService, project, imported);
logCacheAndClear(projectService.logger);
baselineTsserverLogs("cachingFileSystemInformation", "works using legacy resolution logic", projectService);
setCompilerOptionsForInferredProjectsRequestForSession({
module: ts.server.protocol.ModuleKind.AMD,
noLib: true,
target: ts.server.protocol.ScriptTarget.ES5,
}, session);
logSemanticDiagnostics(session, project, imported);
logCacheAndClear(session.logger);
baselineTsserverLogs("cachingFileSystemInformation", "works using legacy resolution logic", session);
function editContent(newContent: string) {
rootScriptInfo.editContent(0, rootContent.length, newContent);
@@ -178,22 +183,25 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
};
const host = createServerHost([root]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setCompilerOptionsForInferredProjects({ module: ts.ModuleKind.AMD, noLib: true });
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({
module: ts.server.protocol.ModuleKind.AMD,
noLib: true,
}, session);
const logCacheAndClear = createLoggerTrackingHostCalls(host);
projectService.openClientFile(root.path);
const project = projectService.inferredProjects[0];
openFilesForSession([root], session);
const project = session.getProjectService().inferredProjects[0];
const rootScriptInfo = project.getRootScriptInfos()[0];
assert.equal(rootScriptInfo.fileName, root.path);
logSemanticDiagnostics(projectService, project, root);
logCacheAndClear(projectService.logger);
logSemanticDiagnostics(session, project, root);
logCacheAndClear(session.logger);
host.writeFile(imported.path, imported.content);
host.runQueuedTimeoutCallbacks();
logSemanticDiagnostics(projectService, project, root);
logCacheAndClear(projectService.logger);
baselineTsserverLogs("cachingFileSystemInformation", "loads missing files from disk", projectService);
logSemanticDiagnostics(session, project, root);
logCacheAndClear(session.logger);
baselineTsserverLogs("cachingFileSystemInformation", "loads missing files from disk", session);
});
it("when calling goto definition of module", () => {
@@ -239,7 +247,7 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
};
const projectFiles = [clientFile, anotherModuleFile, moduleFile, tsconfigFile];
const host = createServerHost(projectFiles);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([clientFile], session);
const logCacheAndClear = createLoggerTrackingHostCalls(host);
@@ -319,19 +327,19 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
};
const projectFiles = [file1, file2, es2016LibFile, tsconfigFile];
const host = createServerHost(projectFiles, { useCaseSensitiveFileNames });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
const logCacheAndClear = createLoggerTrackingHostCalls(host);
// Create file cookie.ts
host.writeFile(file3.path, file3.content);
host.runQueuedTimeoutCallbacks();
logCacheAndClear(projectService.logger);
logCacheAndClear(session.logger);
projectService.openClientFile(file3.path);
logCacheAndClear(projectService.logger);
baselineTsserverLogs("cachingFileSystemInformation", `watchDirectories for config file with case ${useCaseSensitiveFileNames ? "" : "in"}sensitive file system`, projectService);
openFilesForSession([file3], session);
logCacheAndClear(session.logger);
baselineTsserverLogs("cachingFileSystemInformation", `watchDirectories for config file with case ${useCaseSensitiveFileNames ? "" : "in"}sensitive file system`, session);
});
}
verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
@@ -359,12 +367,12 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
const files = [file1, file2, tsconfig, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
const project = service.configuredProjects.get(tsconfig.path)!;
logSemanticDiagnostics(service, project, file1);
logSemanticDiagnostics(service, project, file2);
const project = session.getProjectService().configuredProjects.get(tsconfig.path)!;
logSemanticDiagnostics(session, project, file1);
logSemanticDiagnostics(session, project, file2);
const debugTypesFile: File = {
path: `${projectLocation}/node_modules/debug/index.d.ts`,
@@ -374,9 +382,9 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions
host.runQueuedTimeoutCallbacks(); // Actual update
logSemanticDiagnostics(service, project, file1);
logSemanticDiagnostics(service, project, file2);
baselineTsserverLogs("cachingFileSystemInformation", `includes the parent folder FLLs in ${resolution} module resolution mode`, service);
logSemanticDiagnostics(session, project, file1);
logSemanticDiagnostics(session, project, file2);
baselineTsserverLogs("cachingFileSystemInformation", `includes the parent folder FLLs in ${resolution} module resolution mode`, session);
}
it("Includes the parent folder FLLs in node module resolution mode", () => {
@@ -428,9 +436,12 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
`,
});
const host = createServerHost([app, libFile, tsconfigJson, packageJson]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setHostConfiguration({ preferences: { includePackageJsonAutoImports: "off" } });
projectService.openClientFile(app.path);
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { includePackageJsonAutoImports: "off" } },
});
openFilesForSession([app], session);
let npmInstallComplete = false;
@@ -532,7 +543,7 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
baselineTsserverLogs(
"cachingFileSystemInformation",
`npm install works when ${timeoutDuringPartialInstallation ? "timeout occurs inbetween installation" : "timeout occurs after installation"}`,
projectService,
session,
);
function verifyAfterPartialOrCompleteNpmInstall() {
@@ -542,7 +553,7 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
host.runQueuedTimeoutCallbacks(); // Actual update
}
else {
projectService.testhost.logTimeoutQueueLength();
session.host.baselineHost("After partial npm install");
}
}
}
@@ -568,11 +579,11 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
const files = [app, tsconfig, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(app.path);
const session = new TestSession(host);
openFilesForSession([app], session);
const project = service.configuredProjects.get(tsconfig.path)!;
logSemanticDiagnostics(service, project, app);
const project = session.getProjectService().configuredProjects.get(tsconfig.path)!;
logSemanticDiagnostics(session, project, app);
const debugTypesFile: File = {
path: `${projectLocation}/node_modules/@types/debug/index.d.ts`,
@@ -587,8 +598,8 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
};
host.writeFile(debugTypesFile.path, debugTypesFile.content);
host.runQueuedTimeoutCallbacks();
logSemanticDiagnostics(service, project, app);
baselineTsserverLogs("cachingFileSystemInformation", "when node_modules dont receive event for the @types file addition", service);
logSemanticDiagnostics(session, project, app);
baselineTsserverLogs("cachingFileSystemInformation", "when node_modules dont receive event for the @types file addition", session);
});
it("when creating new file in symlinked folder", () => {
@@ -615,10 +626,10 @@ describe("unittests:: tsserver:: CachingFileSystemInformation:: tsserverProjectS
}),
};
const host = createServerHost([module1, module2, symlink, config, libFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(`${symlink.path}/module2.ts`);
const session = new TestSession(host);
openFilesForSession([`${symlink.path}/module2.ts`], session);
host.writeFile(`${symlink.path}/module3.ts`, `import * as M from "folder1/module1";`);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("cachingFileSystemInformation", "when creating new file in symlinked folder", service);
baselineTsserverLogs("cachingFileSystemInformation", "when creating new file in symlinked folder", session);
});
});
@@ -1,14 +1,10 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
TestServerCancellationToken,
TestSession,
TestSessionRequest,
} from "../helpers/tsserver";
import {
@@ -33,13 +29,7 @@ describe("unittests:: tsserver:: cancellationToken", () => {
content: "let xyz = 1;",
};
const host = createServerHost([f1]);
const cancellationToken: ts.server.ServerCancellationToken = {
isCancellationRequested: () => false,
setRequest: requestId => session.logger.log(`ServerCancellationToken:: Cancellation Request id:: ${requestId}`),
resetRequest: ts.noop,
};
const session = createSession(host, { cancellationToken, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useCancellationToken: true });
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
@@ -75,13 +65,9 @@ describe("unittests:: tsserver:: cancellationToken", () => {
};
const host = createServerHost([f1, config]);
const logger = createLoggerWithInMemoryLogs(host);
const cancellationToken = new TestServerCancellationToken(logger);
const session = createSession(host, {
canUseEvents: true,
eventHandler: ts.noop,
cancellationToken,
logger,
const session = new TestSession({
host,
useCancellationToken: true,
});
{
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
@@ -112,9 +98,9 @@ describe("unittests:: tsserver:: cancellationToken", () => {
});
// cancel previously issued Geterr
cancellationToken.setRequestToCancel(getErrId);
session.serverCancellationToken.setRequestToCancel(getErrId);
host.runQueuedTimeoutCallbacks();
cancellationToken.resetToken();
session.serverCancellationToken.resetToken();
}
{
const getErrId = session.getNextSeq();
@@ -126,10 +112,10 @@ describe("unittests:: tsserver:: cancellationToken", () => {
// run first step
host.runQueuedTimeoutCallbacks();
cancellationToken.setRequestToCancel(getErrId);
session.serverCancellationToken.setRequestToCancel(getErrId);
host.runQueuedImmediateCallbacks();
cancellationToken.resetToken();
session.serverCancellationToken.resetToken();
}
{
session.executeCommandSeq<ts.server.protocol.GeterrRequest>({
@@ -141,7 +127,7 @@ describe("unittests:: tsserver:: cancellationToken", () => {
// the semanticDiag message
host.runQueuedImmediateCallbacks();
host.runQueuedImmediateCallbacks();
cancellationToken.resetToken();
session.serverCancellationToken.resetToken();
}
{
session.executeCommandSeq<ts.server.protocol.GeterrRequest>({
@@ -171,14 +157,10 @@ describe("unittests:: tsserver:: cancellationToken", () => {
}),
};
const host = createServerHost([f1, config]);
const logger = createLoggerWithInMemoryLogs(host);
const cancellationToken = new TestServerCancellationToken(logger, /*cancelAfterRequest*/ 3);
const session = createSession(host, {
canUseEvents: true,
eventHandler: ts.noop,
cancellationToken,
const session = new TestSession({
host,
throttleWaitMilliseconds: 0,
logger,
useCancellationToken: 3,
});
{
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
@@ -216,7 +198,7 @@ describe("unittests:: tsserver:: cancellationToken", () => {
// Set the next request to be cancellable
// The cancellation token will cancel the request the third time
// isCancellationRequested() is called.
cancellationToken.setRequestToCancel(session.getNextSeq());
session.serverCancellationToken.setRequestToCancel(session.getNextSeq());
let operationCanceledExceptionThrown = false;
try {
@@ -1,6 +1,6 @@
import {
createLoggerWithInMemoryLogs,
Logger,
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
@@ -8,7 +8,6 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openExternalProjectForSession,
openFilesForSession,
protocolTextSpanFromSubstring,
@@ -61,7 +60,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -108,7 +107,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should be up-to-date with the reference map changes", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1, file1Consumer1], session);
@@ -181,7 +180,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should be up-to-date with changes made in non-open files", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
@@ -214,7 +213,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should be up-to-date with deleted files", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -245,7 +244,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should be up-to-date with newly created files", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -297,7 +296,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1, file1Consumer1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -344,7 +343,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
it("should return all files if a global file changed shape", () => {
const { moduleFile1, file1Consumer1, file1Consumer2, moduleFile2, globalFile3, configFile } = files();
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, globalFile3, moduleFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([globalFile3], session);
@@ -375,7 +374,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
command: ts.server.protocol.CommandTypes.CompileOnSaveAffectedFileList,
@@ -397,7 +396,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
command: ts.server.protocol.CommandTypes.CompileOnSaveAffectedFileList,
@@ -423,7 +422,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer2, configFile2, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1, file1Consumer1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -446,7 +445,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.ChangeRequest>({
@@ -481,7 +480,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
};
const host = createServerHost([moduleFile1, file1Consumer1, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1], session);
session.executeCommandSeq<ts.server.protocol.ChangeRequest>({
@@ -509,7 +508,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
content: `import {y} from "./file1Consumer1";`,
};
const host = createServerHost([moduleFile1, file1Consumer1, file1Consumer1Consumer1, globalFile3, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([moduleFile1, file1Consumer1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -561,7 +560,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
export var t2 = 10;`,
};
const host = createServerHost([file1, file2, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -579,7 +578,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
const configFile2: File = { path: "/a/c/tsconfig.json", content: `{ "compileOnSave": true }` };
const host = createServerHost([file1, file2, file3, configFile1, configFile2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2, file3], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -598,7 +597,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
export var x = Foo();`,
};
const host = createServerHost([moduleFile1, referenceFile1, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([referenceFile1], session);
host.deleteFile(moduleFile1.path);
@@ -623,7 +622,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
export var x = Foo();`,
};
const host = createServerHost([referenceFile1, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([referenceFile1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -653,7 +652,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
}),
};
const host = createServerHost([dtsFile, f2, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([dtsFile], session);
openFilesForSession([f2], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -723,7 +722,7 @@ describe("unittests:: tsserver:: compileOnSave:: affected list", () => {
}),
};
const host = createServerHost([f1, f2, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
command: ts.server.protocol.CommandTypes.CompileOnSaveAffectedFileList,
@@ -745,7 +744,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
test("\r\n", logger);
baselineTsserverLogs("compileOnSave", "line endings", { logger });
function test(newLine: string, logger: Logger) {
function test(newLine: string, logger: LoggerWithInMemoryLogs) {
const lines = ["var x = 1;", "var y = 2;"];
const path = "/a/app";
const f = {
@@ -755,7 +754,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
const host = createServerHost([f], { newLine });
logger.host = host;
logger.log(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames} newLine: ${host.newLine}`);
const session = createSession(host, { logger });
const session = new TestSession({ host, logger });
openFilesForSession([f], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveEmitFileRequest>({
command: ts.server.protocol.CommandTypes.CompileOnSaveEmitFile,
@@ -779,7 +778,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
content: `{}`,
};
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveEmitFileRequest>({
@@ -804,9 +803,9 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
path: "/a/b/file3.js",
content: "console.log('file3');",
};
const externalProjectName = "/a/b/externalproject";
const projectFileName = "/a/b/externalproject";
const host = createServerHost([file1, file2, file3, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: toExternalFiles([file1.path, file2.path]),
options: {
@@ -814,7 +813,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
outFile: "dist.js",
compileOnSave: true,
},
projectFileName: externalProjectName,
projectFileName,
}, session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveEmitFileRequest>({
@@ -831,9 +830,9 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
path: `/root/TypeScriptProject3/TypeScriptProject3/${inputFileName}`,
content: "consonle.log('file1');",
};
const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
const projectFileName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
const host = createServerHost([file1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: toExternalFiles([file1.path]),
options: {
@@ -841,7 +840,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
sourceMap: true,
compileOnSave: true,
},
projectFileName: externalProjectName,
projectFileName,
}, session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveEmitFileRequest>({
command: ts.server.protocol.CommandTypes.CompileOnSaveEmitFile,
@@ -883,7 +882,7 @@ describe("unittests:: tsserver:: compileOnSave:: EmitFile test", () => {
content: "const y = 2;",
};
const host = createServerHost([file1, file2, config, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -954,7 +953,7 @@ function bar() {
};
const files = [file1, file2, file3, ...(hasModule ? [module] : ts.emptyArray)];
const host = createServerHost([...files, config, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1060,7 +1059,7 @@ describe("unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRe
};
const files = [libFile, core, app1, app2, app1Config, app2Config];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([app1, app2, core], session);
insertString(session, app1);
insertString(session, app2);
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -32,7 +29,7 @@ describe("unittests:: tsserver:: completions", () => {
};
const host = createServerHost([aTs, bTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs], session);
const requestLocation: ts.server.protocol.FileLocationRequestArgs = {
@@ -182,11 +179,7 @@ export interface BrowserRouterProps {
];
const host = createServerHost(files, { windowsStyleRoot: "c:/" });
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, {
globalTypingsCacheLocation,
logger,
});
const session = new TestSession({ host, globalTypingsCacheLocation });
openFilesForSession([appFile], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
command: ts.server.protocol.CommandTypes.CompletionInfo,
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -170,7 +167,7 @@ describe("unittests:: tsserver:: completionsIncomplete", () => {
function setup(files: File[]) {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectService = session.getProjectService();
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
@@ -1,9 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
baselineTsserverLogs,
createProjectService,
closeFilesForSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -22,17 +21,17 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
content: "{}",
};
const host = createServerHost([f1, configFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, "/a");
const session = new TestSession(host);
openFilesForSession([{ file: f1, projectRootPath: "/a" }], session);
service.closeClientFile(f1.path);
service.openClientFile(f1.path);
baselineTsserverLogs("configFileSerach", "should stop at projectRootPath if given", service);
closeFilesForSession([f1], session);
openFilesForSession([f1], session);
baselineTsserverLogs("configFileSearch", "should stop at projectRootPath if given", session);
});
it("should use projectRootPath when searching for inferred project again", () => {
const projectDir = "/a/b/projects/project";
const configFileLocation = `${projectDir}/src`;
const projectRootPath = "/a/b/projects/project";
const configFileLocation = `${projectRootPath}/src`;
const f1 = {
path: `${configFileLocation}/file1.ts`,
content: "",
@@ -46,18 +45,18 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
content: "{}",
};
const host = createServerHost([f1, libFile, configFile, configFile2]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
const session = new TestSession(host);
openFilesForSession([{ file: f1, projectRootPath }], session);
// Delete config file - should create inferred project and not configured project
host.deleteFile(configFile.path);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configFileSearch", "should use projectRootPath when searching for inferred project again", service);
baselineTsserverLogs("configFileSearch", "should use projectRootPath when searching for inferred project again", session);
});
it("should use projectRootPath when searching for inferred project again 2", () => {
const projectDir = "/a/b/projects/project";
const configFileLocation = `${projectDir}/src`;
const projectRootPath = "/a/b/projects/project";
const configFileLocation = `${projectRootPath}/src`;
const f1 = {
path: `${configFileLocation}/file1.ts`,
content: "",
@@ -71,17 +70,17 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
content: "{}",
};
const host = createServerHost([f1, libFile, configFile, configFile2]);
const service = createProjectService(host, {
const session = new TestSession({
host,
useSingleInferredProject: true,
useInferredProjectPerProjectRoot: true,
logger: createLoggerWithInMemoryLogs(host),
});
service.openClientFile(f1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectDir);
openFilesForSession([{ file: f1, projectRootPath }], session);
// Delete config file - should create inferred project with project root path set
host.deleteFile(configFile.path);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configFileSearch", "should use projectRootPath when searching for inferred project again 2", service);
baselineTsserverLogs("configFileSearch", "should use projectRootPath when searching for inferred project again 2", session);
});
describe("when the opened file is not from project root", () => {
@@ -96,13 +95,13 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
};
function openClientFile(files: File[]) {
const host = createServerHost(files);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, "/a/b/projects/proj");
return { host, projectService };
const session = new TestSession(host);
openFilesForSession([{ file, projectRootPath: "/a/b/projects/proj" }], session);
return { host, session };
}
it("tsconfig for the file exists", () => {
const { host, projectService } = openClientFile([file, libFile, tsconfig]);
const { host, session } = openClientFile([file, libFile, tsconfig]);
host.deleteFile(tsconfig.path);
host.runQueuedTimeoutCallbacks();
@@ -110,11 +109,11 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
host.writeFile(tsconfig.path, tsconfig.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configFileSearch", "tsconfig for the file exists", projectService);
baselineTsserverLogs("configFileSearch", "tsconfig for the file exists", session);
});
it("tsconfig for the file does not exist", () => {
const { host, projectService } = openClientFile([file, libFile]);
const { host, session } = openClientFile([file, libFile]);
host.writeFile(tsconfig.path, tsconfig.content);
host.runQueuedTimeoutCallbacks();
@@ -122,7 +121,7 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
host.deleteFile(tsconfig.path);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configFileSearch", "tsconfig for the file does not exist", projectService);
baselineTsserverLogs("configFileSearch", "tsconfig for the file does not exist", session);
});
});
@@ -131,9 +130,9 @@ describe("unittests:: tsserver:: configFileSearch:: searching for config file",
it(scenario, () => {
const path = `/root/teams/VSCode68/Shared Documents/General/jt-ts-test-workspace/x.js`;
const host = createServerHost([libFile, { path, content: "const x = 10" }], { useCaseSensitiveFileNames: true });
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectRootPath);
baselineTsserverLogs("configFileSearch", scenario, service);
const session = new TestSession(host);
openFilesForSession([{ file: path, projectRootPath }], session);
baselineTsserverLogs("configFileSearch", scenario, session);
});
}
verifyConfigFileWatch("when projectRootPath is not present", /*projectRootPath*/ undefined);
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -14,11 +11,11 @@ import {
} from "../helpers/tscWatch";
import {
baselineTsserverLogs,
createProjectService,
createSession,
closeFilesForSession,
logConfiguredProjectsHasOpenRefStatus,
logInferredProjectsOrphanStatus,
openFilesForSession,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -54,13 +51,9 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([configFile, libFile, file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${jsonToReadableText(configFileErrors)}`);
baselineTsserverLogs("configuredProjects", "create configured project without file list", projectService);
const session = new TestSession(host);
openFilesForSession([file1], session);
baselineTsserverLogs("configuredProjects", "create configured project without file list", session);
});
it("create configured project with the file list", () => {
@@ -86,13 +79,9 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([configFile, libFile, file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${jsonToReadableText(configFileErrors)}`);
baselineTsserverLogs("configuredProjects", "create configured project with the file list", projectService);
const session = new TestSession(host);
openFilesForSession([file1], session);
baselineTsserverLogs("configuredProjects", "create configured project with the file list", session);
});
it("add and then remove a config file in a folder with loose files", () => {
@@ -113,9 +102,8 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const host = createServerHost([libFile, commonFile1, commonFile2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(commonFile1.path);
projectService.openClientFile(commonFile2.path);
const session = new TestSession(host);
openFilesForSession([commonFile1, commonFile2], session);
// Add a tsconfig file
host.writeFile(configFile.path, configFile.content);
@@ -125,7 +113,7 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
host.deleteFile(configFile.path);
host.runQueuedTimeoutCallbacks(); // Refresh inferred projects
baselineTsserverLogs("configuredProjects", "add and then remove a config file in a folder with loose files", projectService);
baselineTsserverLogs("configuredProjects", "add and then remove a config file in a folder with loose files", session);
});
it("add new files to a configured project without file list", () => {
@@ -134,13 +122,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
content: `{}`,
};
const host = createServerHost([commonFile1, libFile, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(commonFile1.path);
const session = new TestSession(host);
openFilesForSession([commonFile1], session);
// add a new ts file
host.writeFile(commonFile2.path, commonFile2.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "add new files to a configured project without file list", projectService);
baselineTsserverLogs("configuredProjects", "add new files to a configured project without file list", session);
});
it("should ignore non-existing files specified in the config file", () => {
@@ -155,10 +143,9 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
}`,
};
const host = createServerHost([commonFile1, commonFile2, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(commonFile1.path);
projectService.openClientFile(commonFile2.path);
baselineTsserverLogs("configuredProjects", "should ignore non-existing files specified in the config file", projectService);
const session = new TestSession(host);
openFilesForSession([commonFile1, commonFile2], session);
baselineTsserverLogs("configuredProjects", "should ignore non-existing files specified in the config file", session);
});
it("handle recreated files correctly", () => {
@@ -167,8 +154,8 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
content: `{}`,
};
const host = createServerHost([commonFile1, commonFile2, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(commonFile1.path);
const session = new TestSession(host);
openFilesForSession([commonFile1], session);
// delete commonFile2
host.deleteFile(commonFile2.path);
@@ -177,7 +164,7 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
// re-add commonFile2
host.writeFile(commonFile2.path, commonFile2.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "handle recreated files correctly", projectService);
baselineTsserverLogs("configuredProjects", "handle recreated files correctly", session);
});
it("files explicitly excluded in config file", () => {
@@ -194,11 +181,10 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([commonFile1, commonFile2, excludedFile1, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([commonFile1, excludedFile1], session);
projectService.openClientFile(commonFile1.path);
projectService.openClientFile(excludedFile1.path);
baselineTsserverLogs("configuredProjects", "files explicitly excluded in config file", projectService);
baselineTsserverLogs("configuredProjects", "files explicitly excluded in config file", session);
});
it("should properly handle module resolution changes in config file", () => {
@@ -229,10 +215,8 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const files = [file1, nodeModuleFile, classicModuleFile, configFile, randomFile];
const host = createServerHost(files);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
projectService.openClientFile(nodeModuleFile.path);
projectService.openClientFile(classicModuleFile.path);
const session = new TestSession(host);
openFilesForSession([file1, nodeModuleFile, classicModuleFile], session);
host.writeFile(
configFile.path,
@@ -246,11 +230,11 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
host.runQueuedTimeoutCallbacks();
// will not remove project 1
logInferredProjectsOrphanStatus(projectService);
logInferredProjectsOrphanStatus(session);
// Open random file and it will reuse first inferred project
projectService.openClientFile(randomFile.path);
baselineTsserverLogs("configuredProjects", "should properly handle module resolution changes in config file", projectService);
openFilesForSession([randomFile], session);
baselineTsserverLogs("configuredProjects", "should properly handle module resolution changes in config file", session);
});
it("should keep the configured project when the opened file is referenced by the project but not its root", () => {
@@ -272,11 +256,11 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
}`,
};
const host = createServerHost([file1, file2, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
projectService.closeClientFile(file1.path);
projectService.openClientFile(file2.path);
baselineTsserverLogs("configuredProjects", "should keep the configured project when the opened file is referenced by the project but not its root", projectService);
const session = new TestSession(host);
openFilesForSession([file1], session);
closeFilesForSession([file1], session);
openFilesForSession([file2], session);
baselineTsserverLogs("configuredProjects", "should keep the configured project when the opened file is referenced by the project but not its root", session);
});
it("should tolerate config file errors and still try to build a project", () => {
@@ -291,9 +275,9 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
}`,
};
const host = createServerHost([commonFile1, commonFile2, libFile, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(commonFile1.path);
baselineTsserverLogs("configuredProjects", "should tolerate config file errors and still try to build a project", projectService);
const session = new TestSession(host);
openFilesForSession([commonFile1], session);
baselineTsserverLogs("configuredProjects", "should tolerate config file errors and still try to build a project", session);
});
it("should reuse same project if file is opened from the configured project that has no open files", () => {
@@ -315,16 +299,16 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
}`,
};
const host = createServerHost([file1, file2, configFile, libFile]);
const projectService = createProjectService(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // file1
const session = new TestSession({ host, useSingleInferredProject: true });
openFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session); // file1
projectService.closeClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No open files
closeFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session); // No open files
projectService.openClientFile(file2.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // file2
baselineTsserverLogs("configuredProjects", "should reuse same project if file is opened from the configured project that has no open files", projectService);
openFilesForSession([file2], session);
logConfiguredProjectsHasOpenRefStatus(session); // file2
baselineTsserverLogs("configuredProjects", "should reuse same project if file is opened from the configured project that has no open files", session);
});
it("should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project", () => {
@@ -342,16 +326,16 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
}`,
};
const host = createServerHost([file1, configFile, libFile]);
const projectService = createProjectService(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // file1
const session = new TestSession({ host, useSingleInferredProject: true });
openFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session); // file1
projectService.closeClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files
closeFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files
projectService.openClientFile(libFile.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files + project closed
baselineTsserverLogs("configuredProjects", "should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project", projectService);
openFilesForSession([libFile], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files + project closed
baselineTsserverLogs("configuredProjects", "should not close configured project after closing last open file, but should be closed on next file open if its not the file from same project", session);
});
it("open file become a part of configured project if it is referenced from root file", () => {
@@ -373,16 +357,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
projectService.openClientFile(file3.path);
const session = new TestSession(host);
openFilesForSession([file1, file3], session);
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks(); // load configured project from disk + ensureProjectsForOpenFiles
logInferredProjectsOrphanStatus(projectService);
baselineTsserverLogs("configuredProjects", "open file become a part of configured project if it is referenced from root file", projectService);
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("configuredProjects", "open file become a part of configured project if it is referenced from root file", session);
});
it("can correctly update configured project when set of root files has changed (new file on disk)", () => {
@@ -400,15 +381,14 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([file1, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file2.path, file2.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "can correctly update configured project when set of root files has changed (new file on disk)", projectService);
baselineTsserverLogs("configuredProjects", "can correctly update configured project when set of root files has changed (new file on disk)", session);
});
it("can correctly update configured project when set of root files has changed (new file in list of files)", () => {
@@ -426,14 +406,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([file1, file2, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(configFile.path, jsonToReadableText({ compilerOptions: {}, files: ["f1.ts", "f2.ts"] }));
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "can correctly update configured project when set of root files has changed (new file in list of files)", projectService);
baselineTsserverLogs("configuredProjects", "can correctly update configured project when set of root files has changed (new file in list of files)", session);
});
it("can update configured project when set of root files was not changed", () => {
@@ -451,14 +430,13 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
};
const host = createServerHost([file1, file2, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(configFile.path, jsonToReadableText({ compilerOptions: { outFile: "out.js" }, files: ["f1.ts", "f2.ts"] }));
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "can update configured project when set of root files was not changed", projectService);
baselineTsserverLogs("configuredProjects", "can update configured project when set of root files was not changed", session);
});
it("Open ref of configured project when open file gets added to the project as part of configured file update", () => {
@@ -485,43 +463,37 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const files = [file1, file2, file3, file4];
const host = createServerHost(files.concat(configFile));
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2, file3, file4], session);
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
projectService.openClientFile(file3.path);
projectService.openClientFile(file4.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // file1 and file3
logConfiguredProjectsHasOpenRefStatus(session); // file1 and file3
host.writeFile(configFile.path, "{}");
host.runQueuedTimeoutCallbacks();
logConfiguredProjectsHasOpenRefStatus(projectService); // file1, file2, file3
logInferredProjectsOrphanStatus(projectService);
logConfiguredProjectsHasOpenRefStatus(session); // file1, file2, file3
logInferredProjectsOrphanStatus(session);
projectService.closeClientFile(file1.path);
projectService.closeClientFile(file2.path);
projectService.closeClientFile(file4.path);
closeFilesForSession([file1, file2, file4], session);
logConfiguredProjectsHasOpenRefStatus(projectService); // file3
logInferredProjectsOrphanStatus(projectService);
logConfiguredProjectsHasOpenRefStatus(session); // file3
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file4.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // file3
openFilesForSession([file4], session);
logConfiguredProjectsHasOpenRefStatus(session); // file3
projectService.closeClientFile(file3.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files
closeFilesForSession([file3], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files
const file5: File = {
path: "/file5.ts",
content: "let zz = 1;",
};
host.writeFile(file5.path, file5.content);
projectService.testhost.baselineHost("File5 written");
projectService.openClientFile(file5.path);
session.host.baselineHost("File5 written");
openFilesForSession([file5], session);
baselineTsserverLogs("configuredProjects", "Open ref of configured project when open file gets added to the project as part of configured file update", projectService);
baselineTsserverLogs("configuredProjects", "Open ref of configured project when open file gets added to the project as part of configured file update", session);
});
it("Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", () => {
@@ -549,30 +521,26 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const files = [file1, file2, file3];
const hostFiles = files.concat(file4, configFile);
const host = createServerHost(hostFiles);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2, file3], session);
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
projectService.openClientFile(file3.path);
logConfiguredProjectsHasOpenRefStatus(session); // file1 and file3
logConfiguredProjectsHasOpenRefStatus(projectService); // file1 and file3
projectService.closeClientFile(file1.path);
projectService.closeClientFile(file3.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files
closeFilesForSession([file1, file3], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files
host.writeFile(configFile.path, "{}");
projectService.testhost.baselineHost("configFile updated");
session.host.baselineHost("configFile updated");
// Time out is not yet run so there is project update pending
logConfiguredProjectsHasOpenRefStatus(projectService); // Pending update and file2 might get into the project
logConfiguredProjectsHasOpenRefStatus(session); // Pending update and file2 might get into the project
projectService.openClientFile(file4.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // Pending update and F2 might get into the project
openFilesForSession([file4], session);
logConfiguredProjectsHasOpenRefStatus(session); // Pending update and F2 might get into the project
host.runQueuedTimeoutCallbacks();
logConfiguredProjectsHasOpenRefStatus(projectService); // file2
logInferredProjectsOrphanStatus(projectService);
baselineTsserverLogs("configuredProjects", "Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", projectService);
logConfiguredProjectsHasOpenRefStatus(session); // file2
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("configuredProjects", "Open ref of configured project when open file gets added to the project as part of configured file update buts its open file references are all closed when the update happens", session);
});
it("files are properly detached when language service is disabled", () => {
@@ -596,17 +564,17 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) => filePath === f2.path ? ts.server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(f1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // f1
const session = new TestSession(host);
openFilesForSession([f1], session);
logConfiguredProjectsHasOpenRefStatus(session); // f1
projectService.closeClientFile(f1.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files
closeFilesForSession([f1], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files
for (const f of [f1, f2, f3]) {
// All the script infos should be present and contain the project since it is still alive.
const scriptInfo = projectService.getScriptInfoForNormalizedPath(ts.server.toNormalizedPath(f.path))!;
projectService.logger.log(`Containing projects for ${f.path}:: ${scriptInfo.containingProjects.map(p => p.projectName).join(",")}`);
const scriptInfo = session.getProjectService().getScriptInfoForNormalizedPath(ts.server.toNormalizedPath(f.path))!;
session.logger.log(`Containing projects for ${f.path}:: ${scriptInfo.containingProjects.map(p => p.projectName).join(",")}`);
}
const f4 = {
@@ -614,14 +582,14 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
content: "var x = 1",
};
host.writeFile(f4.path, f4.content);
projectService.openClientFile(f4.path);
logConfiguredProjectsHasOpenRefStatus(projectService); // No files
openFilesForSession([f4], session);
logConfiguredProjectsHasOpenRefStatus(session); // No files
for (const f of [f1, f2, f3]) {
// All the script infos should not be present since the project is closed and orphan script infos are collected
assert.isUndefined(projectService.getScriptInfoForNormalizedPath(ts.server.toNormalizedPath(f.path)));
assert.isUndefined(session.getProjectService().getScriptInfoForNormalizedPath(ts.server.toNormalizedPath(f.path)));
}
baselineTsserverLogs("configuredProjects", "files are properly detached when language service is disabled", projectService);
baselineTsserverLogs("configuredProjects", "files are properly detached when language service is disabled", session);
});
it("syntactic features work even if language service is disabled", () => {
@@ -640,7 +608,7 @@ describe("unittests:: tsserver:: ConfiguredProjects", () => {
const host = createServerHost([f1, f2, config]);
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) => filePath === f2.path ? ts.server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f1], session);
session.logger.log(`Language languageServiceEnabled:: ${session.getProjectService().configuredProjects.get(config.path)!.languageServiceEnabled}`);
@@ -702,7 +670,7 @@ declare var console: {
};`,
};
const host = createServerHost([barConfig, barIndex, fooConfig, fooIndex, barSymLink, lib2017, libDom]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([fooIndex, barIndex], session);
verifyGetErrRequest({ session, files: [barIndex, fooIndex] });
baselineTsserverLogs("configuredProjects", "when multiple projects are open detects correct default project", session);
@@ -722,9 +690,9 @@ declare var console: {
content: "{}",
};
const host = createServerHost([file, app, tsconfig, libFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file.path);
baselineTsserverLogs("configuredProjects", "when file name starts with caret", service);
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("configuredProjects", "when file name starts with caret", session);
});
describe("when creating new file", () => {
@@ -762,10 +730,7 @@ declare var console: {
} :
config,
]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: {
@@ -870,8 +835,7 @@ foo();`,
const host = createServerHost([barConfig, barIndex, fooBarConfig, fooBarIndex, fooConfig, fooIndex, libFile]);
ensureErrorFreeBuild(host, [fooConfig.path]);
const fooDts = `/user/username/projects/myproject/foo/lib/index.d.ts`;
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const service = session.getProjectService();
const session = new TestSession(host);
openFilesForSession([barIndex, fooBarIndex, fooIndex, fooDts], session);
session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({
command: ts.server.protocol.CommandTypes.GetApplicableRefactors,
@@ -883,7 +847,7 @@ foo();`,
endOffset: 1,
},
});
session.logger.log(`Default project for file: ${fooDts}: ${service.tryGetDefaultProjectForFile(ts.server.toNormalizedPath(fooDts))?.projectName}`);
session.logger.log(`Default project for file: ${fooDts}: ${session.getProjectService().tryGetDefaultProjectForFile(ts.server.toNormalizedPath(fooDts))?.projectName}`);
baselineTsserverLogs("configuredProjects", "when default configured project does not contain the file", session);
});
@@ -923,15 +887,14 @@ foo();`,
};
const host = createServerHost([alphaExtendedConfig, aConfig, aFile, bravoExtendedConfig, bConfig, bFile, ...(additionalFiles || ts.emptyArray)]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
return { host, projectService, aFile, bFile, aConfig, bConfig, alphaExtendedConfig, bravoExtendedConfig };
const session = new TestSession(host);
return { host, session, aFile, bFile, aConfig, bConfig, alphaExtendedConfig, bravoExtendedConfig };
}
it("should watch the extended configs of multiple projects", () => {
const { host, projectService, aFile, bFile, bConfig, alphaExtendedConfig, bravoExtendedConfig } = getService();
const { host, session, aFile, bFile, bConfig, alphaExtendedConfig, bravoExtendedConfig } = getService();
projectService.openClientFile(aFile.path);
projectService.openClientFile(bFile.path);
openFilesForSession([aFile, bFile], session);
host.writeFile(
alphaExtendedConfig.path,
@@ -964,7 +927,7 @@ foo();`,
host.writeFile(alphaExtendedConfig.path, "{}");
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("configuredProjects", "should watch the extended configs of multiple projects", projectService);
baselineTsserverLogs("configuredProjects", "should watch the extended configs of multiple projects", session);
});
it("should stop watching the extended configs of closed projects", () => {
@@ -976,20 +939,16 @@ foo();`,
path: `/user/username/projects/myproject/dummy/tsconfig.json`,
content: "{}",
};
const { projectService, aFile, bFile } = getService([dummy, dummyConfig]);
const { session, aFile, bFile } = getService([dummy, dummyConfig]);
projectService.openClientFile(aFile.path);
projectService.openClientFile(bFile.path);
projectService.openClientFile(dummy.path);
openFilesForSession([aFile, bFile, dummy], session);
projectService.closeClientFile(bFile.path);
projectService.closeClientFile(dummy.path);
projectService.openClientFile(dummy.path);
closeFilesForSession([bFile, dummy], session);
openFilesForSession([dummy], session);
projectService.closeClientFile(aFile.path);
projectService.closeClientFile(dummy.path);
projectService.openClientFile(dummy.path);
baselineTsserverLogs("configuredProjects", "should stop watching the extended configs of closed projects", projectService);
closeFilesForSession([aFile, dummy], session);
openFilesForSession([dummy], session);
baselineTsserverLogs("configuredProjects", "should stop watching the extended configs of closed projects", session);
});
});
});
@@ -1009,12 +968,12 @@ describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories l
};
const host = createServerHost([file1, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.runQueuedTimeoutCallbacks();
// Since file1 refers to config file as the default project, it needs to be kept alive
baselineTsserverLogs("configuredProjects", "should be tolerated without crashing the server", projectService);
baselineTsserverLogs("configuredProjects", "should be tolerated without crashing the server", session);
});
it("should be able to handle @types if input file list is empty", () => {
@@ -1038,11 +997,11 @@ describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories l
content: `export const x: number`,
};
const host = createServerHost([f, config, t1, t2], { currentDirectory: ts.getDirectoryPath(f.path) });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f], session);
projectService.openClientFile(f.path);
// Since f refers to config file as the default project, it needs to be kept alive
baselineTsserverLogs("configuredProjects", "should be able to handle @types if input file list is empty", projectService);
baselineTsserverLogs("configuredProjects", "should be able to handle @types if input file list is empty", session);
});
it("should tolerate invalid include files that start in subDirectory", () => {
@@ -1063,11 +1022,11 @@ describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories l
}),
};
const host = createServerHost([f, config, libFile], { useCaseSensitiveFileNames: true });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f], session);
projectService.openClientFile(f.path);
// Since f refers to config file as the default project, it needs to be kept alive
baselineTsserverLogs("configuredProjects", "should tolerate invalid include files that start in subDirectory", projectService);
baselineTsserverLogs("configuredProjects", "should tolerate invalid include files that start in subDirectory", session);
});
it("Changed module resolution reflected when specifying files list", () => {
@@ -1089,16 +1048,16 @@ describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories l
};
const files = [file1, file2a, configFile, libFile];
const host = createServerHost(files);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file2.path, file2.content);
host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions
host.runQueuedTimeoutCallbacks(); // Actual update
// On next file open the files file2a should be closed and not watched any more
projectService.openClientFile(file2.path);
baselineTsserverLogs("configuredProjects", "changed module resolution reflected when specifying files list", projectService);
openFilesForSession([file2], session);
baselineTsserverLogs("configuredProjects", "changed module resolution reflected when specifying files list", session);
});
it("Failed lookup locations uses parent most node_modules directory", () => {
@@ -1128,9 +1087,9 @@ describe("unittests:: tsserver:: ConfiguredProjects:: non-existing directories l
nonLibFiles.forEach(f => f.path = root + f.path);
const files = nonLibFiles.concat(libFile);
const host = createServerHost(files);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
baselineTsserverLogs("configuredProjects", "failed lookup locations uses parent most node_modules directory", projectService);
const session = new TestSession(host);
openFilesForSession([file1], session);
baselineTsserverLogs("configuredProjects", "failed lookup locations uses parent most node_modules directory", session);
});
});
@@ -1146,7 +1105,7 @@ describe("unittests:: tsserver:: ConfiguredProjects:: when reading tsconfig file
};
const host = createServerHost([file1, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const originalReadFile = host.readFile;
host.readFile = f => {
return f === configFile.path ?
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -8,7 +5,6 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
@@ -108,7 +104,7 @@ describe("unittests:: tsserver:: with declaration file maps:: project references
function makeSampleProjects(addUserTsConfig?: boolean, keepAllFiles?: boolean) {
const host = createServerHost([aTs, aTsconfig, aDtsMap, aDts, bTsconfig, bTs, bDtsMap, bDts, ...(addUserTsConfig ? [userTsForConfigProject, userTsconfig] : [userTs]), dummyFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
checkDeclarationFiles(aTs, session);
checkDeclarationFiles(bTs, session);
@@ -285,7 +281,7 @@ describe("unittests:: tsserver:: with declaration file maps:: project references
};
const host = createServerHost([aTs, aTsconfig, bTs, bTsconfig, aDts, aDtsMap]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
checkDeclarationFiles(aTs, session);
openFilesForSession([bTs], session);
@@ -387,7 +383,7 @@ describe("unittests:: tsserver:: with declaration file maps:: project references
};
const host = createServerHost([aTs, aTsconfig, bTs, bTsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForFileRenameRequest>({
command: ts.server.protocol.CommandTypes.GetEditsForFileRename,
@@ -409,7 +405,7 @@ describe("unittests:: tsserver:: with declaration file maps:: project references
content: jsonToReadableText(aDtsInlinedSources),
};
const host = createServerHost([aTs, aDtsMapInlinedSources, aDts, bTs, bDtsMap, bDts, userTs, dummyFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([userTs], session);
// If config file then userConfig project and bConfig project since it is referenced
@@ -1,9 +1,6 @@
import {
reportDocumentRegistryStats,
} from "../../../harness/incrementalUtils";
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -11,10 +8,8 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createProjectService,
createSession,
openFilesForSession,
TestProjectService,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -37,88 +32,106 @@ describe("unittests:: tsserver:: documentRegistry:: document registry in project
content: jsonToReadableText({ files: ["index.ts"] }),
};
function getProject(service: TestProjectService) {
return service.configuredProjects.get(configFile.path)!;
function getProject(session: TestSession) {
return session.getProjectService().configuredProjects.get(configFile.path)!;
}
function checkProject(service: TestProjectService, moduleIsOrphan: boolean) {
function checkProject(session: TestSession, moduleIsOrphan: boolean) {
// Update the project
const project = getProject(service);
const project = getProject(session);
project.getLanguageService();
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
const moduleInfo = session.getProjectService().getScriptInfo(moduleFile.path)!;
assert.isDefined(moduleInfo);
assert.equal(moduleInfo.isOrphan(), moduleIsOrphan);
service.logger.log("DocumentRegistry::");
service.logger.log(reportDocumentRegistryStats(service.documentRegistry).join("\n"));
session.logger.log("DocumentRegistry::");
session.logger.log(reportDocumentRegistryStats(session.getProjectService().documentRegistry).join("\n"));
}
function createServiceAndHost() {
function createSessionAndHost() {
const host = createServerHost([file, moduleFile, libFile, configFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file.path);
checkProject(service, /*moduleIsOrphan*/ false);
return { host, service };
const session = new TestSession(host);
openFilesForSession([file], session);
checkProject(session, /*moduleIsOrphan*/ false);
return { host, session };
}
function changeFileToNotImportModule(service: TestProjectService) {
const info = service.getScriptInfo(file.path)!;
service.applyChangesToFile(info, ts.singleIterator({ span: { start: 0, length: importModuleContent.length }, newText: "" }));
checkProject(service, /*moduleIsOrphan*/ true);
function changeFileToNotImportModule(session: TestSession) {
session.executeCommandSeq<ts.server.protocol.ChangeRequest>({
command: ts.server.protocol.CommandTypes.Change,
arguments: {
file: file.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: importModuleContent.length + 1,
insertString: "",
},
});
checkProject(session, /*moduleIsOrphan*/ true);
}
function changeFileToImportModule(service: TestProjectService) {
const info = service.getScriptInfo(file.path)!;
service.applyChangesToFile(info, ts.singleIterator({ span: { start: 0, length: 0 }, newText: importModuleContent }));
checkProject(service, /*moduleIsOrphan*/ false);
function changeFileToImportModule(session: TestSession) {
session.executeCommandSeq<ts.server.protocol.ChangeRequest>({
command: ts.server.protocol.CommandTypes.Change,
arguments: {
file: file.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: importModuleContent,
},
});
checkProject(session, /*moduleIsOrphan*/ false);
}
it("Caches the source file if script info is orphan", () => {
const { service } = createServiceAndHost();
const project = getProject(service);
const { session } = createSessionAndHost();
const project = getProject(session);
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
const moduleInfo = session.getProjectService().getScriptInfo(moduleFile.path)!;
const sourceFile = moduleInfo.cacheSourceFile!.sourceFile;
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
// edit file
changeFileToNotImportModule(service);
changeFileToNotImportModule(session);
assert.equal(moduleInfo.cacheSourceFile!.sourceFile, sourceFile);
// write content back
changeFileToImportModule(service);
changeFileToImportModule(session);
assert.equal(moduleInfo.cacheSourceFile!.sourceFile, sourceFile);
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
baselineTsserverLogs("documentRegistry", "Caches the source file if script info is orphan", service);
baselineTsserverLogs("documentRegistry", "Caches the source file if script info is orphan", session);
});
it("Caches the source file if script info is orphan, and orphan script info changes", () => {
const { host, service } = createServiceAndHost();
const project = getProject(service);
const { host, session } = createSessionAndHost();
const project = getProject(session);
const moduleInfo = service.getScriptInfo(moduleFile.path)!;
const moduleInfo = session.getProjectService().getScriptInfo(moduleFile.path)!;
const sourceFile = moduleInfo.cacheSourceFile!.sourceFile;
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
// edit file
changeFileToNotImportModule(service);
changeFileToNotImportModule(session);
assert.equal(moduleInfo.cacheSourceFile!.sourceFile, sourceFile);
const updatedModuleContent = moduleFile.content + "\nexport const b: number;";
host.writeFile(moduleFile.path, updatedModuleContent);
// write content back
changeFileToImportModule(service);
changeFileToImportModule(session);
assert.notEqual(moduleInfo.cacheSourceFile!.sourceFile, sourceFile);
assert.equal(project.getSourceFile(moduleInfo.path), moduleInfo.cacheSourceFile!.sourceFile);
assert.equal(moduleInfo.cacheSourceFile!.sourceFile.text, updatedModuleContent);
baselineTsserverLogs("documentRegistry", "Caches the source file if script info is orphan, and orphan script info changes", service);
baselineTsserverLogs("documentRegistry", "Caches the source file if script info is orphan, and orphan script info changes", session);
});
});
describe("unittests:: tsserver:: documentRegistry:: works when reusing orphan script info with different scriptKind", () => {
it("works when reusing orphan script info with different scriptKind", () => {
const host = createServerHost({});
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
const newText = "exrpot const x = 10;";
const content = `import x from 'react';\n${newText}`;
openFilesForSession([
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -34,7 +31,7 @@ describe("unittests:: tsserver:: duplicate packages", () => {
};
const host = createServerHost([aFooIndex, aFooPackage, bFooIndex, bFooPackage, aUser, bUser, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aUser, bUser], session);
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createProjectService,
createSession,
closeFilesForSession,
openFilesForSession,
protocolFileLocationFromSubstring,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
verifyDynamic,
} from "../helpers/tsserver";
import {
@@ -26,11 +23,11 @@ function verifyPathRecognizedAsDynamic(subscenario: string, path: string) {
var x = 10;`,
};
const host = createServerHost([libFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file.path, file.content);
verifyDynamic(projectService, projectService.toPath(file.path));
const session = new TestSession(host);
openFilesForSession([{ file, content: file.content }], session);
verifyDynamic(session, session.getProjectService().toPath(file.path));
baselineTsserverLogs("dynamicFiles", subscenario, projectService);
baselineTsserverLogs("dynamicFiles", subscenario, session);
});
}
@@ -40,7 +37,7 @@ describe("unittests:: tsserver:: dynamicFiles:: Untitled files", () => {
const aTs: File = { path: "/proj/a.ts", content: "" };
const tsconfig: File = { path: "/proj/tsconfig.json", content: "{}" };
const host = createServerHost([aTs, tsconfig]);
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
openFilesForSession([aTs], session);
@@ -53,7 +50,7 @@ describe("unittests:: tsserver:: dynamicFiles:: Untitled files", () => {
projectRootPath: "/proj",
},
});
verifyDynamic(session.getProjectService(), `/proj/untitled:^untitled-1`);
verifyDynamic(session, `/proj/untitled:^untitled-1`);
session.executeCommandSeq<ts.server.protocol.CodeFixRequest>({
command: ts.server.protocol.CommandTypes.GetCodeFixes,
arguments: {
@@ -74,23 +71,34 @@ describe("unittests:: tsserver:: dynamicFiles:: Untitled files", () => {
content: "{}",
};
const host = createServerHost([config, libFile], { useCaseSensitiveFileNames: true, currentDirectory: "/user/username/projects/myproject" });
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, "/user/username/projects/myproject");
verifyDynamic(service, `/user/username/projects/myproject/${untitledFile}`);
const session = new TestSession(host);
openFilesForSession([{
file: untitledFile,
content: "const x = 10;",
projectRootPath: "/user/username/projects/myproject",
}], session);
verifyDynamic(session, `/user/username/projects/myproject/${untitledFile}`);
const untitled: File = {
path: `/user/username/projects/myproject/Untitled-1.ts`,
content: "const x = 10;",
};
host.writeFile(untitled.path, untitled.content);
service.testhost.logTimeoutQueueLength();
service.openClientFile(untitled.path, untitled.content, /*scriptKind*/ undefined, "/user/username/projects/myproject");
openFilesForSession([{
file: untitled.path,
content: untitled.content,
projectRootPath: "/user/username/projects/myproject",
}], session);
service.closeClientFile(untitledFile);
closeFilesForSession([untitledFile], session);
service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, "/user/username/projects/myproject");
verifyDynamic(service, `/user/username/projects/myproject/${untitledFile}`);
baselineTsserverLogs("dynamicFiles", "opening untitled files", service);
openFilesForSession([{
file: untitledFile,
content: "const x = 10;",
projectRootPath: "/user/username/projects/myproject",
}], session);
verifyDynamic(session, `/user/username/projects/myproject/${untitledFile}`);
baselineTsserverLogs("dynamicFiles", "opening untitled files", session);
});
it("opening and closing untitled files when projectRootPath is different from currentDirectory", () => {
@@ -103,35 +111,49 @@ describe("unittests:: tsserver:: dynamicFiles:: Untitled files", () => {
content: "const y = 10",
};
const host = createServerHost([config, file, libFile], { useCaseSensitiveFileNames: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, "/user/username/projects/myproject");
verifyDynamic(service, `/user/username/projects/myproject/${untitledFile}`);
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
openFilesForSession([{
file: untitledFile,
content: "const x = 10;",
projectRootPath: "/user/username/projects/myproject",
}], session);
verifyDynamic(session, `/user/username/projects/myproject/${untitledFile}`);
// Close untitled file
service.closeClientFile(untitledFile);
closeFilesForSession([untitledFile], session);
// Open file from configured project which should collect inferredProject
service.openClientFile(file.path);
baselineTsserverLogs("dynamicFiles", "opening and closing untitled files when projectRootPath is different from currentDirectory", service);
openFilesForSession([file], session);
baselineTsserverLogs("dynamicFiles", "opening and closing untitled files when projectRootPath is different from currentDirectory", session);
});
it("when changing scriptKind of the untitled files", () => {
const host = createServerHost([libFile], { useCaseSensitiveFileNames: true });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(untitledFile, "const x = 10;", ts.ScriptKind.TS, "/user/username/projects/myproject");
const program = service.inferredProjects[0].getCurrentProgram()!;
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
openFilesForSession([{
file: untitledFile,
content: "const x = 10;",
scriptKindName: "TS",
projectRootPath: "/user/username/projects/myproject",
}], session);
const program = session.getProjectService().inferredProjects[0].getCurrentProgram()!;
const sourceFile = program.getSourceFile(untitledFile)!;
// Close untitled file
service.closeClientFile(untitledFile);
closeFilesForSession([untitledFile], session);
// Open untitled file with different mode
service.openClientFile(untitledFile, "const x = 10;", ts.ScriptKind.TSX, "/user/username/projects/myproject");
const newProgram = service.inferredProjects[0].getCurrentProgram()!;
openFilesForSession([{
file: untitledFile,
content: "const x = 10;",
scriptKindName: "TSX",
projectRootPath: "/user/username/projects/myproject",
}], session);
const newProgram = session.getProjectService().inferredProjects[0].getCurrentProgram()!;
const newSourceFile = newProgram.getSourceFile(untitledFile)!;
assert.notStrictEqual(newProgram, program);
assert.notStrictEqual(newSourceFile, sourceFile);
baselineTsserverLogs("dynamicFiles", "when changing scriptKind of the untitled files", service);
baselineTsserverLogs("dynamicFiles", "when changing scriptKind of the untitled files", session);
});
});
@@ -142,16 +164,16 @@ describe("unittests:: tsserver:: dynamicFiles:: ", () => {
content: "var x = 10;",
};
const host = createServerHost([libFile], { useCaseSensitiveFileNames: true });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({
module: ts.ModuleKind.CommonJS,
module: ts.server.protocol.ModuleKind.CommonJS,
allowJs: true,
allowSyntheticDefaultImports: true,
allowNonTsExtensions: true,
}, session);
openFilesForSession([{ file: file.path, content: "var x = 10;" }], session);
verifyDynamic(session.getProjectService(), `/${file.path}`);
verifyDynamic(session, `/${file.path}`);
session.executeCommandSeq<ts.server.protocol.QuickInfoRequest>({
command: ts.server.protocol.CommandTypes.Quickinfo,
@@ -177,11 +199,10 @@ describe("unittests:: tsserver:: dynamicFiles:: ", () => {
};
it("with useInferredProjectPerProjectRoot", () => {
const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
openFilesForSession([{ file: file.path, projectRootPath: "/user/username/projects/myproject" }], session);
const projectService = session.getProjectService();
verifyDynamic(projectService, `/user/username/projects/myproject/${file.path}`);
verifyDynamic(session, `/user/username/projects/myproject/${file.path}`);
session.executeCommandSeq<ts.server.protocol.OutliningSpansRequest>({
command: ts.server.protocol.CommandTypes.GetOutliningSpans,
@@ -198,9 +219,13 @@ describe("unittests:: tsserver:: dynamicFiles:: ", () => {
it("fails when useInferredProjectPerProjectRoot is false", () => {
const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
try {
projectService.openClientFile(file.path, file.content, /*scriptKind*/ undefined, "/user/username/projects/myproject");
openFilesForSession([{
file,
content: file.content,
projectRootPath: "/user/username/projects/myproject",
}], session);
}
catch (e) {
assert.strictEqual(
@@ -209,8 +234,8 @@ describe("unittests:: tsserver:: dynamicFiles:: ", () => {
);
}
const file2Path = file.path.replace("#1", "#2");
projectService.openClientFile(file2Path, file.content);
baselineTsserverLogs("dynamicFiles", "dynamic file with projectRootPath fails when useInferredProjectPerProjectRoot is false", projectService);
openFilesForSession([{ file: file2Path, content: file.content }], session);
baselineTsserverLogs("dynamicFiles", "dynamic file with projectRootPath fails when useInferredProjectPerProjectRoot is false", session);
});
});
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../../harness/tsserverLogger";
import * as ts from "../../../_namespaces/ts";
import {
jsonToReadableText,
} from "../../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../../helpers/tsserver";
import {
createServerHost,
@@ -32,8 +29,7 @@ describe("unittests:: tsserver:: events:: LargeFileReferencedEvent with large fi
};
files.push(largeFile);
const host = createServerHost(files);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return session;
}
@@ -1,15 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../../harness/tsserverLogger";
import * as ts from "../../../_namespaces/ts";
import {
jsonToReadableText,
} from "../../helpers";
import {
baselineTsserverLogs,
createProjectService,
createSession,
openFilesForSession,
TestSession,
} from "../../helpers/tsserver";
import {
createServerHost,
@@ -39,7 +35,7 @@ describe("unittests:: tsserver:: events:: ProjectLanguageServiceStateEvent", ()
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) => filePath === f2.path ? ts.server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f1], session);
session.logger.log(`Language service enabled: ${session.getProjectService().configuredProjects.get(config.path)!.languageServiceEnabled}`);
@@ -69,11 +65,11 @@ describe("unittests:: tsserver:: events:: ProjectLanguageServiceStateEvent", ()
content: "{}",
};
const host = createServerHost([f1, f2, f3, libFile, config]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(f1.path);
const project = service.configuredProjects.get(config.path)!;
service.logger.info(`languageServiceEnabled: ${project.languageServiceEnabled}`);
service.logger.info(`lastFileExceededProgramSize: ${project.lastFileExceededProgramSize}`);
baselineTsserverLogs("events/projectLanguageServiceState", "large file size is determined correctly", service);
const session = new TestSession(host);
openFilesForSession([f1], session);
const project = session.getProjectService().configuredProjects.get(config.path)!;
session.logger.info(`languageServiceEnabled: ${project.languageServiceEnabled}`);
session.logger.info(`lastFileExceededProgramSize: ${project.lastFileExceededProgramSize}`);
baselineTsserverLogs("events/projectLanguageServiceState", "large file size is determined correctly", session);
});
});
@@ -1,13 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../../harness/tsserverLogger";
import * as ts from "../../../_namespaces/ts";
import {
jsonToReadableText,
} from "../../helpers";
import {
baselineTsserverLogs,
createSession,
createSessionWithCustomEventHandler,
openExternalProjectForSession,
openFilesForSession,
@@ -180,10 +176,6 @@ describe("unittests:: tsserver:: events:: ProjectLoadingStart and ProjectLoading
});
}
verifyProjectLoadingStartAndFinish("when using event handler", host => createSessionWithCustomEventHandler(host));
verifyProjectLoadingStartAndFinish("when using default event handler", host =>
createSession(
host,
{ canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) },
));
verifyProjectLoadingStartAndFinish("when using event handler", createSessionWithCustomEventHandler);
verifyProjectLoadingStartAndFinish("when using default event handler", host => new TestSession(host));
});
@@ -1,13 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../../harness/tsserverLogger";
import * as ts from "../../../_namespaces/ts";
import {
jsonToReadableText,
} from "../../helpers";
import {
baselineTsserverLogs,
createSession,
createSessionWithCustomEventHandler,
openFilesForSession,
TestSession,
@@ -411,19 +407,14 @@ describe("unittests:: tsserver:: events:: ProjectsUpdatedInBackground", () => {
describe("when event handler is not set but session is created with canUseEvents = true", () => {
describe("without noGetErrOnBackgroundUpdate, diagnostics for open files are queued", () => {
verifyProjectsUpdatedInBackgroundEvent("without noGetErrOnBackgroundUpdate", host =>
createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
new TestSession({
host,
noGetErrOnBackgroundUpdate: false,
}));
});
describe("with noGetErrOnBackgroundUpdate, diagnostics for open file are not queued", () => {
verifyProjectsUpdatedInBackgroundEvent("with noGetErrOnBackgroundUpdate", host =>
createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
noGetErrOnBackgroundUpdate: true,
}));
verifyProjectsUpdatedInBackgroundEvent("with noGetErrOnBackgroundUpdate", host => new TestSession(host));
});
});
});
@@ -1,6 +1,6 @@
import {
createLoggerWithInMemoryLogs,
Logger,
LoggerWithInMemoryLogs,
} from "../../../../harness/tsserverLogger";
import {
createWatchUtils,
@@ -10,7 +10,6 @@ import * as ts from "../../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
createSessionWithCustomEventHandler,
openFilesForSession,
TestSession,
@@ -32,7 +31,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
}
function createTestServerHostWithCustomWatch(
logger: Logger,
logger: LoggerWithInMemoryLogs,
) {
const idToClose = new Map<number, () => void>();
const host = logger.host as TestServerHostWithCustomWatch;
@@ -89,8 +88,8 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
function updateFileOnHost(session: TestSession, file: string, log: string) {
// Change b.ts
session.logger.log(log);
session.testhost.writeFile(file, session.testhost.readFile("/user/username/projects/myproject/a.ts")!);
session.testhost.runQueuedTimeoutCallbacks();
session.host.writeFile(file, session.host.readFile("/user/username/projects/myproject/a.ts")!);
session.host.runQueuedTimeoutCallbacks();
}
function addFile(session: TestSession, path: string) {
@@ -102,7 +101,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
arguments: { id: data.id, path, eventType: "create" },
})
);
session.testhost.runQueuedTimeoutCallbacks();
session.host.runQueuedTimeoutCallbacks();
}
function changeFile(session: TestSession, path: string) {
@@ -114,7 +113,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
arguments: { id: data.id, path, eventType: "update" },
})
);
session.testhost.runQueuedTimeoutCallbacks();
session.host.runQueuedTimeoutCallbacks();
}
function setup() {
@@ -131,7 +130,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
it("canUseWatchEvents", () => {
const { host, logger } = setup();
const session = createSessionWithCustomEventHandler(host, { canUseWatchEvents: true, logger }, handleWatchEvents);
const session = createSessionWithCustomEventHandler({ host, canUseWatchEvents: true, logger }, handleWatchEvents);
openFilesForSession(["/user/username/projects/myproject/a.ts"], session);
// Directory watcher
@@ -166,7 +165,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
it("canUseWatchEvents without canUseEvents", () => {
const { host, logger } = setup();
const session = createSession(host, { canUseEvents: false, logger });
const session = new TestSession({ host, canUseEvents: false, canUseWatchEvents: true, logger });
openFilesForSession(["/user/username/projects/myproject/a.ts"], session);
// Directory watcher
@@ -1,14 +1,12 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
closeFilesForSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -69,8 +67,8 @@ describe("unittests:: tsserver:: exportMapCache", () => {
});
it("invalidates the cache when files are deleted", () => {
const { host, projectService, exportMapCache, session } = setup();
projectService.closeClientFile(aTs.path);
const { host, exportMapCache, session } = setup();
closeFilesForSession([aTs], session);
host.deleteFile(aTs.path);
host.runQueuedTimeoutCallbacks();
assert.ok(!exportMapCache.isUsableByFile(bTs.path as ts.Path));
@@ -162,8 +160,7 @@ describe("unittests:: tsserver:: exportMapCache", () => {
}`,
};
const host = createServerHost([utilsTs, classesTs, tsconfig]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const projectService = session.getProjectService();
const session = new TestSession(host);
openFilesForSession([classesTs], session);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
@@ -187,7 +184,7 @@ describe("unittests:: tsserver:: exportMapCache", () => {
},
});
const project = projectService.configuredProjects.get(tsconfig.path)!;
const project = session.getProjectService().configuredProjects.get(tsconfig.path)!;
const exportMapCache = project.getCachedExportInfoMap();
assert.ok(exportMapCache.isUsableByFile(classesTs.path as ts.Path));
assert.ok(!exportMapCache.isEmpty());
@@ -233,13 +230,12 @@ describe("unittests:: tsserver:: exportMapCache", () => {
function setup() {
const host = createServerHost([aTs, bTs, ambientDeclaration, tsconfig, packageJson, mobxPackageJson, mobxDts, exportEqualsMappedType]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs], session);
const projectService = session.getProjectService();
const project = projectService.configuredProjects.get(tsconfig.path)!;
const project = session.getProjectService().configuredProjects.get(tsconfig.path)!;
triggerCompletions();
const checker = project.getLanguageService().getProgram()!.getTypeChecker();
return { host, project, projectService, session, exportMapCache: project.getCachedExportInfoMap(), checker, triggerCompletions };
return { host, project, session, exportMapCache: project.getCachedExportInfoMap(), checker, triggerCompletions };
function triggerCompletions() {
const requestLocation: ts.server.protocol.FileLocationRequestArgs = {
+2 -5
View File
@@ -1,19 +1,16 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
getSymlinkedExtendsSys,
} from "../helpers/extends";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
describe("unittests:: tsserver:: extends::", () => {
it("resolves the symlink path", () => {
const host = getSymlinkedExtendsSys(/*forTsserver*/ true);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/users/user/projects/myproject/src/index.ts"], session);
baselineTsserverLogs("tsserver", "resolves the symlink path", session);
});
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import {
@@ -8,13 +5,13 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createProjectService,
createSession,
closeFilesForSession,
logConfiguredProjectsHasOpenRefStatus,
logInferredProjectsOrphanStatus,
openExternalProjectForSession,
openExternalProjectsForSession,
openFilesForSession,
TestSession,
toExternalFile,
toExternalFiles,
verifyDynamic,
@@ -40,7 +37,7 @@ describe("unittests:: tsserver:: externalProjects", () => {
};
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -100,7 +97,7 @@ describe("unittests:: tsserver:: externalProjects", () => {
error: undefined,
};
};
const session = createSession(host, { globalPlugins: ["myplugin"], logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, globalPlugins: ["myplugin"] });
openExternalProjectsForSession([p1], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -132,7 +129,7 @@ describe("unittests:: tsserver:: externalProjects", () => {
const p3 = makeProject(f3);
const host = createServerHost([f1, f2, f3]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectsForSession([p1, p2], session);
openExternalProjectsForSession([p1, p3], session);
openExternalProjectsForSession([], session);
@@ -149,38 +146,46 @@ describe("unittests:: tsserver:: externalProjects", () => {
path: "/a/b/f2.ts",
content: "let y =1;",
};
const externalProjectName = "externalproject";
const projectFileName = "externalproject";
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: toExternalFiles([file1.path, file2.path]),
options: {},
projectFileName: externalProjectName,
});
projectFileName,
}, session);
// open client file - should not lead to creation of inferred project
projectService.openClientFile(file1.path, file1.content);
openFilesForSession([file1], session);
// close client file - external project should still exists
projectService.closeClientFile(file1.path);
projectService.closeExternalProject(externalProjectName);
baselineTsserverLogs("externalProjects", "should not close external project with no open files", projectService);
closeFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
baselineTsserverLogs("externalProjects", "should not close external project with no open files", session);
});
it("external project for dynamic file", () => {
const externalProjectName = "^ScriptDocument1 file1.ts";
const projectFileName = "^ScriptDocument1 file1.ts";
const externalFiles = toExternalFiles(["^ScriptDocument1 file1.ts"]);
const host = createServerHost([]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: externalFiles,
options: {},
projectFileName: externalProjectName,
});
projectFileName,
}, session);
verifyDynamic(projectService, "/^scriptdocument1 file1.ts");
verifyDynamic(session, "/^scriptdocument1 file1.ts");
externalFiles[0].content = "let x =1;";
projectService.applyChangesInOpenFiles(externalFiles);
baselineTsserverLogs("externalProjects", "external project for dynamic file", projectService);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
openFiles: externalFiles,
},
});
baselineTsserverLogs("externalProjects", "external project for dynamic file", session);
});
it("when file name starts with ^", () => {
@@ -193,16 +198,16 @@ describe("unittests:: tsserver:: externalProjects", () => {
content: "const y = 10;",
};
const host = createServerHost([file, app, libFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openExternalProjects([{
const session = new TestSession(host);
openExternalProjectsForSession([{
projectFileName: `/user/username/projects/myproject/myproject.njsproj`,
rootFiles: [
toExternalFile(file.path),
toExternalFile(app.path),
],
options: {},
}]);
baselineTsserverLogs("externalProjects", "when file name starts with caret", service);
}], session);
baselineTsserverLogs("externalProjects", "when file name starts with caret", session);
});
it("external project that included config files", () => {
@@ -236,34 +241,37 @@ describe("unittests:: tsserver:: externalProjects", () => {
path: "/a/d/f3.ts",
content: "let z =1;",
};
const externalProjectName = "externalproject";
const projectFileName = "externalproject";
const host = createServerHost([file1, file2, file3, config1, config2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: toExternalFiles([config1.path, config2.path, file3.path]),
options: {},
projectFileName: externalProjectName,
});
projectFileName,
}, session);
// open client file - should not lead to creation of inferred project
projectService.openClientFile(file1.path, file1.content);
logInferredProjectsOrphanStatus(projectService);
openFilesForSession([file1], session);
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file3.path, file3.content);
logInferredProjectsOrphanStatus(projectService);
openFilesForSession([file3], session);
logInferredProjectsOrphanStatus(session);
projectService.closeExternalProject(externalProjectName);
logInferredProjectsOrphanStatus(projectService);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
logInferredProjectsOrphanStatus(session);
// open file 'file1' from configured project keeps project alive
projectService.closeClientFile(file3.path);
logInferredProjectsOrphanStatus(projectService);
closeFilesForSession([file3], session);
logInferredProjectsOrphanStatus(session);
projectService.closeClientFile(file1.path);
logInferredProjectsOrphanStatus(projectService);
closeFilesForSession([file1], session);
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file2.path, file2.content);
baselineTsserverLogs("externalProjects", "external project that included config files", projectService);
openFilesForSession([file2], session);
baselineTsserverLogs("externalProjects", "external project that included config files", session);
});
it("external project with included config file opened after configured project", () => {
@@ -275,23 +283,26 @@ describe("unittests:: tsserver:: externalProjects", () => {
path: "/a/b/tsconfig.json",
content: jsonToReadableText({ compilerOptions: {} }),
};
const externalProjectName = "externalproject";
const projectFileName = "externalproject";
const host = createServerHost([file1, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
openFilesForSession([file1], session);
projectService.openExternalProject({
openExternalProjectForSession({
rootFiles: toExternalFiles([configFile.path]),
options: {},
projectFileName: externalProjectName,
});
projectFileName,
}, session);
projectService.closeClientFile(file1.path);
closeFilesForSession([file1], session);
// configured project is alive since it is opened as part of external project
projectService.closeExternalProject(externalProjectName);
baselineTsserverLogs("externalProjects", "external project with included config file opened after configured project", projectService);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
baselineTsserverLogs("externalProjects", "external project with included config file opened after configured project", session);
});
it("external project with included config file opened after configured project and then closed", () => {
@@ -307,25 +318,28 @@ describe("unittests:: tsserver:: externalProjects", () => {
path: "/a/b/tsconfig.json",
content: jsonToReadableText({ compilerOptions: {} }),
};
const externalProjectName = "externalproject";
const projectFileName = "externalproject";
const host = createServerHost([file1, file2, libFile, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
openFilesForSession([file1], session);
projectService.openExternalProject({
openExternalProjectForSession({
rootFiles: toExternalFiles([configFile.path]),
options: {},
projectFileName: externalProjectName,
});
projectFileName,
}, session);
projectService.closeExternalProject(externalProjectName);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
// configured project is alive since file is still open
projectService.closeClientFile(file1.path);
closeFilesForSession([file1], session);
projectService.openClientFile(file2.path);
baselineTsserverLogs("externalProjects", "external project with included config file opened after configured project and then closed", projectService);
openFilesForSession([file2], session);
baselineTsserverLogs("externalProjects", "external project with included config file opened after configured project and then closed", session);
});
it("can correctly update external project when set of root files has changed", () => {
@@ -338,12 +352,20 @@ describe("unittests:: tsserver:: externalProjects", () => {
content: "let y = 1",
};
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path]) });
openExternalProjectForSession({
projectFileName: "project",
options: {},
rootFiles: toExternalFiles([file1.path]),
}, session);
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]) });
baselineTsserverLogs("externalProjects", "can correctly update external project when set of root files has changed", projectService);
openExternalProjectForSession({
projectFileName: "project",
options: {},
rootFiles: toExternalFiles([file1.path, file2.path]),
}, session);
baselineTsserverLogs("externalProjects", "can correctly update external project when set of root files has changed", session);
});
it("can update external project when set of root files was not changed", () => {
@@ -361,12 +383,20 @@ describe("unittests:: tsserver:: externalProjects", () => {
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ts.ModuleResolutionKind.Node10 }, rootFiles: toExternalFiles([file1.path, file2.path]) });
openExternalProjectForSession({
projectFileName: "project",
options: { moduleResolution: ts.ModuleResolutionKind.Node10 },
rootFiles: toExternalFiles([file1.path, file2.path]),
}, session);
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ts.ModuleResolutionKind.Classic }, rootFiles: toExternalFiles([file1.path, file2.path]) });
baselineTsserverLogs("externalProjects", "can update external project when set of root files was not changed", projectService);
openExternalProjectForSession({
projectFileName: "project",
options: { moduleResolution: ts.ModuleResolutionKind.Classic },
rootFiles: toExternalFiles([file1.path, file2.path]),
}, session);
baselineTsserverLogs("externalProjects", "can update external project when set of root files was not changed", session);
});
it("language service disabled state is updated in external projects", () => {
@@ -382,30 +412,30 @@ describe("unittests:: tsserver:: externalProjects", () => {
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) => filePath === f2.path ? ts.server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectFileName = "/a/proj.csproj";
service.openExternalProject({
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {},
});
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 1");
}, session);
assert.isFalse(session.getProjectService().externalProjects[0].languageServiceEnabled, "language service should be disabled - 1");
service.openExternalProject({
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path]),
options: {},
});
assert.isTrue(service.externalProjects[0].languageServiceEnabled, "language service should be enabled");
}, session);
assert.isTrue(session.getProjectService().externalProjects[0].languageServiceEnabled, "language service should be enabled");
service.openExternalProject({
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {},
});
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
baselineTsserverLogs("externalProjects", "language service disabled state is updated in external projects", service);
}, session);
assert.isFalse(session.getProjectService().externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
baselineTsserverLogs("externalProjects", "language service disabled state is updated in external projects", session);
});
describe("deleting config file opened from the external project works", () => {
@@ -420,8 +450,11 @@ describe("unittests:: tsserver:: externalProjects", () => {
};
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
const host = createServerHost([libFile, site, configFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject } },
});
const externalProject: ts.server.protocol.ExternalProject = {
projectFileName,
@@ -430,18 +463,28 @@ describe("unittests:: tsserver:: externalProjects", () => {
typeAcquisition: { include: [] },
};
projectService.openExternalProjects([externalProject]);
openExternalProjectsForSession([externalProject], session);
const knownProjects = projectService.synchronizeProjectList([]);
const knownProjects = session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: {
knownProjects: [],
},
}).response as ts.server.protocol.ProjectFilesWithDiagnostics[];
host.deleteFile(configFile.path);
projectService.synchronizeProjectList(ts.map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: {
knownProjects: knownProjects.map(p => p.info!),
},
}).response as ts.server.protocol.ProjectFilesWithDiagnostics[];
externalProject.rootFiles.length = 1;
projectService.openExternalProjects([externalProject]);
openExternalProjectsForSession([externalProject], session);
baselineTsserverLogs("externalProjects", `deleting config file opened from the external project works${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, projectService);
baselineTsserverLogs("externalProjects", `deleting config file opened from the external project works${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, session);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ false);
@@ -466,37 +509,40 @@ describe("unittests:: tsserver:: externalProjects", () => {
content: "",
};
const host = createServerHost([f1, f2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject } },
});
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
const projectFileName = "/a/b/proj1";
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {},
});
projectService.openClientFile(f1.path);
}, session);
openFilesForSession([f1], session);
// rename lib.ts to tsconfig.json
host.renameFile(f2.path, tsconfig.path);
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
options: {},
});
}, session);
if (lazyConfiguredProjectsFromExternalProject) {
projectService.ensureInferredProjectsUpToDate_TestOnly();
session.getProjectService().ensureInferredProjectsUpToDate_TestOnly();
}
// rename tsconfig.json back to lib.ts
host.renameFile(tsconfig.path, f2.path);
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {},
});
baselineTsserverLogs("externalProjects", `correctly handling add or remove tsconfig - 1${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, projectService);
}, session);
baselineTsserverLogs("externalProjects", `correctly handling add or remove tsconfig - 1${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, session);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
@@ -529,55 +575,61 @@ describe("unittests:: tsserver:: externalProjects", () => {
content: "{}",
};
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject } },
});
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
const projectFileName = "/a/b/proj1";
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path]),
options: {},
});
}, session);
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {},
});
}, session);
if (lazyConfiguredProjectsFromExternalProject) {
projectService.ensureInferredProjectsUpToDate_TestOnly();
session.getProjectService().ensureInferredProjectsUpToDate_TestOnly();
}
// remove one config file
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
options: {},
});
}, session);
// remove second config file
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path]),
options: {},
});
}, session);
// open two config files
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {},
});
}, session);
if (lazyConfiguredProjectsFromExternalProject) {
projectService.ensureInferredProjectsUpToDate_TestOnly();
session.getProjectService().ensureInferredProjectsUpToDate_TestOnly();
}
// close all projects - no projects should be opened
projectService.closeExternalProject(projectName);
baselineTsserverLogs("externalProjects", `correctly handling add or remove tsconfig - 2${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, projectService);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
baselineTsserverLogs("externalProjects", `correctly handling add or remove tsconfig - 2${lazyConfiguredProjectsFromExternalProject ? " with lazyConfiguredProjectsFromExternalProject" : ""}`, session);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
@@ -635,13 +687,13 @@ describe("unittests:: tsserver:: externalProjects", () => {
),
};
const host = createServerHost([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(app.path);
const session = new TestSession(host);
openFilesForSession([app], session);
host.writeFile(config2.path, config2.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("externalProjects", "correctly handles changes in lib section of config file", projectService);
baselineTsserverLogs("externalProjects", "correctly handles changes in lib section of config file", session);
});
it("should handle non-existing directories in config file", () => {
@@ -660,15 +712,15 @@ describe("unittests:: tsserver:: externalProjects", () => {
}),
};
const host = createServerHost([f, config]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(f.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
projectService.closeClientFile(f.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
const session = new TestSession(host);
openFilesForSession([f], session);
logConfiguredProjectsHasOpenRefStatus(session);
closeFilesForSession([f], session);
logConfiguredProjectsHasOpenRefStatus(session);
projectService.openClientFile(f.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
baselineTsserverLogs("externalProjects", "should handle non-existing directories in config file", projectService);
openFilesForSession([f], session);
logConfiguredProjectsHasOpenRefStatus(session);
baselineTsserverLogs("externalProjects", "should handle non-existing directories in config file", session);
});
it("handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", () => {
@@ -682,29 +734,38 @@ describe("unittests:: tsserver:: externalProjects", () => {
};
const projectFileName = "/a/b/project.csproj";
const host = createServerHost([f1, config]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: true } });
service.openExternalProject({
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject: true } },
});
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, config.path]),
options: {},
} as ts.server.protocol.ExternalProject);
const project = service.configuredProjects.get(config.path)!;
}, session);
const project = session.getProjectService().configuredProjects.get(config.path)!;
assert.equal(project.pendingUpdateLevel, ts.ProgramUpdateLevel.Full); // External project referenced configured project pending to be reloaded
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject: false } },
});
assert.equal(project.pendingUpdateLevel, ts.ProgramUpdateLevel.Update); // External project referenced configured project loaded
service.closeExternalProject(projectFileName);
session.executeCommandSeq<ts.server.protocol.CloseExternalProjectRequest>({
command: ts.server.protocol.CommandTypes.CloseExternalProject,
arguments: { projectFileName },
});
service.openExternalProject({
openExternalProjectForSession({
projectFileName,
rootFiles: toExternalFiles([f1.path, config.path]),
options: {},
} as ts.server.protocol.ExternalProject);
const project2 = service.configuredProjects.get(config.path)!;
}, session);
const project2 = session.getProjectService().configuredProjects.get(config.path)!;
assert.equal(project2.pendingUpdateLevel, ts.ProgramUpdateLevel.Update); // External project referenced configured project loaded
baselineTsserverLogs("externalProjects", "handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", service);
baselineTsserverLogs("externalProjects", "handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", session);
});
it("handles creation of external project with jsconfig before jsconfig creation watcher is invoked", () => {
@@ -715,24 +776,29 @@ describe("unittests:: tsserver:: externalProjects", () => {
};
const files = [libFile, tsconfig];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Create external project
service.openExternalProjects([{
openExternalProjectsForSession([{
projectFileName,
rootFiles: [{ fileName: tsconfig.path }],
options: { allowJs: false },
}]);
}], session);
// write js file, open external project and open it for edit
const jsFilePath = `/user/username/projects/myproject/javascript.js`;
host.writeFile(jsFilePath, "");
service.openExternalProjects([{
openExternalProjectsForSession([{
projectFileName,
rootFiles: [{ fileName: tsconfig.path }, { fileName: jsFilePath }],
options: { allowJs: false },
}]);
service.applyChangesInOpenFiles(ts.singleIterator({ fileName: jsFilePath, scriptKind: ts.ScriptKind.JS, content: "" }));
}], session);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
openFiles: [{ fileName: jsFilePath, scriptKind: "JS", content: "" }],
},
});
// write jsconfig file
const jsConfig: File = {
@@ -743,13 +809,13 @@ describe("unittests:: tsserver:: externalProjects", () => {
host.ensureFileOrFolder(jsConfig, /*ignoreWatchInvokedWithTriggerAsFileCreate*/ true);
// Open external project
service.openExternalProjects([{
openExternalProjectsForSession([{
projectFileName,
rootFiles: [{ fileName: jsConfig.path }, { fileName: tsconfig.path }, { fileName: jsFilePath }],
options: { allowJs: false },
}]);
logInferredProjectsOrphanStatus(service);
baselineTsserverLogs("externalProjects", "handles creation of external project with jsconfig before jsconfig creation watcher is invoked", service);
}], session);
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("externalProjects", "handles creation of external project with jsconfig before jsconfig creation watcher is invoked", session);
});
it("does not crash if external file does not exist", () => {
@@ -778,10 +844,7 @@ describe("unittests:: tsserver:: externalProjects", () => {
error: undefined,
};
};
const session = createSession(host, {
globalPlugins: ["myplugin"],
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession({ host, globalPlugins: ["myplugin"] });
// When the external project is opened, the graph will be updated,
// and in the process getExternalFiles() above will be called.
// Since the external file does not exist, there will not be a script
@@ -1,12 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
protocol,
} from "../../_namespaces/ts.server";
import {
baselineTsserverLogs,
createSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -80,7 +77,7 @@ const bar: Bar = {
},
];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Open files in the two configured projects
session.executeCommandSeq<protocol.UpdateOpenRequest>({
command: protocol.CommandTypes.UpdateOpen,
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -8,9 +5,9 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
protocolTextSpanFromSubstring,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -51,7 +48,7 @@ describe("unittests:: tsserver:: forceConsistentCasingInFileNames", () => {
};
const host = createServerHost([file1, file2, file2Dts, libFile, tsconfig, tsconfigAll], { useCaseSensitiveFileNames: false });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.CompilerOptionsDiagnosticsRequest>({
@@ -80,7 +77,7 @@ describe("unittests:: tsserver:: forceConsistentCasingInFileNames", () => {
};
const host = createServerHost([loggerFile, anotherFile, tsconfig, libFile, tsconfig]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file: loggerFile, projectRootPath: "/user/username/projects/myproject" }], session);
verifyGetErrRequest({ session, files: [loggerFile] });
@@ -129,7 +126,7 @@ describe("unittests:: tsserver:: forceConsistentCasingInFileNames", () => {
};
const host = createServerHost([loggerFile, anotherFile, tsconfig, libFile, tsconfig]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file: anotherFile, projectRootPath: "/user/username/projects/myproject" }], session);
verifyGetErrRequest({ session, files: [anotherFile] });
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -21,7 +18,7 @@ describe("unittests:: tsserver:: formatSettings", () => {
content: "let x;",
};
const host = createServerHost([f1]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f1], session);
const defaultSettings = session.getProjectService().getFormatCodeOptions(f1.path as ts.server.NormalizedPath);
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -16,7 +13,7 @@ describe("unittests:: tsserver:: getApplicableRefactors", () => {
it("works when taking position", () => {
const aTs: File = { path: "/a.ts", content: "" };
const host = createServerHost([aTs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({
command: ts.server.protocol.CommandTypes.GetApplicableRefactors,
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
textSpanFromSubstring,
} from "../helpers/tsserver";
import {
@@ -83,7 +80,7 @@ describe("unittests:: tsserver:: getEditsForFileRename", () => {
};
const host = createServerHost([aUserTs, aOldTs, aTsconfig, bUserTs, bTsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aUserTs, bUserTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForFileRenameRequest>({
@@ -102,7 +99,7 @@ describe("unittests:: tsserver:: getEditsForFileRename", () => {
const tsconfig: File = { path: "/tsconfig.json", content: jsonToReadableText({ files: ["./a.ts", "./b.ts"] }) };
const host = createServerHost([aTs, cTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, cTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForFileRenameRequest>({
@@ -1,12 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -32,7 +29,7 @@ export const { nest: [valueE, { valueF }] } = { nest: [0, { valueF: 1 }] };
content: "{}",
};
const host = createServerHost([mainTs, modTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([mainTs, modTs], session);
return { session, modTs };
}
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -41,7 +38,7 @@ describe("unittests:: tsserver:: getFileReferences", () => {
function makeSampleSession() {
const host = createServerHost([aTs, bTs, cTs, dTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs, cTs, dTs], session);
return session;
}
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -44,7 +41,7 @@ import { value1 } from "../node_modules/.cache/someFile.d.ts";`,
content: "{}",
};
const host = createServerHost([file1, file2, file3, file3, file4, nodeModulesFile1, nodeModulesFile2, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
command: ts.server.protocol.CommandTypes.GetMoveToRefactoringFileSuggestions,
@@ -69,7 +66,7 @@ import { value1 } from "../node_modules/.cache/someFile.d.ts";`,
const tsconfig: File = { path: "/tsconfig.json", content: jsonToReadableText({ files: ["./file1.ts", "./file2.tsx", "./file3.mts", "./file4.cts", "./file5.js", "./file6.d.ts", "./file7.ts"] }) };
const host = createServerHost([file1, file2, file3, file4, file5, file6, file7, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
@@ -90,7 +87,7 @@ import { value1 } from "../node_modules/.cache/someFile.d.ts";`,
const tsconfig: File = { path: "/tsconfig.json", content: jsonToReadableText({ files: ["./file1.js", "./file2.js", "./file3.mts", "./file4.ts", "./file5.js"] }) };
const host = createServerHost([file1, file2, file3, file4, file5, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
@@ -110,7 +107,7 @@ import { value1 } from "../node_modules/.cache/someFile.d.ts";`,
const tsconfig: File = { path: "/tsconfig.json", content: jsonToReadableText({ files: ["./file1.d.ts", "./a/lib.d.ts", "./a/file3.d.ts", "/a/lib.es6.d.ts"] }) };
const host = createServerHost([file1, file2, file3, file4, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.GetMoveToRefactoringFileSuggestionsRequest>({
@@ -1,12 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
protocol,
} from "../../_namespaces/ts.server";
import {
baselineTsserverLogs,
createSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -45,7 +42,7 @@ declare class Stuff {
},
];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Open files in the two configured projects
session.executeCommandSeq<protocol.UpdateOpenRequest>({
command: protocol.CommandTypes.UpdateOpen,
@@ -112,7 +109,7 @@ declare class Stuff {
},
];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Open files in the two configured projects
session.executeCommandSeq<protocol.UpdateOpenRequest>({
command: protocol.CommandTypes.UpdateOpen,
@@ -1,10 +1,7 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
baselineTsserverLogs,
createSession,
openExternalProjectForSession,
TestSession,
toExternalFile,
} from "../helpers/tsserver";
import {
@@ -22,8 +19,12 @@ describe("unittests:: tsserver:: importHelpers", () => {
content: "",
};
const host = createServerHost([f1, tslib]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openExternalProjectForSession({ projectFileName: "p", rootFiles: [toExternalFile(f1.path)], options: { importHelpers: true } }, session);
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "p",
rootFiles: [toExternalFile(f1.path)],
options: { importHelpers: true },
}, session);
baselineTsserverLogs("importHelpers", "should not crash in tsserver", session);
});
});
@@ -1,10 +1,7 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -13,7 +10,7 @@ import {
describe("unittests:: tsserver:: inconsistentErrorInEditor", () => {
it("should not error", () => {
const host = createServerHost([]);
const session = createSession(host, { canUseEvents: true, noGetErrOnBackgroundUpdate: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({
command: ts.server.protocol.CommandTypes.UpdateOpen,
arguments: {
@@ -45,7 +42,7 @@ describe("unittests:: tsserver:: inconsistentErrorInEditor", () => {
describe("unittests:: tsserver:: inconsistentErrorInEditor2", () => {
it("should not error", () => {
const host = createServerHost([]);
const session = createSession(host, { canUseEvents: true, noGetErrOnBackgroundUpdate: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({
command: ts.server.protocol.CommandTypes.UpdateOpen,
arguments: {
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -14,11 +11,10 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createProjectService,
createSession,
logInferredProjectsOrphanStatus,
openFilesForSession,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -41,9 +37,9 @@ describe("unittests:: tsserver:: inferredProjects", () => {
content: `export let x: number`,
};
const host = createServerHost([appFile, moduleFile, libFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(appFile.path);
baselineTsserverLogs("inferredProjects", "create inferred project", projectService);
const session = new TestSession(host);
openFilesForSession([appFile], session);
baselineTsserverLogs("inferredProjects", "create inferred project", session);
});
it("should use only one inferred project if 'useOneInferredProject' is set", () => {
@@ -71,14 +67,12 @@ describe("unittests:: tsserver:: inferredProjects", () => {
};
const host = createServerHost([file1, file2, file3, libFile]);
const projectService = createProjectService(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
projectService.openClientFile(file3.path);
const session = new TestSession({ host, useSingleInferredProject: true });
openFilesForSession([file1, file2, file3], session);
host.writeFile(configFile.path, configFile.content);
host.runQueuedTimeoutCallbacks(); // load configured project from disk + ensureProjectsForOpenFiles
baselineTsserverLogs("inferredProjects", "should use only one inferred project if useOneInferredProject is set", projectService);
baselineTsserverLogs("inferredProjects", "should use only one inferred project if useOneInferredProject is set", session);
});
it("disable inferred project", () => {
@@ -88,12 +82,12 @@ describe("unittests:: tsserver:: inferredProjects", () => {
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic });
projectService.openClientFile(file1.path, file1.content);
openFilesForSession([file1], session);
projectService.logger.log(`LanguageServiceEnabled:: ${projectService.inferredProjects[0].languageServiceEnabled}`);
baselineTsserverLogs("inferredProjects", "disable inferred project", projectService);
session.logger.log(`LanguageServiceEnabled:: ${session.getProjectService().inferredProjects[0].languageServiceEnabled}`);
baselineTsserverLogs("inferredProjects", "disable inferred project", session);
});
it("project settings for inferred projects", () => {
@@ -106,14 +100,15 @@ describe("unittests:: tsserver:: inferredProjects", () => {
content: "export let x: number",
};
const host = createServerHost([file1, modFile]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
projectService.openClientFile(modFile.path);
projectService.setCompilerOptionsForInferredProjects({ moduleResolution: ts.ModuleResolutionKind.Classic });
openFilesForSession([file1, modFile], session);
setCompilerOptionsForInferredProjectsRequestForSession({
moduleResolution: ts.server.protocol.ModuleResolutionKind.Classic,
}, session);
host.runQueuedTimeoutCallbacks();
logInferredProjectsOrphanStatus(projectService);
baselineTsserverLogs("inferredProjects", "project settings for inferred projects", projectService);
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("inferredProjects", "project settings for inferred projects", session);
});
it("should support files without extensions", () => {
@@ -122,7 +117,7 @@ describe("unittests:: tsserver:: inferredProjects", () => {
content: "let x = 1",
};
const host = createServerHost([f]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({ allowJs: true }, session);
openFilesForSession([{ file: f.path, content: f.content, scriptKindName: "JS" }], session);
baselineTsserverLogs("inferredProjects", "should support files without extensions", session);
@@ -134,19 +129,19 @@ describe("unittests:: tsserver:: inferredProjects", () => {
const file3 = { path: "/b/file2.ts", content: "let x = 3;", projectRootPath: "/b" };
const file4 = { path: "/c/file3.ts", content: "let z = 4;" };
const host = createServerHost([file1, file2, file3, file4]);
const session = createSession(host, {
const session = new TestSession({
host,
useSingleInferredProject: true,
useInferredProjectPerProjectRoot: true,
logger: createLoggerWithInMemoryLogs(host),
});
setCompilerOptionsForInferredProjectsRequestForSession({
allowJs: true,
target: ts.ScriptTarget.ESNext,
target: ts.server.protocol.ScriptTarget.ESNext,
}, session);
setCompilerOptionsForInferredProjectsRequestForSession({
options: {
allowJs: true,
target: ts.ScriptTarget.ES2015,
target: ts.server.protocol.ScriptTarget.ES2015,
},
projectRootPath: "/b",
}, session);
@@ -190,15 +185,15 @@ describe("unittests:: tsserver:: inferredProjects", () => {
{ path: "/c/file3.ts", content: "let z = 4;" },
];
const host = createServerHost(files, { useCaseSensitiveFileNames });
const session = createSession(host, { useSingleInferredProject: true, useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useSingleInferredProject: true, useInferredProjectPerProjectRoot: true });
setCompilerOptionsForInferredProjectsRequestForSession({
allowJs: true,
target: ts.ScriptTarget.ESNext,
target: ts.server.protocol.ScriptTarget.ESNext,
}, session);
setCompilerOptionsForInferredProjectsRequestForSession({
options: {
allowJs: true,
target: ts.ScriptTarget.ES2015,
target: ts.server.protocol.ScriptTarget.ES2015,
},
projectRootPath: "/a",
}, session);
@@ -212,7 +207,7 @@ describe("unittests:: tsserver:: inferredProjects", () => {
setCompilerOptionsForInferredProjectsRequestForSession({
options: {
allowJs: true,
target: ts.ScriptTarget.ES2017,
target: ts.server.protocol.ScriptTarget.ES2017,
},
projectRootPath: "/A",
}, session);
@@ -255,7 +250,7 @@ describe("unittests:: tsserver:: inferredProjects", () => {
content: `const jsFile2 = 10;`,
};
const host = createServerHost([appFile, libFile, config, jsFile1, jsFile2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Do not remove config project when opening jsFile that is not present as part of config project
openFilesForSession([jsFile1], session);
@@ -276,25 +271,24 @@ describe("unittests:: tsserver:: inferredProjects", () => {
const file1 = { path: "/a/file1.js", content: "" };
const host = createServerHost([file1]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
openFilesForSession([file1], session);
const inferredProject = projectService.inferredProjects[0];
projectService.logger.log(`typeAcquisition : setting to undefined`);
const inferredProject = session.getProjectService().inferredProjects[0];
session.logger.log(`typeAcquisition : setting to undefined`);
inferredProject.setTypeAcquisition(undefined);
projectService.logger.log(`typeAcquisition should be inferred for inferred projects: ${jsonToReadableText(inferredProject.getTypeAcquisition())}`);
baselineTsserverLogs("inferredProjects", "regression test - should infer typeAcquisition for inferred projects when set undefined", projectService);
session.logger.log(`typeAcquisition should be inferred for inferred projects: ${jsonToReadableText(inferredProject.getTypeAcquisition())}`);
baselineTsserverLogs("inferredProjects", "regression test - should infer typeAcquisition for inferred projects when set undefined", session);
});
it("Setting compiler options for inferred projects when there are no open files should not schedule any refresh", () => {
const host = createServerHost([commonFile1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({
allowJs: true,
target: ts.ScriptTarget.ES2015,
target: ts.server.protocol.ScriptTarget.ES2015,
}, session);
session.testhost.logTimeoutQueueLength();
baselineTsserverLogs("inferredProjects", "Setting compiler options for inferred projects when there are no open files should not schedule any refresh", session);
});
@@ -322,7 +316,7 @@ describe("unittests:: tsserver:: inferredProjects", () => {
`,
[libFile.path]: libFile.content,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{
file: "/user/username/projects/myproject/app.ts",
projectRootPath: "/user/username/projects/myproject",
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
commonFile1,
@@ -8,7 +5,6 @@ import {
} from "../helpers/tscWatch";
import {
baselineTsserverLogs,
createSession,
TestSession,
} from "../helpers/tsserver";
import {
@@ -29,7 +25,7 @@ describe("unittests:: tsserver:: inlayHints", () => {
it("with updateOpen request does not corrupt documents", () => {
const host = createServerHost([app, commonFile1, commonFile2, libFile, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: { file: app.path },
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -31,7 +28,7 @@ describe("unittests:: tsserver:: jsdocTag:: jsdoc @link ", () => {
it(subScenario, () => {
const { command, displayPartsForJSDoc } = options;
const host = createServerHost([file, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { displayPartsForJSDoc } },
@@ -118,7 +115,7 @@ x(1)`,
const { command, displayPartsForJSDoc } = options;
const host = createServerHost([linkInParamTag, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { displayPartsForJSDoc } },
@@ -171,7 +168,7 @@ foo`,
};
const { command, displayPartsForJSDoc } = options;
const host = createServerHost([linkInParamJSDoc, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { preferences: { displayPartsForJSDoc } },
@@ -1,11 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as Utils from "../../_namespaces/Utils";
import {
baselineTsserverLogs,
createProjectService,
logDiagnostics,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -22,10 +20,9 @@ describe("unittests:: tsserver:: languageService", () => {
content: "let x = 1;",
};
const host = createServerHost([lib, f], { executingFilePath: "/a/Lib/tsc.js", useCaseSensitiveFileNames: true });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(f.path);
projectService.inferredProjects[0].getLanguageService().getProgram();
baselineTsserverLogs("languageService", "should work correctly on case-sensitive file systems", projectService);
const session = new TestSession(host);
openFilesForSession([f], session);
baselineTsserverLogs("languageService", "should work correctly on case-sensitive file systems", session);
});
it("should support multiple projects with the same file under differing `paths` settings", () => {
@@ -67,21 +64,20 @@ describe("unittests:: tsserver:: languageService", () => {
];
const host = createServerHost(files, { executingFilePath: "/project/tsc.js", useCaseSensitiveFileNames: true });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(files[3].path);
projectService.openClientFile(files[6].path);
const session = new TestSession(host);
openFilesForSession([files[3], files[6].path], session);
logDiagnostics(
projectService,
session,
`getSemanticDiagnostics:: ${files[1].path}`,
projectService.configuredProjects.get(files[1].path)!,
projectService.configuredProjects.get(files[1].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(),
session.getProjectService().configuredProjects.get(files[1].path)!,
session.getProjectService().configuredProjects.get(files[1].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(),
);
logDiagnostics(
projectService,
session,
`getSemanticDiagnostics:: ${files[4].path}`,
projectService.configuredProjects.get(files[4].path)!,
projectService.configuredProjects.get(files[4].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(),
session.getProjectService().configuredProjects.get(files[4].path)!,
session.getProjectService().configuredProjects.get(files[4].path)!.getLanguageService().getProgram()!.getSemanticDiagnostics(),
);
baselineTsserverLogs("languageService", "should support multiple projects with the same file under differing paths settings", projectService);
baselineTsserverLogs("languageService", "should support multiple projects with the same file under differing paths settings", session);
});
});
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
jsonToReadableText,
} from "../helpers";
@@ -9,14 +6,14 @@ import {
} from "../helpers/libraryResolution";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
describe("unittests:: tsserver:: libraryResolution", () => {
it("with config", () => {
const host = getServerHostForLibResolution();
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/home/src/projects/project1/index.ts"], session);
host.ensureFileOrFolder({ path: "/home/src/projects/node_modules/@typescript/lib-dom/index.d.ts", content: "interface DOMInterface { }" });
host.runQueuedTimeoutCallbacks();
@@ -61,7 +58,7 @@ describe("unittests:: tsserver:: libraryResolution", () => {
});
it("with config with redirection", () => {
const host = getServerHostForLibResolution(/*libRedirection*/ true);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/home/src/projects/project1/index.ts"], session);
host.deleteFile("/home/src/projects/node_modules/@typescript/lib-dom/index.d.ts");
host.runQueuedTimeoutCallbacks();
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -8,9 +5,9 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -30,13 +27,13 @@ describe("unittests:: tsserver:: maxNodeModuleJsDepth for inferred projects", ()
};
const host = createServerHost([file1, moduleFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.logger.log(`maxNodeModuleJsDepth: ${session.getProjectService().inferredProjects[0].getCompilationSettings().maxNodeModuleJsDepth}`);
// Assert the option sticks
setCompilerOptionsForInferredProjectsRequestForSession({ target: ts.ScriptTarget.ES2016 }, session);
setCompilerOptionsForInferredProjectsRequestForSession({ target: ts.server.protocol.ScriptTarget.ES2016 }, session);
session.logger.log(`maxNodeModuleJsDepth: ${session.getProjectService().inferredProjects[0].getCompilationSettings().maxNodeModuleJsDepth}`);
baselineTsserverLogs("maxNodeModuleJsDepth", "should be set to 2 if the project has js root files", session);
});
@@ -52,7 +49,7 @@ describe("unittests:: tsserver:: maxNodeModuleJsDepth for inferred projects", ()
};
const host = createServerHost([file1, file2, libFile]);
const session = createSession(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useSingleInferredProject: true });
openFilesForSession([file1], session);
session.logger.log(`maxNodeModuleJsDepth: ${session.getProjectService().inferredProjects[0].getCompilationSettings().maxNodeModuleJsDepth}`);
@@ -84,7 +81,7 @@ describe("unittests:: tsserver:: maxNodeModuleJsDepth for inferred projects", ()
`,
[libFile.path]: libFile.content,
});
const session = createSession(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useSingleInferredProject: true });
openFilesForSession(["/user/username/projects/project1/src/file1.js"], session);
baselineTsserverLogs("maxNodeModuleJsDepth", "handles resolutions when currentNodeModulesDepth changes when referencing file from another file", session);
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import {
@@ -8,8 +5,8 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -58,7 +55,7 @@ describe("unittests:: tsserver:: with metadataInResponse::", () => {
it("can pass through metadata when the command returns array", () => {
const host = createHostWithPlugin([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
command: ts.server.protocol.CommandTypes.Completions,
@@ -69,7 +66,7 @@ describe("unittests:: tsserver:: with metadataInResponse::", () => {
it("can pass through metadata when the command returns object", () => {
const host = createHostWithPlugin([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
command: ts.server.protocol.CommandTypes.CompletionInfo,
@@ -81,7 +78,7 @@ describe("unittests:: tsserver:: with metadataInResponse::", () => {
it("returns undefined correctly", () => {
const aTs: File = { path: "/a.ts", content: `class c { prop = "hello"; foo() { const x = 0; } }` };
const host = createHostWithPlugin([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
command: ts.server.protocol.CommandTypes.Completions,
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -22,9 +19,9 @@ import {
} from "../helpers/solutionBuilder";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
protocolTextSpanFromSubstring,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -66,7 +63,7 @@ describe("unittests:: tsserver:: moduleResolution", () => {
`,
};
const host = createServerHost([configFile, fileA, fileB, packageFile, { ...libFile, path: "/a/lib/lib.es2016.full.d.ts" }]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([fileA], session);
return {
host,
@@ -168,7 +165,7 @@ describe("unittests:: tsserver:: moduleResolution", () => {
it("node10Result", () => {
const host = createServerHost(getFsContentsForNode10Result());
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/home/src/projects/project/index.mts"], session);
verifyGetErrRequest({
files: ["/home/src/projects/project/index.mts"],
@@ -239,7 +236,7 @@ describe("unittests:: tsserver:: moduleResolution", () => {
solutionBuildWithBaseline(host, ["packages/package-b"]);
host.clearOutput();
}
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host), canUseEvents: true });
const session = new TestSession(host);
openFilesForSession(["/home/src/projects/project/packages/package-b/src/index.ts"], session);
verifyGetErrRequest({
session,
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -144,10 +141,9 @@ describe("unittests:: tsserver:: moduleSpecifierCache", () => {
function setup() {
const host = createServerHost([aTs, bTs, cTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxPackageJson, mobxDts]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs, cTs], session);
const projectService = session.getProjectService();
const project = projectService.configuredProjects.get(tsconfig.path)!;
const project = session.getProjectService().configuredProjects.get(tsconfig.path)!;
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -161,7 +157,7 @@ function setup() {
});
triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
return { host, project, projectService, session, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
return { host, project, session, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
function triggerCompletions(requestLocation: ts.server.protocol.FileLocationRequestArgs) {
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
+5 -8
View File
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -27,7 +24,7 @@ describe("unittests:: tsserver:: navigate-to for javascript project", () => {
content: "{}",
};
const host = createServerHost([file1, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
// Try to find some interface type defined in lib.d.ts
@@ -73,7 +70,7 @@ describe("unittests:: tsserver:: navigate-to for javascript project", () => {
export const ghijkl = a.abcdef;`,
};
const host = createServerHost([configFile1, file1, configFile2, file2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
session.executeCommandSeq<ts.server.protocol.NavtoRequest>({
@@ -121,7 +118,7 @@ export const ghijkl = a.abcdef;`,
export const ghijkl = a.abcdef;`,
};
const host = createServerHost([configFile1, file1, configFile2, file2, solutionConfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.NavtoRequest>({
@@ -141,7 +138,7 @@ export const ghijkl = a.abcdef;`,
content: "{}",
};
const host = createServerHost([file1, configFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
// Try to find some interface type defined in lib.d.ts
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -20,7 +17,7 @@ describe("unittests:: tsserver:: occurrence highlight on string", () => {
};
const host = createServerHost([file1]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.DocumentHighlightsRequest>({
command: ts.server.protocol.CommandTypes.DocumentHighlights,
+31 -45
View File
@@ -1,12 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createProjectService,
createSession,
openExternalProjectForSession,
openFilesForSession,
protocolTextSpanFromSubstring,
TestSession,
@@ -27,21 +23,17 @@ describe("unittests:: tsserver:: Open-file", () => {
};
const projectFileName = "externalProject";
const host = createServerHost([f]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// create a project
projectService.openExternalProject({ projectFileName, rootFiles: [toExternalFile(f.path)], options: {} });
const p = projectService.externalProjects[0];
// force to load the content of the file
p.updateGraph();
const scriptInfo = p.getScriptInfo(f.path)!;
projectService.logger.log(`Snapshot size: ${scriptInfo.getSnapshot().getLength()}`);
openExternalProjectForSession({
projectFileName,
rootFiles: [toExternalFile(f.path)],
options: {},
}, session);
// open project and replace its content with empty string
projectService.openClientFile(f.path, "");
projectService.logger.log(`Snapshot size: ${scriptInfo.getSnapshot().getLength()}`);
baselineTsserverLogs("openfile", "realoaded with empty content", projectService);
openFilesForSession([{ file: f, content: "" }], session);
baselineTsserverLogs("openfile", "realoaded with empty content", session);
});
function verifyOpenFileWorks(subScenario: string, useCaseSensitiveFileNames: boolean) {
@@ -65,7 +57,7 @@ describe("unittests:: tsserver:: Open-file", () => {
const host = createServerHost([file1, file2, configFile, configFile2], {
useCaseSensitiveFileNames,
});
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Open file1 -> configFile
verifyConfigFileName(file1, "/a");
@@ -77,11 +69,10 @@ describe("unittests:: tsserver:: Open-file", () => {
verifyConfigFileName(file2, "/a/b");
verifyConfigFileName(file2, "/a/B");
baselineTsserverLogs("openfile", subScenario, service);
function verifyConfigFileName(file: File, projectRoot: string) {
const { configFileName } = service.openClientFile(file.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectRoot);
service.logger.log(`file: ${file.path} configFile: ${configFileName}`);
service.closeClientFile(file.path);
baselineTsserverLogs("openfile", subScenario, session);
function verifyConfigFileName(file: File, projectRootPath: string) {
openFilesForSession([{ file, projectRootPath }], session);
closeFilesForSession([file], session);
}
});
}
@@ -89,50 +80,45 @@ describe("unittests:: tsserver:: Open-file", () => {
verifyOpenFileWorks("project root is used with case-insensitive system", /*useCaseSensitiveFileNames*/ false);
it("uses existing project even if project refresh is pending", () => {
const projectFolder = "/user/someuser/projects/myproject";
const projectRootPath = "/user/someuser/projects/myproject";
const aFile: File = {
path: `${projectFolder}/src/a.ts`,
path: `${projectRootPath}/src/a.ts`,
content: "export const x = 0;",
};
const configFile: File = {
path: `${projectFolder}/tsconfig.json`,
path: `${projectRootPath}/tsconfig.json`,
content: "{}",
};
const files = [aFile, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(aFile.path, /*fileContent*/ undefined, ts.ScriptKind.TS, projectFolder);
const session = new TestSession(host);
openFilesForSession([{ file: aFile, projectRootPath }], session);
const bFile: File = {
path: `${projectFolder}/src/b.ts`,
path: `${projectRootPath}/src/b.ts`,
content: `export {}; declare module "./a" { export const y: number; }`,
};
host.writeFile(bFile.path, bFile.content);
service.openClientFile(bFile.path, /*fileContent*/ undefined, ts.ScriptKind.TS, projectFolder);
baselineTsserverLogs("openfile", "uses existing project even if project refresh is pending", service);
openFilesForSession([{ file: bFile, projectRootPath }], session);
baselineTsserverLogs("openfile", "uses existing project even if project refresh is pending", session);
});
it("can open same file again", () => {
const projectFolder = "/user/someuser/projects/myproject";
const projectRootPath = "/user/someuser/projects/myproject";
const aFile: File = {
path: `${projectFolder}/src/a.ts`,
path: `${projectRootPath}/src/a.ts`,
content: "export const x = 0;",
};
const configFile: File = {
path: `${projectFolder}/tsconfig.json`,
path: `${projectRootPath}/tsconfig.json`,
content: "{}",
};
const files = [aFile, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
verifyProject(aFile.content);
verifyProject(`${aFile.content}export const y = 10;`);
baselineTsserverLogs("openfile", "can open same file again", service);
function verifyProject(aFileContent: string) {
service.openClientFile(aFile.path, aFileContent, ts.ScriptKind.TS, projectFolder);
service.logger.log(`aFileContent: ${service.configuredProjects.get(configFile.path)!.getCurrentProgram()?.getSourceFile(aFile.path)!.text}`);
}
const session = new TestSession(host);
openFilesForSession([{ file: aFile, content: aFile.content, projectRootPath }], session);
openFilesForSession([{ file: aFile, content: `${aFile.content}export const y = 10;`, projectRootPath }], session);
baselineTsserverLogs("openfile", "can open same file again", session);
});
it("when file makes edits to add/remove comment directives, they are handled correcrly", () => {
@@ -153,7 +139,7 @@ foo();
bar();`,
};
const host = createServerHost([file, libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
verifyGetErrRequest({ session, files: [file] });
@@ -198,7 +184,7 @@ bar();`,
"/project/tsconfig.json": "{}",
[libFile.path]: libFile.content,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return { host, session };
}
@@ -1,14 +1,11 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -46,7 +43,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
// Add package.json
host.writeFile(packageJson.path, packageJson.content);
session.testhost.baselineHost("Add package.json");
session.host.baselineHost("Add package.json");
let packageJsonInfo = projectService.packageJsonCache.getInDirectory("/" as ts.Path)!;
assert.ok(packageJsonInfo);
assert.ok(packageJsonInfo.dependencies);
@@ -62,7 +59,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
dependencies: undefined,
}),
);
session.testhost.baselineHost("Edit package.json");
session.host.baselineHost("Edit package.json");
packageJsonInfo = projectService.packageJsonCache.getInDirectory("/" as ts.Path)!;
assert.isUndefined(packageJsonInfo.dependencies);
@@ -77,7 +74,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
// Delete package.json
host.deleteFile(packageJson.path);
session.testhost.baselineHost("delete packageJson");
session.host.baselineHost("delete packageJson");
assert.isUndefined(projectService.packageJsonCache.getInDirectory("/" as ts.Path));
baselineTsserverLogs("packageJsonInfo", "finds package.json on demand, watches for deletion, and removes them from cache", session);
});
@@ -87,7 +84,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
const { session, projectService, host } = setup();
// Add package.json in /src
host.writeFile("/src/package.json", packageJson.content);
session.testhost.baselineHost("packageJson");
session.host.baselineHost("packageJson");
assert.lengthOf(projectService.getPackageJsonsVisibleToFile("/a.ts" as ts.Path), 1);
assert.lengthOf(projectService.getPackageJsonsVisibleToFile("/src/b.ts" as ts.Path), 2);
baselineTsserverLogs("packageJsonInfo", "finds multiple package.json files when present", session);
@@ -101,7 +98,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
assert.isFalse(packageJsonInfo.parseable);
host.writeFile(packageJson.path, packageJson.content);
session.testhost.baselineHost("packageJson");
session.host.baselineHost("packageJson");
projectService.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as ts.Path);
const packageJsonInfo2 = projectService.packageJsonCache.getInDirectory("/" as ts.Path)!;
assert.ok(packageJsonInfo2);
@@ -120,7 +117,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
assert.isFalse(packageJsonInfo.parseable);
host.writeFile(packageJson.path, packageJson.content);
session.testhost.baselineHost("PackageJson");
session.host.baselineHost("PackageJson");
projectService.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as ts.Path);
const packageJsonInfo2 = projectService.packageJsonCache.getInDirectory("/" as ts.Path)!;
assert.ok(packageJsonInfo2);
@@ -134,7 +131,7 @@ describe("unittests:: tsserver:: packageJsonInfo::", () => {
function setup(files: readonly File[] = [tsConfig, packageJson]) {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([files[0]], session);
return { host, session, projectService: session.getProjectService() };
}
@@ -1,13 +1,10 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -43,10 +40,10 @@ import { something } from "something";
content: "{}",
};
const host = createServerHost([file1, file2, file3, something, libFile, configFile]);
const session = createSession(host, {
const session = new TestSession({
host,
serverMode: ts.LanguageServiceMode.PartialSemantic,
useSingleInferredProject: true,
logger: createLoggerWithInMemoryLogs(host),
});
return { host, session, file1, file2, file3, something, configFile };
}
@@ -105,10 +102,10 @@ import { something } from "something";
const expectedErrorMessage = "')' expected.";
const host = createServerHost([file1, libFile, configFile]);
const session = createSession(host, {
const session = new TestSession({
host,
serverMode: ts.LanguageServiceMode.PartialSemantic,
useSingleInferredProject: true,
logger: createLoggerWithInMemoryLogs(host),
});
const service = session.getProjectService();
@@ -170,10 +167,10 @@ function fooB() { }`,
content: "{}",
};
const host = createServerHost([file1, file2, file3, something, libFile, configFile]);
const session = createSession(host, {
const session = new TestSession({
host,
serverMode: ts.LanguageServiceMode.PartialSemantic,
useSingleInferredProject: true,
logger: createLoggerWithInMemoryLogs(host),
});
openFilesForSession([file1], session);
baselineTsserverLogs("partialSemanticServer", "should not include referenced files from unopened files", session);
@@ -214,7 +211,7 @@ function fooB() { }`,
content: "",
};
const host = createServerHost([angularFormsDts, angularFormsPackageJson, tsconfig, packageJson, indexTs, libFile]);
const session = createSession(host, { serverMode: ts.LanguageServiceMode.PartialSemantic, useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, serverMode: ts.LanguageServiceMode.PartialSemantic, useSingleInferredProject: true });
const service = session.getProjectService();
openFilesForSession([indexTs], session);
const project = service.inferredProjects[0];
+7 -11
View File
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import {
@@ -8,9 +5,8 @@ import {
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSessionOptions,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -23,7 +19,7 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
const testProtocolCommandRequest = "testProtocolCommandRequest";
const testProtocolCommandResponse = "testProtocolCommandResponse";
function createHostWithPlugin(files: readonly File[], opts?: Partial<TestSessionOptions>) {
function createHostWithPlugin(files: readonly File[], globalPlugins?: readonly string[]) {
const host = createServerHost(files);
host.require = (_initialPath, moduleName) => {
session.logger.log(`Loading plugin: ${moduleName}`);
@@ -42,7 +38,7 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
error: undefined,
};
};
const session = createSession(host, { ...opts, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, globalPlugins });
return { host, session };
}
@@ -74,7 +70,7 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
path: "/tsconfig.json",
content: "{}",
};
const { session } = createHostWithPlugin([aTs, tsconfig, libFile], { globalPlugins: [...expectedToLoad, ...notToLoad] });
const { session } = createHostWithPlugin([aTs, tsconfig, libFile], [...expectedToLoad, ...notToLoad]);
openFilesForSession([aTs], session);
baselineTsserverLogs("plugins", "With global plugins", session);
});
@@ -135,7 +131,7 @@ describe("unittests:: tsserver:: plugins:: loading", () => {
error: undefined,
};
};
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.logger.log(`ExternalFiles:: ${jsonToReadableText(session.getProjectService().configuredProjects.get(tsconfig.path)!.getExternalFiles())}`);
@@ -197,7 +193,7 @@ describe("unittests:: tsserver:: plugins:: overriding getSupportedCodeFixes", ()
error: undefined,
};
};
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs, cTs], session);
// Without arguments
session.executeCommandSeq<ts.server.protocol.GetSupportedCodeFixesRequest>({
@@ -293,7 +289,7 @@ describe("unittests:: tsserver:: plugins:: supportedExtensions::", () => {
error: undefined,
};
};
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host), globalPlugins: ["myplugin"] });
const session = new TestSession({ host, globalPlugins: ["myplugin"] });
openFilesForSession([aTs], session);
host.writeFile("/user/username/projects/myproject/c.vue", "cVue file");
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
defer,
@@ -8,8 +5,8 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -19,7 +16,7 @@ import {
describe("unittests:: tsserver:: pluginsAsync:: async loaded plugins", () => {
function setup(globalPlugins: string[]) {
const host = createServerHost([libFile]);
const session = createSession(host, { canUseEvents: true, globalPlugins, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, globalPlugins });
return { host, session };
}
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -9,11 +6,10 @@ import {
appendAllScriptInfos,
baselineTsserverLogs,
closeFilesForSession,
createProjectService,
createSession,
openExternalProjectForSession,
openFilesForSession,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
TestSessionRequest,
toExternalFiles,
verifyGetErrRequest,
@@ -37,7 +33,7 @@ describe("unittests:: tsserver:: projectErrors::", () => {
content: "",
};
const host = createServerHost([file1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectFileName = "/a/b/test.csproj";
const compilerOptionsRequest: TestSessionRequest<ts.server.protocol.CompilerOptionsDiagnosticsRequest> = {
command: ts.server.protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
@@ -82,7 +78,7 @@ describe("unittests:: tsserver:: projectErrors::", () => {
content: jsonToReadableText({ files: [file1, file2].map(f => ts.getBaseFileName(f.path)) }),
};
const host = createServerHost([file1, config, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
const compilerOptionsRequest: TestSessionRequest<ts.server.protocol.CompilerOptionsDiagnosticsRequest> = {
command: ts.server.protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
@@ -114,7 +110,7 @@ describe("unittests:: tsserver:: projectErrors::", () => {
content: correctConfig.content.substr(1),
};
const host = createServerHost([file1, file2, corruptedConfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
{
@@ -166,7 +162,7 @@ describe("unittests:: tsserver:: projectErrors::", () => {
content: correctConfig.content.substr(1),
};
const host = createServerHost([file1, file2, correctConfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
{
@@ -212,9 +208,9 @@ describe("unittests:: tsserver:: projectErrors:: are reported as appropriate", (
content: "{",
};
const host = createServerHost([file1, corruptedConfig]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path);
baselineTsserverLogs("projectErrors", "document is not contained in project", projectService);
const session = new TestSession(host);
openFilesForSession([file1], session);
baselineTsserverLogs("projectErrors", "document is not contained in project", session);
});
describe("when opening new file that doesnt exist on disk yet", () => {
@@ -229,7 +225,7 @@ describe("unittests:: tsserver:: projectErrors:: are reported as appropriate", (
content: "class c { }",
};
const host = createServerHost([libFile, fileInRoot, fileInProjectRoot]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host), useInferredProjectPerProjectRoot: true });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
const untitledFile = "untitled:Untitled-1";
const refPathNotFound1 = "../../../../../../typings/@epic/Core.d.ts";
@@ -276,7 +272,7 @@ describe("unittests:: tsserver:: projectErrors:: are reported as appropriate", (
content: jsonToReadableText({ compilerOptions: { module: "none", targer: "es5" }, exclude: ["node_modules"] }),
};
const host = createServerHost([app, foo, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
@@ -297,7 +293,7 @@ describe("unittests:: tsserver:: projectErrors:: are reported as appropriate", (
content: "let x: number = false;",
};
const host = createServerHost([file, libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.GeterrRequest>({
command: ts.server.protocol.CommandTypes.Geterr,
arguments: {
@@ -325,7 +321,7 @@ describe("unittests:: tsserver:: projectErrors:: are reported as appropriate", (
};
const files = [libFile, app, serverUtilities, backendTest];
const host = createServerHost(files);
const session = createSession(host, { useInferredProjectPerProjectRoot: true, canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
openFilesForSession([{ file: app, projectRootPath: "/user/username/projects/myproject" }], session);
openFilesForSession([{ file: backendTest, projectRootPath: "/user/username/projects/myproject" }], session);
verifyGetErrRequest({ session, files: [backendTest.path, app.path] });
@@ -363,7 +359,7 @@ declare module '@custom/plugin' {
};
const files = [libFile, aFile, config, plugin, pluginProposed];
const host = createServerHost(files);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aFile], session);
verifyGetErrRequest({ session, files: [aFile] });
@@ -421,7 +417,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
}`,
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("projectErrors", "configFileDiagnostic events are generated when the config file has errors", session);
});
@@ -438,7 +434,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
}`,
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("projectErrors", "configFileDiagnostic events are generated when the config file doesnt have errors", session);
});
@@ -456,7 +452,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
configFile.content = `{
@@ -499,7 +495,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
}`,
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file2], session);
openFilesForSession([file], session);
// We generate only if project is created when opening file from the project
@@ -522,7 +518,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
}`,
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, suppressDiagnosticEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, suppressDiagnosticEvents: true });
openFilesForSession([file], session);
baselineTsserverLogs("projectErrors", "configFileDiagnostic events are not generated when the config file has errors but suppressDiagnosticEvents is true", session);
});
@@ -548,7 +544,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
};
const host = createServerHost([file, file2, file3, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file2], session);
openFilesForSession([file], session);
// We generate only if project is created when opening file from the project
@@ -571,7 +567,7 @@ describe("unittests:: tsserver:: Project Errors for Configure file diagnostics e
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("projectErrors", "configFileDiagnostic events contains the project reference errors", session);
});
@@ -584,7 +580,7 @@ describe("unittests:: tsserver:: projectErrors:: dont include overwrite emit err
content: "function test1() { }",
};
const host = createServerHost([f1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([f1], session);
const projectService = session.getProjectService();
@@ -594,7 +590,7 @@ describe("unittests:: tsserver:: projectErrors:: dont include overwrite emit err
arguments: { projectFileName },
});
setCompilerOptionsForInferredProjectsRequestForSession({ module: ts.ModuleKind.CommonJS }, session);
setCompilerOptionsForInferredProjectsRequestForSession({ module: ts.server.protocol.ModuleKind.CommonJS }, session);
session.executeCommandSeq<ts.server.protocol.CompilerOptionsDiagnosticsRequest>({
command: ts.server.protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
arguments: { projectFileName },
@@ -608,7 +604,7 @@ describe("unittests:: tsserver:: projectErrors:: dont include overwrite emit err
content: "function test1() { }",
};
const host = createServerHost([f1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectFileName = "/a/b/project.csproj";
const externalFiles = toExternalFiles([f1.path]);
openExternalProjectForSession({
@@ -625,7 +621,7 @@ describe("unittests:: tsserver:: projectErrors:: dont include overwrite emit err
openExternalProjectForSession({
projectFileName,
rootFiles: externalFiles,
options: { module: ts.ModuleKind.CommonJS },
options: { module: ts.server.protocol.ModuleKind.CommonJS },
}, session);
session.executeCommandSeq<ts.server.protocol.CompilerOptionsDiagnosticsRequest>({
command: ts.server.protocol.CommandTypes.CompilerOptionsDiagnosticsFull,
@@ -658,7 +654,7 @@ describe("unittests:: tsserver:: projectErrors:: reports Options Diagnostic loca
content: configFileContentWithComment,
};
const host = createServerHost([file, libFile, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -683,7 +679,7 @@ describe("unittests:: tsserver:: projectErrors:: with config file change", () =>
const tsconfig: File = { path: "/tsconfig.json", content: options(/*allowUnusedLabels*/ true) };
const host = createServerHost([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
host.modifyFile(tsconfig.path, options(/*allowUnusedLabels*/ false));
@@ -722,7 +718,7 @@ console.log(blabla);`,
};
const host = createServerHost([test, blabla, libFile, tsconfig]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([test], session);
return { host, session, test, blabla, tsconfig };
}
@@ -761,7 +757,7 @@ describe("unittests:: tsserver:: projectErrors:: with npm install when", () => {
};
const projectFiles = [main, libFile, config];
const host = createServerHost(projectFiles);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file: main, projectRootPath: "/user/username/projects/myproject" }], session);
verifyGetErrRequest({ session, files: [main] });
@@ -805,9 +801,6 @@ describe("unittests:: tsserver:: projectErrors:: with npm install when", () => {
host.runQueuedTimeoutCallbacks(); // Invalidation of failed lookups
host.runQueuedTimeoutCallbacks(); // Actual update
}
else {
session.testhost.logTimeoutQueueLength();
}
verifyGetErrRequest({ session, files: [main], existingTimeouts: !npmInstallComplete && !timeoutDuringPartialInstallation });
}
}
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -10,9 +7,9 @@ import {
} from "../helpers/solutionBuilder";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
protocolToLocation,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -61,7 +58,7 @@ fn2();
describe("Of usageTs", () => {
it("with initial file open, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -85,7 +82,7 @@ fn2();
});
it("with initial file open, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -109,7 +106,7 @@ fn2();
});
it("with local change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -139,7 +136,7 @@ fn2();
});
it("with local change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -169,7 +166,7 @@ fn2();
});
it("with local change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -210,7 +207,7 @@ fn2();
});
it("with local change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -251,7 +248,7 @@ fn2();
});
it("with change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -281,7 +278,7 @@ fn2();
});
it("with change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -311,7 +308,7 @@ fn2();
});
it("with change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -352,7 +349,7 @@ fn2();
});
it("with change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -396,7 +393,7 @@ fn2();
describe("Of dependencyTs in usage project", () => {
it("with initial file open, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -420,7 +417,7 @@ fn2();
});
it("with initial file open, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -444,7 +441,7 @@ fn2();
});
it("with local change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -474,7 +471,7 @@ fn2();
});
it("with local change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -504,7 +501,7 @@ fn2();
});
it("with local change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -545,7 +542,7 @@ fn2();
});
it("with local change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -586,7 +583,7 @@ fn2();
});
it("with change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -616,7 +613,7 @@ fn2();
});
it("with change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -646,7 +643,7 @@ fn2();
});
it("with change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -687,7 +684,7 @@ fn2();
});
it("with change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -733,7 +730,7 @@ fn2();
describe("Of usageTs", () => {
it("with initial file open, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -757,7 +754,7 @@ fn2();
});
it("with initial file open, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -781,7 +778,7 @@ fn2();
});
it("with local change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -822,7 +819,7 @@ fn2();
});
it("with local change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -863,7 +860,7 @@ fn2();
});
it("with local change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -904,7 +901,7 @@ fn2();
});
it("with local change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -945,7 +942,7 @@ fn2();
});
it("with change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -986,7 +983,7 @@ fn2();
});
it("with change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1027,7 +1024,7 @@ fn2();
});
it("with change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1068,7 +1065,7 @@ fn2();
});
it("with change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1111,7 +1108,7 @@ fn2();
describe("Of dependencyTs in usage project", () => {
it("with initial file open, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -1135,7 +1132,7 @@ fn2();
});
it("with local change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1176,7 +1173,7 @@ fn2();
});
it("with local change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1217,7 +1214,7 @@ fn2();
});
it("with change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1258,7 +1255,7 @@ fn2();
});
it("with change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1302,7 +1299,7 @@ fn2();
describe("Of dependencyTs", () => {
it("with initial file open, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -1326,7 +1323,7 @@ fn2();
});
it("with initial file open, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
// Verify CompileOnSaveAffectedFileList
@@ -1350,7 +1347,7 @@ fn2();
});
it("with local change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1391,7 +1388,7 @@ fn2();
});
it("with local change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1432,7 +1429,7 @@ fn2();
});
it("with local change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1473,7 +1470,7 @@ fn2();
});
it("with local change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1514,7 +1511,7 @@ fn2();
});
it("with change to dependency, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1555,7 +1552,7 @@ fn2();
});
it("with change to dependency, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1596,7 +1593,7 @@ fn2();
});
it("with change to usage, without specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1637,7 +1634,7 @@ fn2();
});
it("with change to usage, with specifying project file", () => {
const host = createServerHost([dependencyTs, dependencyConfig, usageTs, usageConfig, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([usageTs, dependencyTs], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveAffectedFileListRequest>({
@@ -1741,7 +1738,7 @@ describe("unittests:: tsserver:: with project references and compile on save wit
// ts build should succeed
ensureErrorFreeBuild(host, [siblingConfig.path]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([siblingSource], session);
session.executeCommandSeq<ts.server.protocol.CompileOnSaveEmitFileRequest>({
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -14,11 +11,10 @@ import {
import {
baselineTsserverLogs,
createHostWithSolutionBuild,
createProjectService,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
protocolLocationFromSubstring,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -119,7 +115,7 @@ describe("unittests:: tsserver:: with project references and tsbuild", () => {
const files = [libFile, containerLibConfig, containerLibIndex, containerExecConfig, containerExecIndex, containerCompositeExecConfig, containerCompositeExecIndex, containerConfig];
if (tempFile) files.push(tempFile);
const host = createHostWithSolutionBuild(files, [containerConfig.path]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return { files, session, containerConfig, containerCompositeExecIndex };
}
@@ -260,7 +256,7 @@ function foo() {
[commonConfig, keyboardTs, keyboardTestTs, srcConfig, terminalTs, libFile],
[srcConfig.path],
);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([keyboardTs, terminalTs], session);
const searchStr = "evaluateKeyboardEvent";
@@ -331,23 +327,23 @@ function foo() {
};
const files = [libFile, aTs, a2Ts, configA, bDts, bTs, configB, cTs, configC];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(aTs.path);
const session = new TestSession(host);
openFilesForSession([aTs], session);
// project A referencing b.d.ts without project reference
const projectA = service.configuredProjects.get(configA.path)!;
const projectA = session.getProjectService().configuredProjects.get(configA.path)!;
assert.isDefined(projectA);
// reuses b.d.ts but sets the path and resolved path since projectC has project references
// as the real resolution was to b.ts
service.openClientFile(cTs.path);
openFilesForSession([cTs], session);
// Now new project for project A tries to reuse b but there is no filesByName mapping for b's source location
host.writeFile(a2Ts.path, `${a2Ts.content}export const y = 30;`);
service.testhost.baselineHost("a2Ts modified");
session.host.baselineHost("a2Ts modified");
assert.isTrue(projectA.dirty);
projectA.updateGraph();
baselineTsserverLogs("projectReferences", "reusing d.ts files from composite and non composite projects", service);
baselineTsserverLogs("projectReferences", "reusing d.ts files from composite and non composite projects", session);
});
describe("when references are monorepo like with symlinks", () => {
@@ -389,7 +385,7 @@ function foo() {
createServerHost(files);
// Create symlink in node module
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTest], session);
verifyGetErrRequest({ session, files: [aTest] });
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({
@@ -548,7 +544,7 @@ testCompositeFunction('why hello there', 42);`,
symLink: `/user/username/projects/myproject/packages/emit-composite`,
};
const host = createServerHost([libFile, compositeConfig, compositePackageJson, compositeIndex, compositeTestModule, consumerConfig, consumerIndex, symlink], { useCaseSensitiveFileNames: true });
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([consumerIndex], session);
verifyGetErrRequest({ session, files: [consumerIndex] });
baselineTsserverLogs("projectReferences", `when the referenced projects have allowJs and emitDeclarationOnly`, session);
@@ -618,7 +614,7 @@ testCompositeFunction('why hello there', 42);`,
const files = [libFile, solution, compilerConfig, typesFile, programFile, servicesConfig, servicesFile, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([programFile], session);
// Find all references for getSourceFile
@@ -738,7 +734,7 @@ testCompositeFunction('why hello there', 42);`,
const files = [libFile, solutionConfig, aConfig, aFile, bConfig, bFile, cConfig, cFile, dConfig, dFile, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([bFile], session);
// The first search will trigger project loads
@@ -812,7 +808,7 @@ ${usage}`,
content: definition,
};
const host = createServerHost([libFile, solution, libFile, apiConfig, apiFile, appConfig, appFile, sharedConfig, sharedFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([apiFile], session);
// Find all references
@@ -929,7 +925,7 @@ export const foo = local;`,
const files = [libFile, solution, compilerConfig, typesFile, programFile, servicesConfig, servicesFile, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([programFile], session);
// Find all references
@@ -1047,7 +1043,7 @@ export function bar() {}`,
fileResolvingToMainDts,
...additionalFiles,
]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const service = session.getProjectService();
service.openClientFile(main.path);
return { session, service, host };
@@ -1324,7 +1320,7 @@ bar;`,
content: `class class2 {}`,
};
const host = createServerHost([config1, class1, class1Dts, config2, class2, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([class2], session);
return { host, session, class1 };
}
@@ -1479,7 +1475,7 @@ bar;`,
solutionBuildWithBaseline(host, [solnConfig.path]);
host.clearOutput();
}
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([appIndex], session);
session.executeCommandSeq<ts.server.protocol.CodeFixRequest>({
command: ts.server.protocol.CommandTypes.GetCodeFixes,
@@ -1561,7 +1557,7 @@ bar;`,
noCoreRef2File,
noCoreRef2Config,
], { useCaseSensitiveFileNames: true });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([mainFile, coreFile], session);
// Find all refs in coreFile
@@ -1652,7 +1648,7 @@ const b: B = new B();`,
};
const host = createServerHost([configA, indexA, configB, indexB, helperB, dtsB, ...(dtsMapPresent ? [dtsMapB] : [])]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([indexA, ...(projectAlreadyLoaded ? [helperB] : [])], session);
session.executeCommandSeq<ts.server.protocol.ReferencesRequest>({
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -9,7 +6,6 @@ import {
baselineTsserverLogs,
closeFilesForSession,
createHostWithSolutionBuild,
createSession,
openFilesForSession,
TestSession,
TestSessionRequest,
@@ -248,14 +244,14 @@ fn5();
}),
);
onHostCreate?.(host);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return { host, session };
}
function createSessionWithProjectReferences(onHostCreate?: OnHostCreate) {
const host = createHostWithSolutionBuild(files, [mainConfig.path]);
onHostCreate?.(host);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return { host, session };
}
@@ -274,7 +270,7 @@ fn5();
}),
);
onHostCreate?.(host);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
return { host, session };
}
@@ -855,7 +851,7 @@ ${dependencyTs.content}`,
it("when projects are not built", () => {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([mainTs, randomFile], session);
verifyAllFnAction(
session,
@@ -1671,7 +1667,7 @@ ${dependencyTs.content}`,
it("when projects are not built", () => {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([dependencyTs, randomFile], session);
verifyAllFnAction(
session,
@@ -2723,7 +2719,7 @@ ${dependencyTs.content}`,
it("when projects are not built", () => {
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([mainTs, dependencyTs, randomFile], session);
verifyAllFnAction(
session,
+306 -254
View File
@@ -12,13 +12,13 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createProjectService,
createSession,
logConfiguredProjectsHasOpenRefStatus,
logInferredProjectsOrphanStatus,
openExternalProjectForSession,
openFilesForSession,
protocolFileLocationFromSubstring,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
TestSessionRequest,
toExternalFile,
toExternalFiles,
@@ -42,7 +42,7 @@ describe("unittests:: tsserver:: projects::", () => {
let x = y`,
};
const host = createServerHost([file1, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
// Two errors: CommonFile2 not found and cannot find name y
@@ -70,7 +70,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [commonFile1, commonFile2, configFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([commonFile1], session);
configFile.content = `{
@@ -105,13 +105,25 @@ describe("unittests:: tsserver:: projects::", () => {
const proj1name = "proj1", proj2name = "proj2", proj3name = "proj3";
const host = createServerHost([file1, file2, file3]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({ rootFiles: toExternalFiles([file1.path]), options: {}, projectFileName: proj1name }, session);
openExternalProjectForSession({
rootFiles: toExternalFiles([file1.path]),
options: {},
projectFileName: proj1name,
}, session);
openExternalProjectForSession({ rootFiles: toExternalFiles([file2.path]), options: {}, projectFileName: proj2name }, session);
openExternalProjectForSession({
rootFiles: toExternalFiles([file2.path]),
options: {},
projectFileName: proj2name,
}, session);
openExternalProjectForSession({ rootFiles: toExternalFiles([file3.path]), options: {}, projectFileName: proj3name }, session);
openExternalProjectForSession({
rootFiles: toExternalFiles([file3.path]),
options: {},
projectFileName: proj3name,
}, session);
baselineTsserverLogs("projects", "should disable features when the files are too large", session);
});
@@ -127,12 +139,16 @@ describe("unittests:: tsserver:: projects::", () => {
fileSize: 100,
};
const projName = "proj1";
const projectFileName = "proj1";
const host = createServerHost([file1, file2]);
const session = createSession(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useSingleInferredProject: true });
openExternalProjectForSession({ rootFiles: toExternalFiles([file1.path, file2.path]), options: {}, projectFileName: projName }, session);
openExternalProjectForSession({
rootFiles: toExternalFiles([file1.path, file2.path]),
options: {},
projectFileName,
}, session);
openFilesForSession([file2], session);
baselineTsserverLogs("projects", "should not crash when opening a file in a project with a disabled language service", session);
@@ -154,16 +170,24 @@ describe("unittests:: tsserver:: projects::", () => {
),
};
const externalProjectName = "externalproject";
const projectFileName = "externalproject";
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic, logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path, config1.path]),
options: {},
projectFileName: externalProjectName,
});
baselineTsserverLogs("projects", "external project including config file", projectService);
const session = new TestSession({ host, useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic });
const request: ts.server.protocol.OpenExternalProjectRequest = {
command: ts.server.protocol.CommandTypes.OpenExternalProject,
arguments: {
rootFiles: toExternalFiles([file1.path, config1.path]),
options: {},
projectFileName,
},
seq: session.getNextSeq(),
type: "request",
};
session.host.baselineHost("Before request");
session.logger.info(`request:${ts.server.stringifyIndented(request)}`);
session.getProjectService().openExternalProject(request.arguments);
session.host.baselineHost("After request");
baselineTsserverLogs("projects", "external project including config file", session);
});
it("loose file included in config file (openClientFile)", () => {
@@ -182,9 +206,9 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic, logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(file1.path, file1.content);
baselineTsserverLogs("projects", "loose file included in config file (openClientFile)", projectService);
const session = new TestSession({ host, useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic });
openFilesForSession([{ file: file1, content: file1.content }], session);
baselineTsserverLogs("projects", "loose file included in config file (openClientFile)", session);
});
it("loose file included in config file (applyCodeChanges)", () => {
@@ -203,10 +227,15 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([file1, config1]);
const projectService = createProjectService(host, { useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic, logger: createLoggerWithInMemoryLogs(host) });
projectService.applyChangesInOpenFiles(ts.singleIterator({ fileName: file1.path, content: file1.content }));
const session = new TestSession({ host, useSingleInferredProject: true, serverMode: ts.LanguageServiceMode.Syntactic });
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
openFiles: [{ fileName: file1.path, content: file1.content }],
},
});
baselineTsserverLogs("projects", "loose file included in config file (applyCodeChanges)", projectService);
baselineTsserverLogs("projects", "loose file included in config file (applyCodeChanges)", session);
});
});
@@ -221,8 +250,12 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([f1, f2, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openExternalProjectForSession({ projectFileName: "/a/b/project", rootFiles: toExternalFiles([f1.path, f2.path]), options: {} }, session);
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "/a/b/project",
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {},
}, session);
openFilesForSession([f1, { file: f2.path, content: "let x: string" }], session);
// should contain completions for string
@@ -251,8 +284,12 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([f1, f2, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openExternalProjectForSession({ projectFileName: "/a/b/project", rootFiles: [{ fileName: f1.path }, { fileName: f2.path, hasMixedContent: true }], options: {} }, session);
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "/a/b/project",
rootFiles: [{ fileName: f1.path }, { fileName: f2.path, hasMixedContent: true }],
options: {},
}, session);
openFilesForSession([f1, { file: f2.path, content: "let somelongname: string" }], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
@@ -282,16 +319,14 @@ describe("unittests:: tsserver:: projects::", () => {
content: `export let y = 1;`,
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
projectService.openClientFile(file3.path);
openFilesForSession([file1, file3], session);
host.writeFile(file2.path, `export * from "../c/f3"`); // now inferred project should inclule file3
host.runQueuedTimeoutCallbacks();
logInferredProjectsOrphanStatus(projectService);
baselineTsserverLogs("projects", "changes in closed files are reflected in project structure", projectService);
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("projects", "changes in closed files are reflected in project structure", session);
});
it("deleted files affect project structure", () => {
@@ -308,14 +343,13 @@ describe("unittests:: tsserver:: projects::", () => {
content: `export let y = 1;`,
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
projectService.openClientFile(file3.path);
openFilesForSession([file1, file3], session);
host.deleteFile(file2.path);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projects", "deleted files affect project structure", projectService);
baselineTsserverLogs("projects", "deleted files affect project structure", session);
});
it("ignores files excluded by a custom safe type list", () => {
@@ -328,15 +362,19 @@ describe("unittests:: tsserver:: projects::", () => {
content: "whoa do @@ not parse me ok thanks!!!",
};
const host = createServerHost([file1, office, customTypesMap]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
try {
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, office.path]) });
projectService.logger.log(`TypeAcquisition:: ${jsonToReadableText(projectService.externalProjects[0].getTypeAcquisition())}`);
openExternalProjectForSession({
projectFileName: "project",
options: {},
rootFiles: toExternalFiles([file1.path, office.path]),
}, session);
session.logger.log(`TypeAcquisition:: ${jsonToReadableText(session.getProjectService().externalProjects[0].getTypeAcquisition())}`);
}
finally {
projectService.resetSafeList();
session.getProjectService().resetSafeList();
}
baselineTsserverLogs("projects", "ignores files excluded by a custom safe type list", projectService);
baselineTsserverLogs("projects", "ignores files excluded by a custom safe type list", session);
});
it("file with name constructor.js doesnt cause issue with typeAcquisition when safe type list", () => {
@@ -353,50 +391,15 @@ describe("unittests:: tsserver:: projects::", () => {
content: "export function is() { return true; }",
};
const host = createServerHost([file1, libFile, constructorFile, bliss, customTypesMap]);
let request: string | undefined;
const cachePath = "/a/data";
const typingsInstaller: ts.server.ITypingsInstaller = {
isKnownTypesPackageName: ts.returnFalse,
installPackage: ts.notImplemented,
enqueueInstallTypingsRequest: (proj, typeAcquisition, unresolvedImports) => {
assert.isUndefined(request);
request = jsonToReadableText(ts.server.createInstallTypingsRequest(proj, typeAcquisition, unresolvedImports || ts.server.emptyArray, cachePath));
},
attach: ts.noop,
onProjectClosed: ts.noop,
globalTypingsCacheLocation: cachePath,
};
const projectFileName = "project";
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName,
options: {},
rootFiles: toExternalFiles([file1.path, constructorFile.path, bliss.path]),
}, session);
const projectName = "project";
const projectService = createProjectService(host, { typingsInstaller, logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({ projectFileName: projectName, options: {}, rootFiles: toExternalFiles([file1.path, constructorFile.path, bliss.path]) });
assert.equal(
request,
jsonToReadableText({
projectName,
fileNames: [libFile.path, file1.path, constructorFile.path, bliss.path],
compilerOptions: { allowNonTsExtensions: true, noEmitForJsFiles: true },
typeAcquisition: { include: ["blissfuljs"], exclude: [], enable: true },
unresolvedImports: ["s"],
projectRootPath: "/",
cachePath,
kind: "discover",
}),
);
const response = JSON.parse(request!);
request = undefined;
projectService.updateTypingsForProject({
kind: "action::set",
projectName: response.projectName,
typeAcquisition: response.typeAcquisition,
compilerOptions: response.compilerOptions,
typings: ts.emptyArray,
unresolvedImports: response.unresolvedImports,
});
projectService.testhost.logTimeoutQueueLength();
assert.isUndefined(request);
baselineTsserverLogs("projects", "file with name constructor.js doesnt cause issue with typeAcquisition when safe type list", projectService);
baselineTsserverLogs("projects", "file with name constructor.js doesnt cause issue with typeAcquisition when safe type list", session);
});
it("ignores files excluded by the default type list", () => {
@@ -430,15 +433,19 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [file1, minFile, kendoFile1, kendoFile2, kendoFile3, officeFile1, officeFile2];
const host = createServerHost(files);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
try {
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles(files.map(f => f.path)) });
projectService.logger.log(`TypeAcquisition:: ${jsonToReadableText(projectService.externalProjects[0].getTypeAcquisition())}`);
openExternalProjectForSession({
projectFileName: "project",
options: {},
rootFiles: toExternalFiles(files.map(f => f.path)),
}, session);
session.logger.log(`TypeAcquisition:: ${jsonToReadableText(session.getProjectService().externalProjects[0].getTypeAcquisition())}`);
}
finally {
projectService.resetSafeList();
session.getProjectService().resetSafeList();
}
baselineTsserverLogs("projects", "ignores files excluded by the default type list", projectService);
baselineTsserverLogs("projects", "ignores files excluded by the default type list", session);
});
it("removes version numbers correctly", () => {
@@ -471,14 +478,19 @@ describe("unittests:: tsserver:: projects::", () => {
content: "let y = 5",
};
const host = createServerHost([file1, file2, file3, customTypesMap]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
try {
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]), typeAcquisition: { enable: true } });
openExternalProjectForSession({
projectFileName: "project",
options: {},
rootFiles: toExternalFiles([file1.path, file2.path]),
typeAcquisition: { enable: true },
}, session);
}
finally {
projectService.resetSafeList();
session.getProjectService().resetSafeList();
}
baselineTsserverLogs("projects", "ignores files excluded by a legacy safe type list", projectService);
baselineTsserverLogs("projects", "ignores files excluded by a legacy safe type list", session);
});
it("correctly migrate files between projects", () => {
@@ -497,49 +509,48 @@ describe("unittests:: tsserver:: projects::", () => {
content: "export let y = 1;",
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file2.path);
logInferredProjectsOrphanStatus(projectService);
openFilesForSession([file2], session);
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file3.path);
logInferredProjectsOrphanStatus(projectService);
openFilesForSession([file3], session);
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file1.path);
logInferredProjectsOrphanStatus(projectService);
openFilesForSession([file1], session);
logInferredProjectsOrphanStatus(session);
projectService.closeClientFile(file1.path);
logInferredProjectsOrphanStatus(projectService);
closeFilesForSession([file1], session);
logInferredProjectsOrphanStatus(session);
projectService.closeClientFile(file3.path);
logInferredProjectsOrphanStatus(projectService);
closeFilesForSession([file3], session);
logInferredProjectsOrphanStatus(session);
projectService.openClientFile(file3.path);
logInferredProjectsOrphanStatus(projectService);
baselineTsserverLogs("projects", "correctly migrate files between projects", projectService);
openFilesForSession([file3], session);
logInferredProjectsOrphanStatus(session);
baselineTsserverLogs("projects", "correctly migrate files between projects", session);
});
it("regression test for crash in acquireOrUpdateDocument", () => {
const tsFile = {
fileName: "/a/b/file1.ts",
path: "/a/b/file1.ts",
content: "",
};
const jsFile = {
path: "/a/b/file1.js",
content: "var x = 10;",
fileName: "/a/b/file1.js",
scriptKind: "JS" as const,
};
const host = createServerHost([]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.applyChangesInOpenFiles(ts.singleIterator(tsFile));
const projs = projectService.synchronizeProjectList([]);
projectService.findProject(projs[0].info!.projectName)!.getLanguageService().getNavigationBarItems(tsFile.fileName);
projectService.synchronizeProjectList([projs[0].info!]);
projectService.applyChangesInOpenFiles(ts.singleIterator(jsFile));
baselineTsserverLogs("projects", "regression test for crash in acquireOrUpdateDocument", projectService);
const session = new TestSession(host);
openFilesForSession(["/a/b/file1.ts"], session);
const projs = session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: { knownProjects: [] },
}).response as ts.server.protocol.ProjectFilesWithDiagnostics[];
session.executeCommandSeq<ts.server.protocol.NavBarRequest>({
command: ts.server.protocol.CommandTypes.NavBar,
arguments: { file: "/a/b/file1.ts" },
});
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: {
knownProjects: projs.map(p => p.info!),
},
});
openFilesForSession([{ file: "/a/b/file1.js", content: "var x = 10;" }], session);
baselineTsserverLogs("projects", "regression test for crash in acquireOrUpdateDocument", session);
});
it("config file is deleted", () => {
@@ -556,15 +567,13 @@ describe("unittests:: tsserver:: projects::", () => {
content: jsonToReadableText({ compilerOptions: {} }),
};
const host = createServerHost([file1, file2, config]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
openFilesForSession([file1, file2], session);
host.deleteFile(config.path);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projects", "config file is deleted", projectService);
baselineTsserverLogs("projects", "config file is deleted", session);
});
it("loading files with correct priority", () => {
@@ -587,23 +596,26 @@ describe("unittests:: tsserver:: projects::", () => {
}),
};
const host = createServerHost([f1, f2, f3, config]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.setHostConfiguration({
extraFileExtensions: [
{ extension: ".js", isMixedContent: false },
{ extension: ".html", isMixedContent: true },
],
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
extraFileExtensions: [
{ extension: ".js", isMixedContent: false },
{ extension: ".html", isMixedContent: true },
],
},
});
projectService.openClientFile(f1.path);
openFilesForSession([f1], session);
// Since f2 refers to config file as the default project, it needs to be kept alive
projectService.closeClientFile(f1.path);
projectService.openClientFile(f2.path);
closeFilesForSession([f1], session);
openFilesForSession([f2], session);
// Should close configured project with next file open
projectService.closeClientFile(f2.path);
projectService.openClientFile(f3.path);
baselineTsserverLogs("projects", "loading files with correct priority", projectService);
closeFilesForSession([f2], session);
openFilesForSession([f3], session);
baselineTsserverLogs("projects", "loading files with correct priority", session);
});
it("tsconfig script block support", () => {
@@ -620,10 +632,9 @@ describe("unittests:: tsserver:: projects::", () => {
content: jsonToReadableText({ compilerOptions: { allowJs: true } }),
};
const host = createServerHost([file1, file2, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// HTML file will not be included in any projects yet
openFilesForSession([file1], session);
const projectService = session.getProjectService();
// Specify .html extension as mixed content
const extraFileExtensions = [{ extension: ".html", scriptKind: ts.ScriptKind.JS, isMixedContent: true }];
@@ -634,12 +645,17 @@ describe("unittests:: tsserver:: projects::", () => {
});
// Open HTML file
projectService.applyChangesInOpenFiles(ts.singleIterator({
fileName: file2.path,
hasMixedContent: true,
scriptKind: ts.ScriptKind.JS,
content: `var hello = "hello";`,
}));
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
openFiles: [{
fileName: file2.path,
hasMixedContent: true,
scriptKind: "JS",
content: `var hello = "hello";`,
}],
},
});
// Now HTML file is included in the project
// Check identifiers defined in HTML content are available in .ts file
@@ -654,11 +670,12 @@ describe("unittests:: tsserver:: projects::", () => {
});
// Close HTML file
projectService.applyChangesInOpenFiles(
/*openFiles*/ undefined,
/*changedFiles*/ undefined,
/*closedFiles*/ [file2.path],
);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
closedFiles: [file2.path],
},
});
// HTML file is still included in project
@@ -731,7 +748,7 @@ describe("unittests:: tsserver:: projects::", () => {
function verfiy(config: File, host: TestServerHost) {
logger.host = host;
logger.log(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
const session = createSession(host, { logger });
const session = new TestSession({ host, logger });
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { extraFileExtensions },
@@ -758,19 +775,21 @@ describe("unittests:: tsserver:: projects::", () => {
content: "export let x = 1",
};
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
changedFiles: [{
fileName: file1.path,
changes: [{ span: ts.createTextSpan(0, file1.path.length), newText: "let y = 1" }],
}],
},
});
projectService.applyChangesInOpenFiles(
/*openFiles*/ undefined,
/*changedFiles*/ ts.singleIterator({ fileName: file1.path, changes: ts.singleIterator({ span: ts.createTextSpan(0, file1.path.length), newText: "let y = 1" }) }),
/*closedFiles*/ undefined,
);
projectService.ensureInferredProjectsUpToDate_TestOnly();
baselineTsserverLogs("projects", "project structure update is deferred if files are not added or removed", projectService);
session.getProjectService().ensureInferredProjectsUpToDate_TestOnly();
baselineTsserverLogs("projects", "project structure update is deferred if files are not added or removed", session);
});
it("files with mixed content are handled correctly", () => {
@@ -779,29 +798,37 @@ describe("unittests:: tsserver:: projects::", () => {
content: `<html><script language="javascript">var x = 1;</></html>`,
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectFileName = "projectFileName";
projectService.openExternalProject({ projectFileName, options: {}, rootFiles: [{ fileName: file1.path, scriptKind: ts.ScriptKind.JS, hasMixedContent: true }] });
openExternalProjectForSession({
projectFileName,
options: {},
rootFiles: [{
fileName: file1.path,
scriptKind: "JS",
hasMixedContent: true,
}],
}, session);
const project = projectService.externalProjects[0];
const project = session.getProjectService().externalProjects[0];
const scriptInfo = project.getScriptInfo(file1.path)!;
const snap = scriptInfo.getSnapshot();
const actualText = ts.getSnapshotText(snap);
projectService.logger.info(`Text of${file1.path}: ${actualText}`);
session.logger.info(`Text of${file1.path}: ${actualText}`);
projectService.openClientFile(file1.path, `var x = 1;`);
project.updateGraph();
const quickInfo = project.getLanguageService().getQuickInfoAtPosition(file1.path, 4)!;
projectService.logger.info(`QuickInfo : ${quickInfo.kind}`);
projectService.closeClientFile(file1.path);
file1.content = `var x = 1;`;
openFilesForSession([{ file: file1.path, content: file1.content }], session);
session.executeCommandSeq<ts.server.protocol.QuickInfoRequest>({
command: ts.server.protocol.CommandTypes.Quickinfo,
arguments: protocolFileLocationFromSubstring(file1, "x"),
});
closeFilesForSession([file1], session);
const scriptInfo2 = project.getScriptInfo(file1.path)!;
const actualText2 = ts.getSnapshotText(scriptInfo2.getSnapshot());
projectService.logger.info(`Text of${file1.path}: ${actualText2}`);
baselineTsserverLogs("projects", "files with mixed content are handled correctly", projectService);
session.logger.info(`Text of${file1.path}: ${actualText2}`);
baselineTsserverLogs("projects", "files with mixed content are handled correctly", session);
});
it("syntax tree cache handles changes in project settings", () => {
@@ -810,15 +837,38 @@ describe("unittests:: tsserver:: projects::", () => {
content: "{x: 1}",
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
projectService.setCompilerOptionsForInferredProjects({ target: ts.ScriptTarget.ES5, allowJs: false });
projectService.openClientFile(file1.path);
projectService.inferredProjects[0].getLanguageService(/*ensureSynchronized*/ false).getOutliningSpans(file1.path);
projectService.setCompilerOptionsForInferredProjects({ target: ts.ScriptTarget.ES5, allowJs: true });
projectService.getScriptInfo(file1.path)!.editContent(0, 0, " ");
projectService.inferredProjects[0].getLanguageService(/*ensureSynchronized*/ false).getOutliningSpans(file1.path);
projectService.closeClientFile(file1.path);
baselineTsserverLogs("projects", "syntax tree cache handles changes in project settings", projectService);
const session = new TestSession({ host, useSingleInferredProject: true });
setCompilerOptionsForInferredProjectsRequestForSession({
target: ts.server.protocol.ScriptTarget.ES5,
allowJs: false,
}, session);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.OutliningSpansRequest>({
command: ts.server.protocol.CommandTypes.GetOutliningSpans,
arguments: { file: file1.path },
});
setCompilerOptionsForInferredProjectsRequestForSession({
target: ts.server.protocol.ScriptTarget.ES5,
allowJs: true,
}, session);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
changedFiles: [{
fileName: file1.path,
changes: [{
span: { start: 0, length: 0 },
newText: " ",
}],
}],
},
});
session.executeCommandSeq<ts.server.protocol.OutliningSpansRequest>({
command: ts.server.protocol.CommandTypes.GetOutliningSpans,
arguments: { file: file1.path },
});
closeFilesForSession([file1], session);
baselineTsserverLogs("projects", "syntax tree cache handles changes in project settings", session);
});
it("File in multiple projects at opened and closed correctly", () => {
@@ -839,23 +889,23 @@ describe("unittests:: tsserver:: projects::", () => {
content: "{}",
};
const host = createServerHost([file1, file2, tsconfig1, tsconfig2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(file2.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
openFilesForSession([file2], session);
logConfiguredProjectsHasOpenRefStatus(session);
projectService.openClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
openFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session);
projectService.closeClientFile(file2.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
closeFilesForSession([file2], session);
logConfiguredProjectsHasOpenRefStatus(session);
projectService.closeClientFile(file1.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
closeFilesForSession([file1], session);
logConfiguredProjectsHasOpenRefStatus(session);
projectService.openClientFile(file2.path);
logConfiguredProjectsHasOpenRefStatus(projectService);
baselineTsserverLogs("projects", "File in multiple projects at opened and closed correctly", projectService);
openFilesForSession([file2], session);
logConfiguredProjectsHasOpenRefStatus(session);
baselineTsserverLogs("projects", "File in multiple projects at opened and closed correctly", session);
});
it("snapshot from different caches are incompatible", () => {
@@ -865,7 +915,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([f1]);
const projectFileName = "/a/b/proj.csproj";
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName,
rootFiles: [toExternalFile(f1.path)],
@@ -895,7 +945,7 @@ describe("unittests:: tsserver:: projects::", () => {
content: jsonToReadableText({ compilerOptions: {} }),
};
const host = createServerHost([f1, libFile, config]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.Open,
arguments: {
@@ -934,10 +984,10 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([file1, configFile], { windowsStyleRoot: "c:/" });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
projectService.openClientFile(file1.path);
baselineTsserverLogs("projects", "Properly handle Windows-style outDir", projectService);
baselineTsserverLogs("projects", "Properly handle Windows-style outDir", session);
});
it("files opened and closed affecting multiple projects", () => {
@@ -960,7 +1010,7 @@ describe("unittests:: tsserver:: projects::", () => {
const files = [config, file, filesFile1, filesFile2, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// Create configured project
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
@@ -1022,7 +1072,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [file1, file2, libFile, config];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: { file: file2.path, fileContent: file2.content },
@@ -1064,7 +1114,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const host = createServerHost([file1, file2, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectService = session.getProjectService();
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
@@ -1079,9 +1129,9 @@ describe("unittests:: tsserver:: projects::", () => {
});
// Open external project
const projectName = "/proj1";
const projectFileName = "/proj1";
openExternalProjectForSession({
projectFileName: projectName,
projectFileName,
rootFiles: toExternalFiles([file1.path, file2.path, tsconfig.path]),
options: {},
}, session);
@@ -1124,8 +1174,8 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [file1, file2, libFile, config];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
const configContent2 = jsonToReadableText({
files: ["src/file1.ts"],
@@ -1141,11 +1191,11 @@ describe("unittests:: tsserver:: projects::", () => {
host.runQueuedTimeoutCallbacks();
verifyFile2InfoIsOrphan();
baselineTsserverLogs("projects", "Orphan source files are handled correctly on watch trigger", service);
baselineTsserverLogs("projects", "Orphan source files are handled correctly on watch trigger", session);
function verifyFile2InfoIsOrphan() {
const info = ts.Debug.checkDefined(service.getScriptInfoForPath(file2.path as ts.Path));
service.logger.log(`Containing projects for ${file2.path}:: ${info.containingProjects.map(p => p.projectName).join(",")}`);
const info = ts.Debug.checkDefined(session.getProjectService().getScriptInfoForPath(file2.path as ts.Path));
session.logger.log(`Containing projects for ${file2.path}:: ${info.containingProjects.map(p => p.projectName).join(",")}`);
}
});
@@ -1161,12 +1211,12 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [file1, config];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.modifyFile(file1.path, file1.content, { invokeFileDeleteCreateAsPartInsteadOfChange: true });
service.testhost.logTimeoutQueueLength();
baselineTsserverLogs("projects", "no project structure update on directory watch invoke on open file save", service);
session.host.baselineHost("After modifying file");
baselineTsserverLogs("projects", "no project structure update on directory watch invoke on open file save", session);
});
it("synchronizeProjectList provides redirect info when requested", () => {
@@ -1202,7 +1252,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [fileA, fileB, configA, configB, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([fileA, fileB], session);
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
@@ -1248,13 +1298,19 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [fileA, fileB, fileB2, configA, configB, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([fileA, fileB], session);
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: { knownProjects: [], includeProjectReferenceRedirectInfo: true },
});
const knownProjects = session.getProjectService().synchronizeProjectList([], /*includeProjectReferenceRedirectInfo*/ true);
const knownProjects = session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
arguments: {
knownProjects: [],
includeProjectReferenceRedirectInfo: true,
},
}).response as ts.server.protocol.ProjectFilesWithDiagnostics[];
host.modifyFile(
configA.path,
@@ -1287,7 +1343,7 @@ describe("unittests:: tsserver:: projects::", () => {
content: jsonToReadableText({ extends: "./tsconfig_base.json" }),
};
const host = createServerHost([file, config, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
@@ -1306,7 +1362,7 @@ describe("unittests:: tsserver:: projects::", () => {
content: jsonToReadableText({ extends: "./tsconfig_base.json" }),
};
const host = createServerHost([file, config, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
session.executeCommandSeq<ts.server.protocol.SynchronizeProjectListRequest>({
command: ts.server.protocol.CommandTypes.SynchronizeProjectList,
@@ -1335,7 +1391,7 @@ describe("unittests:: tsserver:: projects::", () => {
};
const files = [fileSubA, fileB, config, libFile];
const host = createServerHost(files);
const session = createSession(host, { canUseEvents: true, noGetErrOnBackgroundUpdate: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFile(fileB);
openFile(fileSubA);
@@ -1345,11 +1401,9 @@ describe("unittests:: tsserver:: projects::", () => {
host.deleteFile(fileSubA.path);
host.deleteFolder(ts.getDirectoryPath(fileSubA.path));
host.writeFile(fileA.path, fileA.content);
session.testhost.logTimeoutQueueLength();
closeFilesForSession([fileSubA], session);
// This should cancel existing updates and schedule new ones
session.testhost.logTimeoutQueueLength();
// Open the fileA (as if rename)
// config project is updated to check if fileA is present in it
@@ -1362,7 +1416,6 @@ describe("unittests:: tsserver:: projects::", () => {
const originalFileExists = host.fileExists;
host.fileExists = s => s === fileA.path ? false : originalFileExists.call(host, s);
closeFilesForSession([fileA], session);
session.testhost.logTimeoutQueueLength(); // Update configured project and projects for open file
// This should create inferred project since fileSubA not on the disk
openFile(fileSubA);
@@ -1373,7 +1426,6 @@ describe("unittests:: tsserver:: projects::", () => {
// Actually trigger the file move
host.deleteFile(fileA.path);
host.ensureFileOrFolder(fileSubA);
session.testhost.logTimeoutQueueLength();
verifyGetErrRequest({ session, files: [fileB, fileSubA], existingTimeouts: true });
baselineTsserverLogs("projects", "handles delayed directory watch invoke on file creation", session);
@@ -1384,20 +1436,28 @@ describe("unittests:: tsserver:: projects::", () => {
});
it("assert when removing project", () => {
const host = createServerHost([commonFile1, commonFile2, libFile]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(commonFile1.path);
const randomFile: File = { path: "/random/random.ts", content: "export const y = 10;" };
const host = createServerHost([commonFile1, commonFile2, randomFile, libFile]);
const session = new TestSession(host);
openFilesForSession([commonFile1], session);
const service = session.getProjectService();
const project = service.inferredProjects[0];
// Intentionally create scriptinfo and attach it to project
const info = service.getOrCreateScriptInfoForNormalizedPath(commonFile2.path as ts.server.NormalizedPath, /*openedByClient*/ false)!;
info.attachToProject(project);
try {
service.applyChangesInOpenFiles(/*openFiles*/ undefined, /*changedFiles*/ undefined, [commonFile1.path]);
session.executeCommandSeq<ts.server.protocol.ApplyChangedToOpenFilesRequest>({
command: ts.server.protocol.CommandTypes.ApplyChangedToOpenFiles,
arguments: {
openFiles: [{ fileName: randomFile.path }],
closedFiles: [commonFile1.path],
},
});
}
catch (e) {
assert.isTrue(e.message.indexOf("Debug Failure. False expression: Found script Info still attached to project") === 0);
}
baselineTsserverLogs("projects", "assert when removing project", service);
baselineTsserverLogs("projects", "assert when removing project", session);
});
it("does not look beyond node_modules folders for default configured projects", () => {
const rootFilePath = ts.server.asNormalizedPath("/project/index.ts");
@@ -1405,25 +1465,17 @@ describe("unittests:: tsserver:: projects::", () => {
const nodeModulesFilePath1 = ts.server.asNormalizedPath("/project/node_modules/@types/a/index.d.ts");
const nodeModulesProjectPath1 = ts.server.asNormalizedPath("/project/node_modules/@types/a/tsconfig.json");
const nodeModulesFilePath2 = ts.server.asNormalizedPath("/project/node_modules/@types/b/index.d.ts");
const serverHost = createServerHost([
const host = createServerHost([
{ path: rootFilePath, content: "import 'a'; import 'b';" },
{ path: rootProjectPath, content: "{}" },
{ path: nodeModulesFilePath1, content: "{}" },
{ path: nodeModulesProjectPath1, content: "{}" },
{ path: nodeModulesFilePath2, content: "{}" },
]);
const projectService = createProjectService(serverHost, { useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(serverHost) });
const session = new TestSession({ host, useSingleInferredProject: true });
const openRootFileResult = projectService.openClientFile(rootFilePath);
assert.strictEqual(openRootFileResult.configFileName?.toString(), rootProjectPath);
const openNodeModulesFileResult1 = projectService.openClientFile(nodeModulesFilePath1);
assert.strictEqual(openNodeModulesFileResult1.configFileName?.toString(), nodeModulesProjectPath1);
const openNodeModulesFileResult2 = projectService.openClientFile(nodeModulesFilePath2);
assert.isUndefined(openNodeModulesFileResult2.configFileName);
baselineTsserverLogs("projects", "does not look beyond node_modules folders for default configured projects", projectService);
openFilesForSession([rootFilePath, nodeModulesFilePath1, nodeModulesFilePath2], session);
baselineTsserverLogs("projects", "does not look beyond node_modules folders for default configured projects", session);
});
describe("file opened is in configured project that will be removed", () => {
@@ -1452,7 +1504,7 @@ describe("unittests:: tsserver:: projects::", () => {
content: `export function foobar() { }`,
};
const host = createServerHost([testsConfig, testsFile, innerFile, innerConfig, innerSrcFile, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([testsFile], session);
closeFilesForSession([testsFile], session);
openFilesForSession([innerFile], session);
@@ -1501,7 +1553,7 @@ describe("unittests:: tsserver:: projects::", () => {
content: `function fooBar() { }`,
};
const host = createServerHost([rootConfig, mocksFile, innerFile, innerConfig, innerSrcFile, libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([mocksFile], session);
closeFilesForSession([mocksFile], session);
openFilesForSession([innerFile], session);
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
jsonToReadableText,
} from "../helpers";
@@ -9,7 +6,8 @@ import {
} from "../helpers/sampleProjectReferences";
import {
baselineTsserverLogs,
createProjectService,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -20,9 +18,8 @@ import {
describe("unittests:: tsserver:: projects with references: invoking when references are already built", () => {
it("on sample project", () => {
const host = createServerHost(getFsContentsForSampleProjectReferences());
const logger = createLoggerWithInMemoryLogs(host);
const service = createProjectService(host, { logger });
service.openClientFile("/user/username/projects/sample1/tests/index.ts");
const session = new TestSession(host);
openFilesForSession(["/user/username/projects/sample1/tests/index.ts"], session);
// local edit in ts file
host.appendFile("/user/username/projects/sample1/logic/index.ts", `function foo() {}`);
@@ -41,11 +38,11 @@ describe("unittests:: tsserver:: projects with references: invoking when referen
}),
);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "sample project", service);
baselineTsserverLogs("projectsWithReferences", "sample project", session);
});
describe("on transitive references in different folders", () => {
function createService() {
function setup() {
const aConfig: File = {
path: `/user/username/projects/myproject/a/tsconfig.json`,
content: jsonToReadableText({
@@ -91,22 +88,22 @@ X;`,
export class A {}`,
};
const host = createServerHost([libFile, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(cTs.path);
return { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs };
const session = new TestSession(host);
openFilesForSession([cTs], session);
return { host, session, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs };
}
it("non local edit", () => {
const { host, service, bTs } = createService();
const { host, session, bTs } = setup();
// non local edit
host.appendFile(bTs.path, `export function gFoo() { }`);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "transitive references with non local edit", service);
baselineTsserverLogs("projectsWithReferences", "transitive references with non local edit", session);
});
it("edit on config file", () => {
const { host, service, cConfig, refsTs } = createService();
const { host, session, cConfig, refsTs } = setup();
const nRefsTs: File = {
path: `/user/username/projects/myproject/nrefs/a.d.ts`,
content: refsTs.content,
@@ -120,11 +117,11 @@ export class A {}`,
// revert the edit on config file
host.writeFile(cConfig.path, cConfig.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "transitive references with edit on config file", service);
baselineTsserverLogs("projectsWithReferences", "transitive references with edit on config file", session);
});
it("edit in referenced config file", () => {
const { host, service, bConfig, refsTs } = createService();
const { host, session, bConfig, refsTs } = setup();
const nRefsTs: File = {
path: `/user/username/projects/myproject/nrefs/a.d.ts`,
content: refsTs.content,
@@ -138,34 +135,34 @@ export class A {}`,
// revert the edit on config file
host.writeFile(bConfig.path, bConfig.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "transitive references with edit in referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "transitive references with edit in referenced config file", session);
});
it("deleting referenced config file", () => {
const { host, service, bConfig } = createService();
const { host, session, bConfig } = setup();
host.deleteFile(bConfig.path);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
// revert
host.writeFile(bConfig.path, bConfig.content);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
baselineTsserverLogs("projectsWithReferences", "transitive references with deleting referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "transitive references with deleting referenced config file", session);
});
it("deleting transitively referenced config file", () => {
const { host, service, aConfig } = createService();
const { host, session, aConfig } = setup();
host.deleteFile(aConfig.path);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
// revert
host.writeFile(aConfig.path, aConfig.content);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
baselineTsserverLogs("projectsWithReferences", "transitive references with deleting transitively referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "transitive references with deleting transitively referenced config file", session);
});
});
describe("on transitive references in different folders without files", () => {
function createService() {
function setup() {
const aConfig: File = {
path: `/user/username/projects/myproject/a/tsconfig.json`,
content: jsonToReadableText({ compilerOptions: { composite: true } }),
@@ -206,22 +203,22 @@ X;`,
export class A {}`,
};
const host = createServerHost([libFile, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs]);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(cTs.path);
return { host, service, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs };
const session = new TestSession(host);
openFilesForSession([cTs], session);
return { host, session, aConfig, bConfig, cConfig, aTs, bTs, cTs, refsTs };
}
it("non local edit", () => {
const { host, service, bTs } = createService();
const { host, session, bTs } = setup();
// non local edit
host.appendFile(bTs.path, `export function gFoo() { }`);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with non local edit", service);
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with non local edit", session);
});
it("edit on config file", () => {
const { host, service, cConfig, refsTs } = createService();
const { host, session, cConfig, refsTs } = setup();
const nRefsTs: File = {
path: `/user/username/projects/myproject/nrefs/a.d.ts`,
content: refsTs.content,
@@ -235,11 +232,11 @@ export class A {}`,
// revert the edit on config file
host.writeFile(cConfig.path, cConfig.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with edit on config file", service);
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with edit on config file", session);
});
it("edit in referenced config file", () => {
const { host, service, bConfig, refsTs } = createService();
const { host, session, bConfig, refsTs } = setup();
const nRefsTs: File = {
path: `/user/username/projects/myproject/nrefs/a.d.ts`,
content: refsTs.content,
@@ -253,29 +250,29 @@ export class A {}`,
// revert the edit on config file
host.writeFile(bConfig.path, bConfig.content);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with edit in referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with edit in referenced config file", session);
});
it("deleting referenced config file", () => {
const { host, service, bConfig } = createService();
const { host, session, bConfig } = setup();
host.deleteFile(bConfig.path);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
// revert
host.writeFile(bConfig.path, bConfig.content);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with deleting referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with deleting referenced config file", session);
});
it("deleting transitively referenced config file", () => {
const { host, service, aConfig } = createService();
const { host, session, aConfig } = setup();
host.deleteFile(aConfig.path);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
// revert
host.writeFile(aConfig.path, aConfig.content);
host.runQueuedTimeoutCallbacks(); // Schedules failed lookup invalidation
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with deleting transitively referenced config file", service);
baselineTsserverLogs("projectsWithReferences", "trasitive references without files with deleting transitively referenced config file", session);
});
});
});
+7 -10
View File
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -19,7 +16,7 @@ describe("unittests:: tsserver:: refactors", () => {
content: "function f() {\n 1;\n}",
};
const host = createServerHost([file]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
@@ -57,7 +54,7 @@ describe("unittests:: tsserver:: refactors", () => {
};
const host = createServerHost([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
@@ -79,7 +76,7 @@ describe("unittests:: tsserver:: refactors", () => {
const aTs: File = { path: "/Foo/a.ts", content: "const x = 0;" };
const tsconfig: File = { path: "/Foo/tsconfig.json", content: '{ "files": ["./a.ts"] }' };
const host = createServerHost([aTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
@@ -106,7 +103,7 @@ describe("unittests:: tsserver:: refactors", () => {
};
const tsconfig: File = { path: "/Foo/tsconfig.json", content: `{ "files": ["./a.ts", "./b.ts"] }` };
const host = createServerHost([aTs, bTs, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
@@ -139,7 +136,7 @@ describe("unittests:: tsserver:: refactors", () => {
content: `{ "files": ["./a.ts"] }`,
};
const host = createServerHost([aTs, bTxt, tsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
@@ -181,7 +178,7 @@ describe("unittests:: tsserver:: refactors", () => {
content: `{ "files": ["./a.ts"] }`,
};
const host = createServerHost([fooATs, fooTsconfig, barATs, barTsconfig]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([barATs], session);
session.executeCommandSeq<ts.server.protocol.GetEditsForRefactorRequest>({
+6 -10
View File
@@ -1,13 +1,10 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
logInferredProjectsOrphanStatus,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -25,7 +22,7 @@ describe("unittests:: tsserver:: reload", () => {
content: "const y = 42",
};
const host = createServerHost([f1, tmp]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
// send open request
openFilesForSession([f1], session);
@@ -63,13 +60,12 @@ describe("unittests:: tsserver:: reload", () => {
content: "const y = 42",
};
const host = createServerHost([f1, tmp, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const openContent = "let z = 1";
// send open request
openFilesForSession([{ file: f1.path, content: openContent }], session);
const projectService = session.getProjectService();
const info = projectService.getScriptInfo(f1.path)!;
const info = session.getProjectService().getScriptInfo(f1.path)!;
assert.isDefined(info);
checkScriptInfoContents("contents set during open request");
@@ -126,12 +122,12 @@ describe("unittests:: tsserver:: reload", () => {
baselineTsserverLogs("reload", "should work when script info doesnt have any project open", session);
function checkInferredProjectIsOrphan() {
logInferredProjectsOrphanStatus(projectService);
logInferredProjectsOrphanStatus(session);
session.logger.log(`info:: ${info.path}:: ${info.containingProjects.map(p => p.projectName).join(",")}`);
}
function checkScriptInfoAndProjects(captionForContents: string) {
assert.strictEqual(projectService.getScriptInfo(f1.path), info);
assert.strictEqual(session.getProjectService().getScriptInfo(f1.path), info);
checkScriptInfoContents(captionForContents);
}
@@ -1,13 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openExternalProjectForSession,
openFilesForSession,
setCompilerOptionsForInferredProjectsRequestForSession,
@@ -48,14 +44,12 @@ describe("unittests:: tsserver:: reloadProjects", () => {
const updatedText = `${file2.content}
bar();`;
host.writeFile(file2.path, updatedText);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
});
// delete file
host.deleteFile(file2.path);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
});
@@ -63,7 +57,7 @@ describe("unittests:: tsserver:: reloadProjects", () => {
it("configured project", () => {
const host = createServerHost([configFile, libFile, file1, file2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { watchOptions: { excludeFiles: [file2.path] } },
@@ -72,7 +66,6 @@ describe("unittests:: tsserver:: reloadProjects", () => {
// Install module1
host.ensureFileOrFolder(moduleFile);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
@@ -84,7 +77,7 @@ describe("unittests:: tsserver:: reloadProjects", () => {
it("inferred project", () => {
const host = createServerHost([libFile, file1, file2]);
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { watchOptions: { excludeFiles: [file2.path] } },
@@ -99,7 +92,6 @@ describe("unittests:: tsserver:: reloadProjects", () => {
// Install module1
host.ensureFileOrFolder(moduleFile);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
@@ -111,7 +103,7 @@ describe("unittests:: tsserver:: reloadProjects", () => {
it("external project", () => {
const host = createServerHost([libFile, file1, file2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { watchOptions: { excludeFiles: [file2.path] } },
@@ -125,7 +117,6 @@ describe("unittests:: tsserver:: reloadProjects", () => {
// Install module1
host.ensureFileOrFolder(moduleFile);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
@@ -137,7 +128,7 @@ describe("unittests:: tsserver:: reloadProjects", () => {
it("external project with config file", () => {
const host = createServerHost([libFile, file1, file2, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: { watchOptions: { excludeFiles: [file2.path] } },
@@ -151,7 +142,6 @@ describe("unittests:: tsserver:: reloadProjects", () => {
// Install module1
host.ensureFileOrFolder(moduleFile);
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq({
command: ts.server.protocol.CommandTypes.ReloadProjects,
+5 -8
View File
@@ -1,12 +1,9 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -19,7 +16,7 @@ describe("unittests:: tsserver:: rename", () => {
const bTs: File = { path: "/b.ts", content: 'import { a } from "./a";' };
const host = createServerHost([aTs, bTs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([bTs], session);
// rename fails with allowRenameOfImportPath disabled
@@ -57,7 +54,7 @@ describe("unittests:: tsserver:: rename", () => {
it("works with prefixText and suffixText when enabled", () => {
const aTs: File = { path: "/a.ts", content: "const x = 0; const o = { x };" };
const host = createServerHost([aTs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs], session);
// rename with prefixText and suffixText disabled
@@ -96,7 +93,7 @@ describe("unittests:: tsserver:: rename", () => {
const bTs: File = { path: "/b.ts", content: `import aTest from "./a"; function test() { return aTest(); }` };
const host = createServerHost([aTs, bTs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([bTs], session);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
@@ -114,7 +111,7 @@ describe("unittests:: tsserver:: rename", () => {
const aTs: File = { path: "/a.ts", content: "const x = 1; export { x };" };
const bTs: File = { path: "/b.ts", content: `import { x } from "./a"; const y = x + 1;` };
const host = createServerHost([aTs, bTs]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([aTs, bTs], session);
// rename from file with prefixText and suffixText enabled
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import * as Utils from "../../_namespaces/Utils";
import {
@@ -11,16 +8,13 @@ import {
} from "../helpers/contents";
import {
baselineTsserverLogs,
createProjectService,
createSession,
openExternalProjectForSession,
openFilesForSession,
setCompilerOptionsForInferredProjectsRequestForSession,
TestSession,
toExternalFiles,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
TestTypingsInstaller,
} from "../helpers/typingsInstaller";
import {
createServerHost,
File,
@@ -38,15 +32,11 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem extra r
content: "export let x = 1",
};
const host = createServerHost([file1, lib]);
const logger = createLoggerWithInMemoryLogs(host);
const projectService = createProjectService(host, {
typingsInstaller: new TestTypingsInstaller(host, logger, { globalTypingsCacheLocation: "/a/cache" }),
logger,
});
const session = new TestSession({ host, globalTypingsCacheLocation: "/a/cache" });
projectService.setCompilerOptionsForInferredProjects({ traceResolution: true, allowJs: true });
projectService.openClientFile(file1.path);
baselineTsserverLogs("resolutionCache", "can load typings that are proper modules", projectService);
setCompilerOptionsForInferredProjectsRequestForSession({ traceResolution: true, allowJs: true }, session);
openFilesForSession([file1], session);
baselineTsserverLogs("resolutionCache", "can load typings that are proper modules", session);
});
});
@@ -72,9 +62,9 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem watchin
}),
};
const host = createServerHost([f1, t1, tsconfig]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
projectService.openClientFile(f1.path);
openFilesForSession([f1], session);
// delete t1
host.deleteFile(t1.path);
@@ -86,7 +76,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem watchin
// run throttled operation
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "works correctly when typings are added or removed", projectService);
baselineTsserverLogs("resolutionCache", "works correctly when typings are added or removed", session);
});
});
@@ -101,7 +91,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
content: "import * as T from './moduleFile'; T.bar();",
};
const host = createServerHost([file1]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
command: ts.server.protocol.CommandTypes.SemanticDiagnosticsSync,
@@ -132,7 +122,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
content: 'import f = require("pad"); f;',
};
const host = createServerHost([file1, libFile]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: {
@@ -164,7 +154,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
};
const host = createServerHost([file]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
@@ -182,7 +172,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
};
const host = createServerHost([file]);
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
@@ -207,14 +197,13 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
};
const host = createServerHost([file]);
const session = createSession(host, { canUseEvents: true, suppressDiagnosticEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, suppressDiagnosticEvents: true });
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: { file: file.path, fileContent: file.content },
});
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq<ts.server.protocol.GeterrRequest>({
command: ts.server.protocol.CommandTypes.Geterr,
arguments: {
@@ -223,7 +212,6 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
},
});
session.testhost.logTimeoutQueueLength();
session.executeCommandSeq<ts.server.protocol.GeterrForProjectRequest>({
command: ts.server.protocol.CommandTypes.GeterrForProject,
arguments: {
@@ -232,7 +220,6 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem add the
},
});
session.testhost.logTimeoutQueueLength();
baselineTsserverLogs("resolutionCache", "suppressed diagnostic events", session);
});
});
@@ -248,7 +235,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem rename
content: "import * as T from './moduleFile'; T.bar();",
};
const host = createServerHost([moduleFile, file1]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -295,7 +282,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem rename
content: `{}`,
};
const host = createServerHost([moduleFile, file1, configFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -329,15 +316,23 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem rename
path: "/a/b/tsconfig.json",
content: "{}",
};
const projectName = "project1";
const projectFileName = "project1";
const host = createServerHost([f1]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
openExternalProjectForSession({ rootFiles: toExternalFiles([f1.path, config.path]), options: {}, projectFileName: projectName }, session);
const session = new TestSession(host);
openExternalProjectForSession({
rootFiles: toExternalFiles([f1.path, config.path]),
options: {},
projectFileName,
}, session);
// should have one external project since config file is missing
host.writeFile(config.path, config.content);
openExternalProjectForSession({ rootFiles: toExternalFiles([f1.path, config.path]), options: {}, projectFileName: projectName }, session);
openExternalProjectForSession({
rootFiles: toExternalFiles([f1.path, config.path]),
options: {},
projectFileName,
}, session);
baselineTsserverLogs("resolutionCache", "should property handle missing config files", session);
});
@@ -360,9 +355,9 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem rename
path: "/a/c",
};
const host = createServerHost([f1, config, node, cwd], { currentDirectory: cwd.path });
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(f1.path);
baselineTsserverLogs("resolutionCache", subScenario, projectService);
const session = new TestSession(host);
openFilesForSession([f1], session);
baselineTsserverLogs("resolutionCache", subScenario, session);
});
}
verifyTypesLoad("types should load from config file path if config exists", /*includeTypeRoots*/ false);
@@ -407,14 +402,13 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module
const { module1, module2 } = getModules(`/user/username/projects/myproject/src/module1.ts`, `/user/username/projects/myproject/module2.ts`);
const files = [module1, module2, file1, file2, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file1.path, file1.content + fileContent);
host.writeFile(file2.path, file2.content + fileContent);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "relative module name from files in same folder", service);
baselineTsserverLogs("resolutionCache", "relative module name from files in same folder", session);
});
it("non relative module name", () => {
@@ -423,13 +417,12 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module
const { module1, module2 } = getModules(`/user/username/projects/myproject/src/node_modules/module1/index.ts`, `/user/username/projects/myproject/node_modules/module2/index.ts`);
const files = [module1, module2, file1, file2, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file1.path, file1.content + fileContent);
host.writeFile(file2.path, file2.content + fileContent);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "non relative module name from files in same folder", service);
baselineTsserverLogs("resolutionCache", "non relative module name from files in same folder", session);
});
});
@@ -463,15 +456,14 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module
const { module1, module2 } = getModules(`/user/username/projects/myproject/product/src/module1.ts`, `/user/username/projects/myproject/product/module2.ts`);
const files = [module1, module2, file1, file2, file3, file4, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file1.path, file1.content + fileContent1);
host.writeFile(file2.path, file2.content + fileContent2);
host.writeFile(file3.path, file3.content + fileContent3);
host.writeFile(file4.path, file4.content + fileContent4);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "relative module name from files in different folders", service);
baselineTsserverLogs("resolutionCache", "relative module name from files in different folders", session);
});
it("non relative module name", () => {
@@ -480,15 +472,14 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module
const { module1, module2 } = getModules(`/user/username/projects/myproject/product/node_modules/module1/index.ts`, `/user/username/projects/myproject/node_modules/module2/index.ts`);
const files = [module1, module2, file1, file2, file3, file4, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
host.writeFile(file1.path, file1.content + fileContent);
host.writeFile(file2.path, file2.content + fileContent);
host.writeFile(file3.path, file3.content + fileContent);
host.writeFile(file4.path, file4.content + fileContent);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "non relative module name from files in different folders", service);
baselineTsserverLogs("resolutionCache", "non relative module name from files in different folders", session);
});
it("non relative module name from inferred project", () => {
@@ -502,15 +493,14 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem module
const { module1, module2 } = getModules(`/user/username/projects/myproject/product/node_modules/module1/index.ts`, `/user/username/projects/myproject/node_modules/module2/index.ts`);
const files = [module1, module2, file1, file2, file3, file4, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.setCompilerOptionsForInferredProjects({ traceResolution: true });
service.openClientFile(file1.path);
host.writeFile(file1.path, file1.content + importModuleContent);
const session = new TestSession(host);
setCompilerOptionsForInferredProjectsRequestForSession({ traceResolution: true }, session);
openFilesForSession([file1], session);
host.writeFile(file2.path, file2.content + importModuleContent);
host.writeFile(file3.path, file3.content + importModuleContent);
host.writeFile(file4.path, file4.content + importModuleContent);
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "non relative module name from inferred project", service);
baselineTsserverLogs("resolutionCache", "non relative module name from inferred project", session);
});
});
@@ -561,7 +551,7 @@ export const x = 10;`,
const files = [...(useNodeFile ? [nodeFile] : []), electronFile, srcFile, moduleFile, configFile, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file: srcFile.path, content: srcFile.content, scriptKindName: "TS", projectRootPath: "/user/username/projects/myproject" }], session);
baselineTsserverLogs("resolutionCache", scenario, session);
});
@@ -586,13 +576,12 @@ export const x = 10;`,
it("when watching node_modules in inferred project for failed lookup/closed script infos", () => {
const files = [libFile, file1, file2];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
service.testhost.logTimeoutQueueLength();
const session = new TestSession(host);
openFilesForSession([file1], session);
host.ensureFileOrFolder(npmCacheFile);
service.testhost.logTimeoutQueueLength();
baselineTsserverLogs("resolutionCache", "when watching node_modules in inferred project for failed lookup/closed script infos", service);
session.host.baselineHost("After npm cache update");
baselineTsserverLogs("resolutionCache", "when watching node_modules in inferred project for failed lookup/closed script infos", session);
});
it("when watching node_modules as part of wild card directories in config project", () => {
const config: File = {
@@ -601,13 +590,12 @@ export const x = 10;`,
};
const files = [libFile, file1, file2, config];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
service.testhost.logTimeoutQueueLength();
const session = new TestSession(host);
openFilesForSession([file1], session);
host.ensureFileOrFolder(npmCacheFile);
service.testhost.logTimeoutQueueLength();
baselineTsserverLogs("resolutionCache", "when watching node_modules as part of wild card directories in config project", service);
session.host.baselineHost("After npm cache update");
baselineTsserverLogs("resolutionCache", "when watching node_modules as part of wild card directories in config project", session);
});
});
@@ -621,13 +609,13 @@ export const x = 10;`,
const { module1, module2 } = getModules(`/user/username/projects/myproject/src/node_modules/module1/index.ts`, `/user/username/projects/myproject/node_modules/module2/index.ts`);
const files = [module1, module2, file1, configFile, libFile];
const host = createServerHost(files);
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openClientFile(file1.path);
const session = new TestSession(host);
openFilesForSession([file1], session);
// invoke callback to simulate saving
host.modifyFile(file1.path, file1.content, { invokeFileDeleteCreateAsPartInsteadOfChange: true });
host.runQueuedTimeoutCallbacks();
baselineTsserverLogs("resolutionCache", "avoid unnecessary lookup invalidation on save", service);
baselineTsserverLogs("resolutionCache", "avoid unnecessary lookup invalidation on save", session);
});
});
});
@@ -663,7 +651,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem with pr
export const y = x;
`,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/users/username/projects/app/appB.ts"], session);
baselineTsserverLogs("resolutionCache", "sharing across references", session);
});
@@ -696,7 +684,7 @@ describe("unittests:: tsserver:: resolutionCache:: tsserverProjectSystem with pr
export const y = x;
`,
});
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(["/users/username/projects/app/appB.ts"], session);
baselineTsserverLogs("resolutionCache", "not sharing across references", session);
});
@@ -70,7 +70,6 @@ describe("unittests:: tsserver:: Session:: General functionality", () => {
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined!, // TODO: GH#18217
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
logger: nullLogger(),
@@ -1,15 +1,12 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openExternalProjectForSession,
openFilesForSession,
TestSession,
toExternalFiles,
} from "../helpers/tsserver";
import {
@@ -35,7 +32,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};`,
};
const host = createServerHost([file1, file2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1, file2], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -77,7 +74,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};`,
};
const host = createServerHost([jsFile, dTsFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "project1",
@@ -108,7 +105,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};`,
};
const host = createServerHost([jsFile, dTsFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "project1",
@@ -143,7 +140,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
declare var x: string;`,
};
const host = createServerHost([jsconfigFile, jsFile, dTsFile1, dTsFile2]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([jsFile], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -168,7 +165,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};
const host = createServerHost([jsFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([jsFile], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -194,7 +191,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};
const host = createServerHost([jsconfigFile, jsFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([jsFile], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -221,7 +218,7 @@ describe("unittests:: tsserver:: with skipLibCheck", () => {
};
const host = createServerHost([jsconfigFile, jsFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([jsFile], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -29,7 +26,7 @@ class Foo {
}`,
};
const host = createServerHost([file, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
session.executeCommandSeq<ts.server.protocol.SelectionRangeRequest>({
command: ts.server.protocol.CommandTypes.SelectionRange,
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
dedent,
@@ -11,9 +8,9 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
protocolLocationFromSubstring,
TestSession,
verifyGetErrRequest,
} from "../helpers/tsserver";
import {
@@ -64,7 +61,7 @@ describe("unittests:: tsserver:: symLinks", () => {
const files = [cFile, libFile, aFile, aTsconfig, aC, bFile, bTsconfig, bC];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession(
[
{ file: aFile, projectRootPath: folderA },
@@ -134,7 +131,7 @@ new C();`,
};
function createSessionAndOpenFile(host: TestServerHost) {
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.OpenRequest>({
command: ts.server.protocol.CommandTypes.Open,
arguments: {
@@ -244,7 +241,7 @@ new C();`,
}),
"C:/temp/replay/axios-src/node_modules/follow-redirects/index.js": "export const x = 10;",
}, { windowsStyleRoot: "C:/" });
const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host), disableAutomaticTypingAcquisition: true });
const session = new TestSession({ host, disableAutomaticTypingAcquisition: true });
openFilesForSession(["c:/temp/replay/axios-src/lib/core/AxiosHeaders.js"], session); // Creates InferredProject1 and AutoImportProvider1
session.executeCommandSeq<ts.server.protocol.UpdateOpenRequest>({ // Different content from disk
command: ts.server.protocol.CommandTypes.UpdateOpen,
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -98,7 +95,7 @@ function setup() {
depSrcSubFolderIndexTs,
link,
]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
const projectService = session.getProjectService();
return {
host,
@@ -1,11 +1,7 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openFilesForSession,
protocolFileLocationFromSubstring,
TestSession,
@@ -44,7 +40,7 @@ import { something } from "something";
content: "{}",
};
const host = createServerHost([file1, file2, file3, something, libFile, configFile]);
const session = createSession(host, { serverMode: ts.LanguageServiceMode.Syntactic, useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, serverMode: ts.LanguageServiceMode.Syntactic, useSingleInferredProject: true });
return { host, session, file1, file2, file3, something, configFile };
}
@@ -155,7 +151,7 @@ function fooB() { }`,
content: "{}",
};
const host = createServerHost([file1, file2, file3, something, libFile, configFile]);
const session = createSession(host, { serverMode: ts.LanguageServiceMode.Syntactic, useSingleInferredProject: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, serverMode: ts.LanguageServiceMode.Syntactic, useSingleInferredProject: true });
const service = session.getProjectService();
openFilesForSession([file1], session);
const project = service.inferredProjects[0];
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -40,7 +37,7 @@ describe("Test Suite 1", () => {
};
const files = [app, libFile, tsconfig];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file: app.path, content: app.content }], session);
host.writeFile(unitTest1.path, unitTest1.content);
+13 -52
View File
@@ -1,6 +1,3 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
@@ -8,9 +5,9 @@ import {
import {
baselineTsserverLogs,
closeFilesForSession,
createSession,
openExternalProjectForSession,
openFilesForSession,
TestSession,
toExternalFiles,
} from "../helpers/tsserver";
import {
@@ -22,10 +19,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
it("does nothing for inferred project", () => {
const file = makeFile("/a.js");
const host = createServerHost([file]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("telemetry", "does nothing for inferred project", session);
});
@@ -36,10 +30,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const tsconfig = makeFile("/a/tsconfig.json", {});
const host = createServerHost([file, file2, tsconfig]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
closeFilesForSession([file], session);
openFilesForSession([file2], session);
@@ -54,10 +45,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, include: ["src"] });
const host = createServerHost([...files, notIncludedFile, tsconfig]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([files[0]], session);
baselineTsserverLogs("telemetry", "counts files by extension", session);
});
@@ -65,10 +53,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
it("works with external project", () => {
const file1 = makeFile("/a.ts");
const host = createServerHost([file1]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
const compilerOptions: ts.server.protocol.CompilerOptions = { strict: true };
const projectFileName = "/hunter2/foo.csproj";
@@ -133,10 +118,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, files: ["/a.ts"] });
const host = createServerHost([file, tsconfig]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("telemetry", "does not expose paths", session);
});
@@ -152,10 +134,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
compileOnSave: true,
});
const host = createServerHost([file, tsconfig]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("telemetry", "sends telemetry for extends, files, include, exclude, and compileOnSave", session);
});
@@ -180,10 +159,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
},
});
const host = createServerHost([file, jsconfig]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("telemetry", "sends telemetry for typeAcquisition settings", session);
});
@@ -195,10 +171,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
compilerOptions: autoJsCompilerOptions,
});
const host = createServerHost([tsconfig, tsFile, jsFile]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([jsFile], session);
baselineTsserverLogs("telemetry", "sends telemetry for file sizes", session);
});
@@ -207,10 +180,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const file = makeFile("/a.js");
const tsconfig = makeFile("/jsconfig.json", {});
const host = createServerHost([tsconfig, file]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
const fileSize = ts.server.maxProgramSizeForNonTsFiles + 1;
host.getFileSize = () => fileSize;
openFilesForSession([file], session);
@@ -222,10 +192,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const ajs = makeFile("/a.js", "// @ts-check\nconst x = 0;");
const bjs = makeFile("/b.js");
const host = createServerHost([ajs, bjs]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([ajs, bjs], session);
// No repeated send for opening a file seen before.
@@ -236,10 +203,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
it("not for '.ts' file", () => {
const ats = makeFile("/a.ts", "");
const host = createServerHost([ats]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([ats], session);
baselineTsserverLogs("telemetry", "not for ts file", session);
});
@@ -249,10 +213,7 @@ describe("unittests:: tsserver:: project telemetry", () => {
const compilerOptions: ts.CompilerOptions = { checkJs: true };
const jsconfig = makeFile("/jsconfig.json", { compilerOptions });
const host = createServerHost([jsconfig, file]);
const session = createSession(host, {
canUseEvents: true,
logger: createLoggerWithInMemoryLogs(host),
});
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("telemetry", "even for project with ts-check in config", session);
});
@@ -1,10 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createProjectService,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -116,9 +114,9 @@ describe("unittests:: tsserver:: Text storage", () => {
const host = createServerHost([largeFile]);
// The large-file handling requires a ScriptInfo with a containing project
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openClientFile(largeFile.path);
const scriptInfo = projectService.getScriptInfo(largeFile.path);
const session = new TestSession(host);
openFilesForSession([largeFile], session);
const scriptInfo = session.getProjectService().getScriptInfo(largeFile.path);
const ts1 = new ts.server.TextStorage(host, scriptInfo!);
@@ -126,7 +124,7 @@ describe("unittests:: tsserver:: Text storage", () => {
assert.isFalse(ts1.hasScriptVersionCache_TestOnly());
assert.strictEqual(largeFile.content.length, ts1.getTelemetryFileSize());
baselineTsserverLogs("textStorage", "should be able to return the file size when a JS file is too large to load into text", projectService);
baselineTsserverLogs("textStorage", "should be able to return the file size when a JS file is too large to load into text", session);
});
it("should return the file size without reloading the file", () => {
@@ -1,18 +1,13 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createProjectService,
openExternalProjectForSession,
openFilesForSession,
TestSession,
toExternalFile,
} from "../helpers/tsserver";
import {
TestTypingsInstaller,
} from "../helpers/typingsInstaller";
import {
createServerHost,
} from "../helpers/virtualFileSystemWithWatch";
@@ -28,15 +23,15 @@ describe("unittests:: tsserver:: typeAquisition:: autoDiscovery", () => {
content: "",
};
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
projectService.openExternalProject({
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: "/a/b/proj.csproj",
rootFiles: [toExternalFile(file2.path), { fileName: file1.path, hasMixedContent: true, scriptKind: ts.ScriptKind.JS }],
rootFiles: [toExternalFile(file2.path), { fileName: file1.path, hasMixedContent: true, scriptKind: "JS" }],
options: {},
});
const typeAcquisition = projectService.externalProjects[0].getTypeAcquisition();
projectService.logger.log(`Typine acquisition should be enabled: ${typeAcquisition.enable}`);
baselineTsserverLogs("typeAquisition", "does not depend on extension", projectService);
}, session);
const typeAcquisition = session.getProjectService().externalProjects[0].getTypeAcquisition();
session.logger.log(`Typine acquisition should be enabled: ${typeAcquisition.enable}`);
baselineTsserverLogs("typeAquisition", "does not depend on extension", session);
});
});
@@ -60,14 +55,10 @@ describe("unittests:: tsserver:: typeAquisition:: prefer typings to js", () => {
content: jsonToReadableText({ compilerOptions: { allowJs: true }, exclude: ["node_modules"] }),
};
const host = createServerHost([f1, barjs, barTypings, config]);
const logger = createLoggerWithInMemoryLogs(host);
const projectService = createProjectService(host, {
typingsInstaller: new TestTypingsInstaller(host, logger, { globalTypingsCacheLocation }),
logger,
});
const session = new TestSession({ host, globalTypingsCacheLocation });
projectService.openClientFile(f1.path);
openFilesForSession([f1], session);
baselineTsserverLogs("typeAquisition", "prefer typings in second pass", projectService);
baselineTsserverLogs("typeAquisition", "prefer typings in second pass", session);
});
});
@@ -1,11 +1,8 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -165,7 +162,7 @@ describe("unittests:: tsserver:: typeOnlyImportChains", () => {
function assertUsageError(subScenario: string, files: readonly File[], openFile: File) {
const host = createServerHost([...files, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([openFile], session);
session.executeCommandSeq<ts.server.protocol.SemanticDiagnosticsSyncRequest>({
command: ts.server.protocol.CommandTypes.SemanticDiagnosticsSync,
@@ -1,13 +1,10 @@
import {
createLoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import {
jsonToReadableText,
} from "../helpers";
import {
baselineTsserverLogs,
createSession,
openFilesForSession,
TestSession,
} from "../helpers/tsserver";
import {
createServerHost,
@@ -63,7 +60,7 @@ declare class TestLib {
const files = [typeLib, appLib, testFile, testConfig, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([testFile], session);
host.writeFile(appLib.path, appLib.content.replace("test()", "test2()"));
host.runQueuedTimeoutCallbacks();
@@ -92,7 +89,7 @@ declare class TestLib {
};
const files = [file, tsconfig, filesystem, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file], session);
baselineTsserverLogs("typeReferenceDirectives", "when typeReferenceDirective is relative path and in a sibling folder", session);
});
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
import {
createLoggerWithInMemoryLogs,
Logger,
LoggerWithInMemoryLogs,
} from "../../../harness/tsserverLogger";
import * as ts from "../../_namespaces/ts";
import {
@@ -12,8 +12,6 @@ import {
} from "../helpers/tscWatch";
import {
baselineTsserverLogs,
createProjectService,
createSession,
openExternalProjectForSession,
openFilesForSession,
protocolFileLocationFromSubstring,
@@ -55,7 +53,7 @@ describe("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem watchD
const environmentVariables = new Map<string, string>();
environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
const host = createServerHost(files, { environmentVariables });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([index], session);
session.executeCommandSeq<ts.server.protocol.CompletionsRequest>({
command: ts.server.protocol.CommandTypes.CompletionInfo,
@@ -109,7 +107,7 @@ describe("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem Watche
};
const files = [configFile, file1, file2, libFile];
const host = createServerHost(files, { windowsStyleRoot: "c:/" });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([file1], session);
baselineTsserverLogs("watchEnvironment", scenario, session);
});
@@ -143,7 +141,7 @@ it(`unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem recursive wa
const environmentVariables = new Map<string, string>();
environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
const host = createServerHost([index, file1, configFile, libFile, nodeModulesExistingUnusedFile], { environmentVariables });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([index], session);
const nodeModulesIgnoredFileFromIgnoreDirectory: File = {
@@ -178,7 +176,7 @@ it(`unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem recursive wa
emacsIgnoredFileFromIgnoreDirectory,
].forEach(ignoredEntity => {
host.ensureFileOrFolder(ignoredEntity);
session.testhost.logTimeoutQueueLength();
session.host.baselineHost("After writing ignored file or folder");
});
baselineTsserverLogs("watchEnvironment", `recursive directory does not watch files starting with dot in node_modules`, session);
@@ -193,7 +191,7 @@ it("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem watching fil
verifyFilePathStyle("//vda1cs4850/c$/users/username/myprojects/project/x.js", logger);
baselineTsserverLogs("watchEnvironment", `watching files with network style paths`, { logger });
function verifyFilePathStyle(path: string, logger: Logger) {
function verifyFilePathStyle(path: string, logger: LoggerWithInMemoryLogs) {
const windowsStyleRoot = path.substring(0, ts.getRootLength(path));
const file: File = { path, content: "const x = 10" };
const host = createServerHost(
@@ -203,7 +201,7 @@ it("unittests:: tsserver:: watchEnvironment:: tsserverProjectSystem watching fil
logger.host = host;
logger.info(`For files of style ${path}`);
logger.log(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
const session = createSession(host, { logger });
const session = new TestSession({ host, logger });
openFilesForSession([file], session);
}
});
@@ -216,8 +214,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1));
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -237,8 +234,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true });
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -258,8 +254,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -283,8 +278,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1));
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
baselineTsserverLogs("watchEnvironment", `with watchFile option in configFile`, session);
});
@@ -300,8 +294,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true });
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
openFilesForSession([{ file: commonFile1, projectRootPath: "/a/b" }], session);
baselineTsserverLogs("watchEnvironment", `with watchDirectory option in configFile`, session);
});
@@ -317,8 +310,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
};
const files = [libFile, commonFile2, configFile];
const host = createServerHost(files.concat(commonFile1), { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
const logger = createLoggerWithInMemoryLogs(host);
const session = createSession(host, { logger });
const session = new TestSession(host);
session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({
command: ts.server.protocol.CommandTypes.Configure,
arguments: {
@@ -366,7 +358,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo, configFile];
const host = createServerHost(files, { currentDirectory: "/user/username/projects/myproject" });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
setupConfigureHost(session, configureHost);
openFilesForSession([main], session);
return session;
@@ -386,7 +378,7 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: "/user/username/projects/myproject" });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
setupConfigureHost(session, configureHost);
openExternalProjectForSession({
projectFileName: `/user/username/projects/myproject/project.csproj`,
@@ -411,23 +403,23 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: "/user/username/projects/myproject" });
const service = createProjectService(host, { logger: createLoggerWithInMemoryLogs(host) });
service.openExternalProject({
const session = new TestSession(host);
openExternalProjectForSession({
projectFileName: `/user/username/projects/myproject/project.csproj`,
rootFiles: toExternalFiles([main.path, bar.path, foo.path]),
options: { excludeDirectories: ["**/../*"] },
} as ts.server.protocol.ExternalProject);
service.openClientFile(main.path);
const project = service.externalProjects[0];
service.logger.info(jsonToReadableText(project.getAllProjectErrors()));
baselineTsserverLogs("watchEnvironment", `external project watch options errors`, service);
}, session);
openFilesForSession([main], session);
const project = session.getProjectService().externalProjects[0];
session.logger.info(jsonToReadableText(project.getAllProjectErrors()));
baselineTsserverLogs("watchEnvironment", `external project watch options errors`, session);
});
function setupInferredProject(configureHost?: boolean) {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: "/user/username/projects/myproject" });
const session = createSession(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
setupConfigureHost(session, configureHost);
setCompilerOptionsForInferredProjectsRequestForSession({
options: { excludeDirectories: ["node_modules"] },
@@ -451,12 +443,15 @@ describe("unittests:: tsserver:: watchEnvironment:: handles watch compiler optio
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: "/user/username/projects/myproject" });
const service = createProjectService(host, { useInferredProjectPerProjectRoot: true, logger: createLoggerWithInMemoryLogs(host) });
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["**/../*"] }, "/user/username/projects/myproject");
service.openClientFile(main.path, main.content, ts.ScriptKind.TS, "/user/username/projects/myproject");
const project = service.inferredProjects[0];
service.logger.info(jsonToReadableText(project.getAllProjectErrors()));
baselineTsserverLogs("watchEnvironment", `inferred project watch options errors`, service);
const session = new TestSession({ host, useInferredProjectPerProjectRoot: true });
setCompilerOptionsForInferredProjectsRequestForSession({
options: { excludeDirectories: ["**/../*"] },
projectRootPath: "/user/username/projects/myproject",
}, session);
openFilesForSession([{ file: main.path, projectRootPath: "/user/username/projects/myproject" }], session);
const project = session.getProjectService().inferredProjects[0];
session.logger.info(jsonToReadableText(project.getAllProjectErrors()));
baselineTsserverLogs("watchEnvironment", `inferred project watch options errors`, session);
});
});
});
@@ -469,7 +464,7 @@ describe("unittests:: tsserver:: watchEnvironment:: file names on case insensiti
content: `import { foo } from "bar"`,
};
const host = createServerHost([file, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([{ file, projectRootPath }], session);
baselineTsserverLogs("watchEnvironment", scenario, session);
});
@@ -496,7 +491,7 @@ describe("unittests:: tsserver:: watchEnvironment:: watchFile is single watcher
content: `import * as tsconfig from "./tsconfig.json";`,
};
const host = createServerHost([config, index, libFile]);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) });
const session = new TestSession(host);
openFilesForSession([index], session);
baselineTsserverLogs("watchEnvironment", "when watchFile is single watcher per file", session);
});
@@ -517,7 +512,7 @@ describe("unittests:: tsserver:: watchEnvironment:: watching at workspaces codes
content: `export function randomSeed(): string;`,
};
const host = createServerHost([config, main, randomSeed, libFile], { inodeWatching: true, runWithoutRecursiveWatches: true });
const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host), canUseEvents: true, noGetErrOnBackgroundUpdate: true });
const session = new TestSession(host);
openFilesForSession([main], session);
verifyGetErrRequest({ session, files: [main] });
// npm ci
+2 -25
View File
@@ -5,7 +5,6 @@ import {
combinePaths,
createGetCanonicalFileName,
Debug,
forEachAncestorDirectory,
getDirectoryPath,
MapLike,
normalizePath,
@@ -15,7 +14,6 @@ import {
version,
} from "./_namespaces/ts";
import {
ActionPackageInstalled,
Arguments,
EventTypesRegistry,
findArgument,
@@ -23,7 +21,6 @@ import {
InitializationFailedResponse,
InstallTypingHost,
nowString,
PackageInstalledResponse,
stringifyIndented,
TypesRegistryResponse,
TypingInstallerRequestUnion,
@@ -182,19 +179,7 @@ export class NodeTypingsInstaller extends TypingsInstaller {
break;
}
case "installPackage": {
const { fileName, packageName, projectName, projectRootPath } = req;
const cwd = getDirectoryOfPackageJson(fileName, this.installTypingHost) || projectRootPath;
if (cwd) {
this.installWorker(-1, [packageName], cwd, success => {
const message = success ? `Package ${packageName} installed.` : `There was an error installing ${packageName}.`;
const response: PackageInstalledResponse = { kind: ActionPackageInstalled, projectName, success, message };
this.sendResponse(response);
});
}
else {
const response: PackageInstalledResponse = { kind: ActionPackageInstalled, projectName, success: false, message: "Could not determine a project root path." };
this.sendResponse(response);
}
this.installPackage(req);
break;
}
default:
@@ -214,7 +199,7 @@ export class NodeTypingsInstaller extends TypingsInstaller {
protected installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(packageNames)}'.`);
this.log.writeLine(`#${requestId} with cwd: ${cwd} arguments: ${JSON.stringify(packageNames)}`);
}
const start = Date.now();
const hasError = installNpmPackages(this.npmPath, version, packageNames, command => this.execSyncAndLog(command, { cwd }));
@@ -244,14 +229,6 @@ export class NodeTypingsInstaller extends TypingsInstaller {
}
}
function getDirectoryOfPackageJson(fileName: string, host: InstallTypingHost): string | undefined {
return forEachAncestorDirectory(getDirectoryPath(fileName), directory => {
if (host.fileExists(combinePaths(directory, "package.json"))) {
return directory;
}
});
}
const logFilePath = findArgument(Arguments.LogFile);
const globalTypingsCacheLocation = findArgument(Arguments.GlobalCacheLocation);
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
+39 -1
View File
@@ -1,5 +1,6 @@
import {
combinePaths,
forEachAncestorDirectory,
forEachKey,
getBaseFileName,
getDirectoryPath,
@@ -18,6 +19,7 @@ import {
versionMajorMinor,
} from "./_namespaces/ts";
import {
ActionPackageInstalled,
ActionSet,
ActionWatchTypingLocations,
BeginInstallTypes,
@@ -26,8 +28,10 @@ import {
EndInstallTypes,
EventBeginInstallTypes,
EventEndInstallTypes,
InstallPackageRequest,
InstallTypingHost,
InvalidateCachedTypings,
PackageInstalledResponse,
SetTypings,
stringifyIndented,
WatchTypingLocations,
@@ -196,6 +200,39 @@ export abstract class TypingsInstaller {
}
}
/** @internal */
installPackage(req: InstallPackageRequest) {
const { fileName, packageName, projectName, projectRootPath } = req;
const cwd = forEachAncestorDirectory(getDirectoryPath(fileName), directory => {
if (this.installTypingHost.fileExists(combinePaths(directory, "package.json"))) {
return directory;
}
}) || projectRootPath;
if (cwd) {
this.installWorker(-1, [packageName], cwd, success => {
const message = success ?
`Package ${packageName} installed.` :
`There was an error installing ${packageName}.`;
const response: PackageInstalledResponse = {
kind: ActionPackageInstalled,
projectName,
success,
message,
};
this.sendResponse(response);
});
}
else {
const response: PackageInstalledResponse = {
kind: ActionPackageInstalled,
projectName,
success: false,
message: "Could not determine a project root path.",
};
this.sendResponse(response);
}
}
private initializeSafeList() {
// Prefer the safe list from the types map if it exists
if (this.typesMapLocation) {
@@ -455,7 +492,8 @@ export abstract class TypingsInstaller {
protected abstract installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void;
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | WatchTypingLocations): void;
/** @internal */
protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | WatchTypingLocations | PackageInstalledResponse): void;
protected readonly latestDistTag = "latest";
}

Some files were not shown because too many files have changed in this diff Show More