diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index 4a54dc8ea2a19..85bdddb0c8a08 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -110,12 +110,15 @@ import { } from "node:test"; import { fileURLToPath } from "node:url"; import { isSignatureDeclaration } from "../../src/ast/is.ts"; +import { areTestsFiltered } from "../testUtils.ts"; import { defaultFiles, spawnAPI, } from "./api.testUtils.ts"; -describe("API", () => { +const concurrency = areTestsFiltered(); + +describe("API", { concurrency }, () => { test("getCurrentLanguageServerSnapshot is LSP-only", () => { if (!!false) { const standalone = new API(); @@ -718,7 +721,7 @@ declare module "augmentation" {}`, }); // @sync-skip-block-start -describe("API - automatic batching", () => { +describe("API - automatic batching", { concurrency }, () => { test("initializes only once for concurrent first requests", async () => { await using api = spawnAPI(); const client = (api as unknown as { @@ -761,7 +764,7 @@ describe("API - automatic batching", () => { }); }); -describe("API - batchContext", () => { +describe("API - batchContext", { concurrency }, () => { test("transparently paginates batch responses", async () => { const api = spawnAPI({ ...defaultFiles }, { maxResponseBytesPerPage: 1 }); try { @@ -838,7 +841,7 @@ describe("API - batchContext", () => { }); // @sync-skip-block-end -describe("Checker - getImmediateAliasedSymbol", () => { +describe("Checker - getImmediateAliasedSymbol", { concurrency }, () => { test("resolves one level of alias indirection", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -857,7 +860,7 @@ describe("Checker - getImmediateAliasedSymbol", () => { }); }); -describe("Checker - getTargetSymbol", () => { +describe("Checker - getTargetSymbol", { concurrency }, () => { test("gets the target symbol of instantiated symbol", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -895,7 +898,7 @@ test(); }); }); -describe("Snapshot", () => { +describe("Snapshot", { concurrency }, () => { test("createSnapshot returns snapshot with projects", async () => { await using api = spawnAPI(); @@ -1049,7 +1052,7 @@ describe("Snapshot", () => { }); }); -describe("LanguageService - imports", () => { +describe("LanguageService - imports", { concurrency }, () => { test("getImportEditsForSymbols adds a named import", async () => { const source = `const value = foo;\n`; await using api = spawnAPI({ @@ -1150,7 +1153,7 @@ describe("LanguageService - imports", () => { }); }); -describe("LanguageService - getCompletionsAtPosition", () => { +describe("LanguageService - getCompletionsAtPosition", { concurrency }, () => { test("returns member completions after a dot", async () => { const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; await using api = spawnAPI({ @@ -1217,7 +1220,7 @@ describe("LanguageService - getCompletionsAtPosition", () => { }); }); -describe("LanguageService - getReferencedSymbolsForNode", () => { +describe("LanguageService - getReferencedSymbolsForNode", { concurrency }, () => { test("getReferencedSymbolsForNode", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -1239,7 +1242,7 @@ describe("LanguageService - getReferencedSymbolsForNode", () => { }); }); -describe("LanguageService - getSignatureUsage", () => { +describe("LanguageService - getSignatureUsage", { concurrency }, () => { test("getSignatureUsage", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -1259,7 +1262,7 @@ describe("LanguageService - getSignatureUsage", () => { }); }); -describe("Checker - getApparentType", () => { +describe("Checker - getApparentType", { concurrency }, () => { test("returns the apparent type of a literal type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1282,7 +1285,7 @@ describe("Checker - getApparentType", () => { }); }); -describe("Checker - getReducedType", () => { +describe("Checker - getReducedType", { concurrency }, () => { test("returns the reduced type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1319,7 +1322,7 @@ export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, }); }); -describe("Checker - getMemberInModuleExports", () => { +describe("Checker - getMemberInModuleExports", { concurrency }, () => { test("returns a named export when present", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1340,7 +1343,7 @@ describe("Checker - getMemberInModuleExports", () => { }); }); -describe("SourceFile", () => { +describe("SourceFile", { concurrency }, () => { test("getSourceFile rejects invalid document identifiers", async () => { await using api = spawnAPI(); @@ -1530,7 +1533,7 @@ describe("SourceFile", () => { }); }); -describe("NodeArray", () => { +describe("NodeArray", { concurrency }, () => { test("hasTrailingComma", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1549,98 +1552,100 @@ describe("NodeArray", () => { }); }); -test("unicode escapes", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/1.ts": `"😃"`, - "/src/2.ts": `"\\ud83d\\ude03"`, - "/src/3.ts": `"\\ud800a\\udc00"`, +describe("API objects", { concurrency }, () => { + test("unicode escapes", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/1.ts": `"😃"`, + "/src/2.ts": `"\\ud83d\\ude03"`, + "/src/3.ts": `"\\ud800a\\udc00"`, + }); + + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const expectedTexts = new Map([ + ["/src/1.ts", "😃"], + ["/src/2.ts", "😃"], + ["/src/3.ts", "\ud800a\udc00"], + ]); + + for (const file of expectedTexts.keys()) { + const sourceFile = await project.program.getSourceFile(file); + assert.ok(sourceFile); + + sourceFile.forEachChild(function visit(node) { + if (isStringLiteral(node)) { + assert.equal(node.text, expectedTexts.get(file)); + } + node.forEachChild(visit); + }); + } }); - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const expectedTexts = new Map([ - ["/src/1.ts", "😃"], - ["/src/2.ts", "😃"], - ["/src/3.ts", "\ud800a\udc00"], - ]); + test("template unicode escapes", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": "`\\ud800${0}\\udc00`", + }); - for (const file of expectedTexts.keys()) { - const sourceFile = await project.program.getSourceFile(file); + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); assert.ok(sourceFile); + let sawHead = false; + let sawTail = false; sourceFile.forEachChild(function visit(node) { - if (isStringLiteral(node)) { - assert.equal(node.text, expectedTexts.get(file)); + if (isTemplateHead(node)) { + assert.equal(node.text, "\ud800"); + sawHead = true; + } + else if (isTemplateTail(node)) { + assert.equal(node.text, "\udc00"); + sawTail = true; } node.forEachChild(visit); }); - } -}); - -test("template unicode escapes", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": "`\\ud800${0}\\udc00`", + assert.ok(sawHead); + assert.ok(sawTail); }); - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); + test("Object equality", async () => { + await using api = spawnAPI(); - let sawHead = false; - let sawTail = false; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "\ud800"); - sawHead = true; - } - else if (isTemplateTail(node)) { - assert.equal(node.text, "\udc00"); - sawTail = true; - } - node.forEachChild(visit); + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + // Same symbol returned from same snapshot's checker + assert.strictEqual( + await project.checker.getSymbolAtPosition("/src/index.ts", 9), + await project.checker.getSymbolAtPosition("/src/index.ts", 10), + ); }); - assert.ok(sawHead); - assert.ok(sawTail); -}); -test("Object equality", async () => { - await using api = spawnAPI(); - - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - // Same symbol returned from same snapshot's checker - assert.strictEqual( - await project.checker.getSymbolAtPosition("/src/index.ts", 9), - await project.checker.getSymbolAtPosition("/src/index.ts", 10), - ); -}); - -test("Snapshot dispose", async () => { - await using api = spawnAPI(); + test("Snapshot dispose", async () => { + await using api = spawnAPI(); - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); - // Snapshot dispose should release server-side resources - assert.ok(snapshot.isDisposed() === false); - await snapshot.dispose(); - assert.ok(snapshot.isDisposed() === true); + // Snapshot dispose should release server-side resources + assert.ok(snapshot.isDisposed() === false); + await snapshot.dispose(); + assert.ok(snapshot.isDisposed() === true); - // After dispose, snapshot methods should throw - assert.throws(() => { - snapshot.getConfiguredProject("/tsconfig.json"); - }, { - name: "Error", - message: "Snapshot is disposed", + // After dispose, snapshot methods should throw + assert.throws(() => { + snapshot.getConfiguredProject("/tsconfig.json"); + }, { + name: "Error", + message: "Snapshot is disposed", + }); }); }); -describe("Multiple snapshots", () => { +describe("Multiple snapshots", { concurrency }, () => { test("two snapshots work independently", async () => { await using api = spawnAPI(); @@ -1846,7 +1851,7 @@ describe("Multiple snapshots", () => { }); }); -describe("Source file caching", () => { +describe("Source file caching", { concurrency }, () => { test("same file from same snapshot returns cached object", async () => { await using api = spawnAPI(); @@ -2014,7 +2019,7 @@ describe("Source file caching", () => { }); }); -describe("Snapshot disposal", () => { +describe("Snapshot disposal", { concurrency }, () => { test("dispose is idempotent", async () => { await using api = spawnAPI(); @@ -2053,7 +2058,7 @@ describe("Snapshot disposal", () => { }); }); -describe("Source file cache keying across projects", () => { +describe("Source file cache keying across projects", { concurrency }, () => { // Three projects share the same file (/src/shared.ts). // The file sits inside a package.json scope with "type": "module". // @@ -2132,7 +2137,7 @@ describe("Source file cache keying across projects", () => { }); }); -describe("Checker - symbol identity across projects", () => { +describe("Checker - symbol identity across projects", { concurrency }, () => { const sharedSymbolFiles = { "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), @@ -2186,7 +2191,7 @@ describe("Checker - symbol identity across projects", () => { }); }); -describe("Checker - types and signatures", () => { +describe("Checker - types and signatures", { concurrency }, () => { const checkerFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -2614,7 +2619,7 @@ export class Cache { }); }); -describe("Symbol - parent, members, exports", () => { +describe("Symbol - parent, members, exports", { concurrency }, () => { const symbolFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/mod.ts": ` @@ -2685,7 +2690,7 @@ export const value = 1; }); }); -describe("Type - getSymbol", () => { +describe("Type - getSymbol", { concurrency }, () => { test("getSymbol returns the symbol of a type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -2711,7 +2716,7 @@ export const instance: Foo = new Foo(); }); }); -describe("Type - sub-property fetchers", () => { +describe("Type - sub-property fetchers", { concurrency }, () => { const typeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), "/src/types.ts": ` @@ -3049,7 +3054,7 @@ export function gh1449(a: T): T { }); }); -describe("Checker - intrinsic type getters", () => { +describe("Checker - intrinsic type getters", { concurrency }, () => { const intrinsicFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": `export const x = 1;`, @@ -3176,7 +3181,7 @@ describe("Checker - intrinsic type getters", () => { }); }); -describe("Checker - multi-project type ID uniqueness", () => { +describe("Checker - multi-project type ID uniqueness", { concurrency }, () => { test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", async () => { await using api = spawnAPI({ "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3282,7 +3287,7 @@ describe("Checker - multi-project type ID uniqueness", () => { }); }); -describe("Checker - getBaseTypeOfLiteralType", () => { +describe("Checker - getBaseTypeOfLiteralType", { concurrency }, () => { test("number literal widens to number", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3324,7 +3329,7 @@ describe("Checker - getBaseTypeOfLiteralType", () => { }); }); -describe("Checker - getContextualType", () => { +describe("Checker - getContextualType", { concurrency }, () => { test("contextual type from function parameter", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3359,7 +3364,7 @@ foo(42); }); }); -describe("Checker - getTypeOfSymbolAtLocation", () => { +describe("Checker - getTypeOfSymbolAtLocation", { concurrency }, () => { test("narrowed type via typeof check", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3414,7 +3419,7 @@ export function check(x: string | number) { }); }); -describe("Checker - getShorthandAssignmentValueSymbol", () => { +describe("Checker - getShorthandAssignmentValueSymbol", { concurrency }, () => { test("shorthand property symbol resolves to variable", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3446,7 +3451,7 @@ export const obj = { name }; }); }); -describe("readFile callback semantics", () => { +describe("readFile callback semantics", { concurrency }, () => { test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", async () => { const virtualFiles: Record = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3496,7 +3501,7 @@ describe("readFile callback semantics", () => { }); }); -describe("updateSnapshot file systems", () => { +describe("updateSnapshot file systems", { concurrency }, () => { test("request filesystem factories normalize files and preserve explicit listings", () => { const memory = createFileSystem([ ["/src/index.ts", "posix"], @@ -4094,7 +4099,7 @@ describe("updateSnapshot file systems", () => { // do not model modification times. }); -describe("Checker - isArrayType / isTupleType", () => { +describe("Checker - isArrayType / isTupleType", { concurrency }, () => { test("number[] is array, not tuple", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4204,7 +4209,7 @@ describe("Checker - isArrayType / isTupleType", () => { }); }); -describe("Checker - isReadonlySymbol", () => { +describe("Checker - isReadonlySymbol", { concurrency }, () => { test("properties with a 'readonly' modifier", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4295,7 +4300,7 @@ export type B = InstanceType; }); }); -describe("Checker - getReturnTypeOfSignature", () => { +describe("Checker - getReturnTypeOfSignature", { concurrency }, () => { test("returns the return type of a function signature", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4318,7 +4323,7 @@ describe("Checker - getReturnTypeOfSignature", () => { }); }); -describe("Checker - getRestTypeOfSignature", () => { +describe("Checker - getRestTypeOfSignature", { concurrency }, () => { test("returns the rest type of a signature with rest parameter", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4341,7 +4346,7 @@ describe("Checker - getRestTypeOfSignature", () => { }); }); -describe("Checker - getTypePredicateOfSignature", () => { +describe("Checker - getTypePredicateOfSignature", { concurrency }, () => { test("returns type predicate for 'x is T' guard", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4441,7 +4446,7 @@ export class Dog extends Animal { }); }); -describe("Checker - getBaseTypes", () => { +describe("Checker - getBaseTypes", { concurrency }, () => { test("returns base types of a class", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4524,7 +4529,7 @@ export type BoxOfString = Box; }); }); -describe("Type - getBaseTypes", () => { +describe("Type - getBaseTypes", { concurrency }, () => { test("returns base types for a class type and undefined for a non-class/interface", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4562,7 +4567,7 @@ export const n: number = 0; }); }); -describe("Type - isErrorType", () => { +describe("Type - isErrorType", { concurrency }, () => { test("identifies the error type from an unresolvable annotation", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4592,7 +4597,7 @@ declare const bad: ThisTypeDoesNotExist; }); }); -describe("Checker - well-known symbols", () => { +describe("Checker - well-known symbols", { concurrency }, () => { test("isUnknownSymbol identifies the aliased unknown symbol", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4614,7 +4619,7 @@ export type Alias = typeof value; }); }); -describe("Checker - well-known signatures", () => { +describe("Checker - well-known signatures", { concurrency }, () => { test("isUnknownSignature identifies an unresolvable call", async () => { const src = ` const ok = (x: number) => x; @@ -4648,7 +4653,7 @@ notCallable(); }); }); -describe("Symbol - escaped names and tables", () => { +describe("Symbol - escaped names and tables", { concurrency }, () => { test("getExports/getMembers return a cached Map keyed by escaped name", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4747,7 +4752,7 @@ export { x as '${maliciousName}' }; }); }); -describe("ast - escapeLeadingUnderscores", () => { +describe("ast - escapeLeadingUnderscores", { concurrency }, () => { test("round-trips display and escaped names", () => { assert.equal(escapeLeadingUnderscores("foo"), "foo"); assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); @@ -4758,7 +4763,7 @@ describe("ast - escapeLeadingUnderscores", () => { }); }); -describe("ast - tryGetAmbientModuleNameFromSymbolName", () => { +describe("ast - tryGetAmbientModuleNameFromSymbolName", { concurrency }, () => { test("gets ambient module names from escaped symbol names", () => { assert.equal(tryGetAmbientModuleNameFromSymbolName('"pkg"' as __String), "pkg"); assert.equal(tryGetAmbientModuleNameFromSymbolName('__"*.css"pattern@1234' as __String), "*.css"); @@ -4768,7 +4773,7 @@ describe("ast - tryGetAmbientModuleNameFromSymbolName", () => { }); }); -describe("ast - getJSDocTags", () => { +describe("ast - getJSDocTags", { concurrency }, () => { test("returns a node's own tags, and inherited @param / @template tags", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4910,7 +4915,7 @@ const cast = /** @type {number} */ (someValue); }); }); -describe("Checker - getPropertiesOfType", () => { +describe("Checker - getPropertiesOfType", { concurrency }, () => { test("returns properties of an object type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4941,7 +4946,7 @@ export declare const p: Person; }); }); -describe("Checker - getIndexInfosOfType", () => { +describe("Checker - getIndexInfosOfType", { concurrency }, () => { test("returns index signatures of an indexed type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4996,7 +5001,7 @@ export declare const m: ReadonlyMap; }); }); -describe("Checker - TypeScript API parity", () => { +describe("Checker - TypeScript API parity", { concurrency }, () => { const files = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -5085,7 +5090,7 @@ export type Exported = number; }); }); -describe("Checker - getConstraintOfTypeParameter", () => { +describe("Checker - getConstraintOfTypeParameter", { concurrency }, () => { test("returns constraint of a type parameter", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5110,7 +5115,7 @@ describe("Checker - getConstraintOfTypeParameter", () => { }); }); -describe("Checker - TypeParameter getters", () => { +describe("Checker - TypeParameter getters", { concurrency }, () => { test("getConstraint() and getDefault() return the constraint and default types", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5165,7 +5170,7 @@ describe("Checker - TypeParameter getters", () => { }); }); -describe("Checker - getTypeArguments", () => { +describe("Checker - getTypeArguments", { concurrency }, () => { test("returns type arguments of a generic instantiation", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5214,7 +5219,7 @@ describe("Checker - getTypeArguments", () => { }); }); -describe("Checker - getBaseConstraintOfType", () => { +describe("Checker - getBaseConstraintOfType", { concurrency }, () => { test("returns the base constraint of a type parameter", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5255,7 +5260,7 @@ describe("Checker - getBaseConstraintOfType", () => { }); }); -describe("Checker - getPropertyOfType", () => { +describe("Checker - getPropertyOfType", { concurrency }, () => { test("returns a named property symbol of a type", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5284,7 +5289,7 @@ export declare const p: Person; }); }); -describe("Checker - getConstantValue", () => { +describe("Checker - getConstantValue", { concurrency }, () => { test("returns numeric value of an enum member", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5363,7 +5368,7 @@ describe("Checker - getConstantValue", () => { }); }); -describe("Checker - getSignatureFromDeclaration", () => { +describe("Checker - getSignatureFromDeclaration", { concurrency }, () => { test("returns the signature of a function declaration", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5389,7 +5394,7 @@ describe("Checker - getSignatureFromDeclaration", () => { }); }); -describe("Checker - getExportSpecifierLocalTargetSymbol", () => { +describe("Checker - getExportSpecifierLocalTargetSymbol", { concurrency }, () => { test("resolves the local target of an export specifier", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5415,7 +5420,7 @@ export { value as renamed }; }); }); -describe("Checker - getAliasedSymbol", () => { +describe("Checker - getAliasedSymbol", { concurrency }, () => { test("resolves an import alias to its target symbol", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5436,7 +5441,7 @@ describe("Checker - getAliasedSymbol", () => { }); }); -describe("Checker - getFullyQualifiedName", () => { +describe("Checker - getFullyQualifiedName", { concurrency }, () => { test("returns module-qualified names for exported symbols and dotted names for members", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5466,7 +5471,7 @@ export class Standalone {} }); }); -describe("Checker - getExportsOfModule", () => { +describe("Checker - getExportsOfModule", { concurrency }, () => { test("returns all exports including re-exports via 'export *'", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5490,7 +5495,7 @@ export * from "./inner"; }); }); -describe("Checker - getSymbolsInScope", () => { +describe("Checker - getSymbolsInScope", { concurrency }, () => { const scopeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -5551,7 +5556,7 @@ function f() { }); }); -describe("Symbol - getDocumentationComment and getJsDocTags", () => { +describe("Symbol - getDocumentationComment and getJsDocTags", { concurrency }, () => { const docFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -5595,7 +5600,7 @@ export function add(a: number, b: number): number { return a + b; } }); }); -describe("TypeParameter - isThisType", () => { +describe("TypeParameter - isThisType", { concurrency }, () => { test("isThisType is true for the polymorphic 'this' type in a class method", async () => { const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; await using api = spawnAPI({ @@ -5635,7 +5640,7 @@ describe("TypeParameter - isThisType", () => { }); }); -describe("Type - getAliasTypeArguments", () => { +describe("Type - getAliasTypeArguments", { concurrency }, () => { test("returns the type arguments of a single-param generic type alias", async () => { const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; await using api = spawnAPI({ @@ -5694,7 +5699,7 @@ describe("Type - getAliasTypeArguments", () => { }); }); -describe("Type - getAliasSymbol", () => { +describe("Type - getAliasSymbol", { concurrency }, () => { test("returns the symbol for a non-generic type alias", async () => { // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; @@ -5753,7 +5758,7 @@ describe("Type - getAliasSymbol", () => { }); }); -describe("IntrinsicType - intrinsicName", () => { +describe("IntrinsicType - intrinsicName", { concurrency }, () => { test("intrinsicName matches the primitive type name", async () => { const src = `\nexport const x: string = "hello";\n`; await using api = spawnAPI({ @@ -5779,7 +5784,7 @@ describe("IntrinsicType - intrinsicName", () => { }); }); -describe("FreshableType - getFreshType and getRegularType", () => { +describe("FreshableType - getFreshType and getRegularType", { concurrency }, () => { test("LiteralType.value is empty string for the empty-string literal type", async () => { const src = `\nexport const empty: "" = "";\n`; await using api = spawnAPI({ @@ -5962,7 +5967,7 @@ describe("FreshableType - getFreshType and getRegularType", () => { }); }); -describe("Checker - isContextSensitive", () => { +describe("Checker - isContextSensitive", { concurrency }, () => { test("arrow function with no type annotation is context sensitive", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5987,7 +5992,7 @@ describe("Checker - isContextSensitive", () => { }); }); -describe("Checker - isTypeAssignableTo", () => { +describe("Checker - isTypeAssignableTo", { concurrency }, () => { test("returns true when source is assignable to target", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6040,7 +6045,7 @@ describe("Checker - isTypeAssignableTo", () => { }); }); -describe("Printer", () => { +describe("Printer", { concurrency }, () => { const emitterFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -6283,7 +6288,7 @@ export const obj = { m: 1, s: "hi", b: true }; }); }); -describe("Program - selected file emit", () => { +describe("Program - selected file emit", { concurrency }, () => { const files = { "/tsconfig.json": JSON.stringify({ compilerOptions: { @@ -6345,7 +6350,7 @@ describe("Program - selected file emit", () => { }); }); -describe("SnapshotInternalAPI - formatNodeForInsertion", () => { +describe("SnapshotInternalAPI - formatNodeForInsertion", { concurrency }, () => { test("formats a synthesized statement with correct indentation for insertion inside a function body", async () => { const files = { "/tsconfig.json": "{}", @@ -6423,7 +6428,7 @@ describe("SnapshotInternalAPI - formatNodeForInsertion", () => { }); }); -describe("modifierFlags", () => { +describe("modifierFlags", { concurrency }, () => { test("export async function has Export | Async flags", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6471,7 +6476,7 @@ describe("modifierFlags", () => { }); }); -describe("Checker - getResolvedSymbol", () => { +describe("Checker - getResolvedSymbol", { concurrency }, () => { test("resolves variable reference to its declaration symbol", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6500,7 +6505,7 @@ describe("Checker - getResolvedSymbol", () => { }); }); -describe("VariableDeclarationList - BlockScoped flags", () => { +describe("VariableDeclarationList - BlockScoped flags", { concurrency }, () => { test("let declaration has Let flag", async () => { await using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6546,161 +6551,163 @@ describe("VariableDeclarationList - BlockScoped flags", () => { }); }); -test("TypeOperator operator kind", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function test(arg: readonly number[]) { }\n`, - }); - - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; - assert(param); - const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; - assert(type); - assert.equal(type.kind, SyntaxKind.TypeOperator); - assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); - const printed = await api.printer.printFile(sourceFile); - assert.equal(sourceFile.text, printed); -}); - -test("SpreadAssignment roundtrip", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `var thing = { ...other };\n`, - }); - - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; - const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; - assert(assignment); - assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); - const expr = assignment.expression; - assert(expr); - assert.equal(expr.kind, SyntaxKind.Identifier); - const printed = await api.printer.printFile(sourceFile); - assert.equal(sourceFile.text, printed); -}); +describe("AST roundtrips", { concurrency }, () => { + test("TypeOperator operator kind", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function test(arg: readonly number[]) { }\n`, + }); -test("VariableDeclarationList const flag clone", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const thing = 123;\n`, + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; + assert(param); + const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; + assert(type); + assert.equal(type.kind, SyntaxKind.TypeOperator); + assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); + const printed = await api.printer.printFile(sourceFile); + assert.equal(sourceFile.text, printed); }); - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - { + test("SpreadAssignment roundtrip", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `var thing = { ...other };\n`, + }); + + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const cloned = getSynthesizedDeepClone(sourceFile); - { - const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const printed = await api.printer.printFile(cloned); - assert.equal(sourceFile.text, printed); -}); + const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; + const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; + assert(assignment); + assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); + const expr = assignment.expression; + assert(expr); + assert.equal(expr.kind, SyntaxKind.Identifier); + const printed = await api.printer.printFile(sourceFile); + assert.equal(sourceFile.text, printed); + }); -test("JSDoc before ExpressionStatement allowed", async () => { - await using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": ` + test("VariableDeclarationList const flag clone", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const thing = 123;\n`, + }); + + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + { + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const cloned = getSynthesizedDeepClone(sourceFile); + { + const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const printed = await api.printer.printFile(cloned); + assert.equal(sourceFile.text, printed); + }); + + test("JSDoc before ExpressionStatement allowed", async () => { + await using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": ` /** * A doc. */ doThing(); `, - }); - - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const printed = await api.printer.printFile(sourceFile); - assert.equal(sourceFile.text.trim(), printed.trim()); -}); + }); -test("Factory ModifierList auto-conversion", async () => { - await using api = spawnAPI(); + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const printed = await api.printer.printFile(sourceFile); + assert.equal(sourceFile.text.trim(), printed.trim()); + }); - const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const node = createTypeAliasDeclaration( - [createToken(SyntaxKind.ExportKeyword)], - createIdentifier("Test"), - undefined, - createKeywordTypeNode(SyntaxKind.AnyKeyword), - ); + test("Factory ModifierList auto-conversion", async () => { + await using api = spawnAPI(); - assert.equal(await api.printer.printNode(node), "export type Test = any;"); + const snapshot = await api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const node = createTypeAliasDeclaration( + [createToken(SyntaxKind.ExportKeyword)], + createIdentifier("Test"), + undefined, + createKeywordTypeNode(SyntaxKind.AnyKeyword), + ); - const cloned = getSynthesizedDeepClone(node); - assert.equal(await api.printer.printNode(cloned), "export type Test = any;"); -}); + assert.equal(await api.printer.printNode(node), "export type Test = any;"); -test("Parse-clone-emit roundtrip", async () => { - const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); - await using api = new API({ - cwd: tsSource, + const cloned = getSynthesizedDeepClone(node); + assert.equal(await api.printer.printNode(cloned), "export type Test = any;"); }); - const target = { - cloneCrashed: 0, - printCrashed: 0, - clonePrintCrashed: 0, - }; - const errors = { ...target }; - - for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { - const snapshot = await api.createSnapshot({ openProject: resolve(tsSource, tsconfig) }); - const project = snapshot.getConfiguredProject(tsconfig); - assert(project); - for (const file of project.rootFiles) { - const source = await project.program.getSourceFile(file); - assert(source); - let clone: typeof source; - - try { - await api.printer.printNode(source); - } - catch { - errors.printCrashed++; - continue; - } - try { - clone = getSynthesizedDeepClone(source); - } - catch { - errors.cloneCrashed++; - continue; - } + test("Parse-clone-emit roundtrip", async () => { + const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); + await using api = new API({ + cwd: tsSource, + }); + const target = { + cloneCrashed: 0, + printCrashed: 0, + clonePrintCrashed: 0, + }; + const errors = { ...target }; + + for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { + const snapshot = await api.createSnapshot({ openProject: resolve(tsSource, tsconfig) }); + const project = snapshot.getConfiguredProject(tsconfig); + assert(project); + for (const file of project.rootFiles) { + const source = await project.program.getSourceFile(file); + assert(source); + let clone: typeof source; + + try { + await api.printer.printNode(source); + } + catch { + errors.printCrashed++; + continue; + } - try { - await api.printer.printNode(clone); - } - catch { - errors.clonePrintCrashed++; - continue; + try { + clone = getSynthesizedDeepClone(source); + } + catch { + errors.cloneCrashed++; + continue; + } + + try { + await api.printer.printNode(clone); + } + catch { + errors.clonePrintCrashed++; + continue; + } } } - } - assert.deepEqual(errors, target); + assert.deepEqual(errors, target); + }); }); -describe("Program - diagnostics", () => { +describe("Program - diagnostics", { concurrency }, () => { test("getSyntacticDiagnostics", async () => { const source = `const x: = 1;`; await using api = spawnAPI({ @@ -7030,7 +7037,7 @@ describe("Program - diagnostics", () => { }); }); -describe("getDefaultProjectForFile", () => { +describe("getDefaultProjectForFile", { concurrency }, () => { test("snapshot opens reject unreadable virtual files without panicking", async () => { const fileName = "/src/App.vue.ts"; const source = `export const component = 1;`; @@ -7212,7 +7219,7 @@ describe("getDefaultProjectForFile", () => { }); }); -describe("Program - emit", () => { +describe("Program - emit", { concurrency }, () => { const files = { "/tsconfig.json": `{ "compilerOptions": { @@ -7472,7 +7479,7 @@ describe("Program - emit", () => { // @sync-skip-block-end }); -describe("Timing", () => { +describe("Timing", { concurrency }, () => { test("collects combined client, server, and transport timing info", async () => { await using api = new API({ cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), @@ -7608,7 +7615,7 @@ describe("Timing", () => { }); }); -describe("runWithTemporaryFileUpdate", () => { +describe("runWithTemporaryFileUpdate", { concurrency }, () => { test("temporary file update is visible in the callback and reverted afterward", async () => { await using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), diff --git a/packages/typescript/test/async/astnav.test.ts b/packages/typescript/test/async/astnav.test.ts index 0200611cd8727..f551056711dc0 100644 --- a/packages/typescript/test/async/astnav.test.ts +++ b/packages/typescript/test/async/astnav.test.ts @@ -24,6 +24,7 @@ import { test, } from "node:test"; import { fileURLToPath } from "node:url"; +import { areTestsFiltered } from "../testUtils.ts"; // --------------------------------------------------------------------------- // Go JSON baseline format @@ -76,7 +77,7 @@ const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav") // Tests // --------------------------------------------------------------------------- -describe("astnav", () => { +describe("astnav", { concurrency: areTestsFiltered() }, () => { let fileText: string; try { diff --git a/packages/typescript/test/diagnosticFormatter.test.ts b/packages/typescript/test/diagnosticFormatter.test.ts index 4cefa0b845f8b..8c4b62651052d 100644 --- a/packages/typescript/test/diagnosticFormatter.test.ts +++ b/packages/typescript/test/diagnosticFormatter.test.ts @@ -9,8 +9,9 @@ import { describe, test, } from "node:test"; +import { areTestsFiltered } from "./testUtils.ts"; -describe("diagnosticFormatter", () => { +describe("diagnosticFormatter", { concurrency: areTestsFiltered() }, () => { test("formats diagnostics with a configured program host", async () => { const source = `const x: number = "oops";\n`; const api = spawnAPI({ diff --git a/packages/typescript/test/encoder.test.ts b/packages/typescript/test/encoder.test.ts index ec90992619a17..7570d240300a4 100644 --- a/packages/typescript/test/encoder.test.ts +++ b/packages/typescript/test/encoder.test.ts @@ -47,6 +47,9 @@ import { NODE_OFFSET_DATA, } from "../src/api/node/protocol.ts"; import { Wtf8Decoder } from "../src/api/node/wtf8.ts"; +import { areTestsFiltered } from "./testUtils.ts"; + +const concurrency = areTestsFiltered(); function makeSF(text: string, fileName: string, statements: readonly Statement[]): SourceFile { const endOfFileToken = createToken(SyntaxKind.EndOfFile); @@ -57,7 +60,7 @@ function decode(data: Uint8Array): RemoteSourceFile { return new RemoteSourceFile(data, new Wtf8Decoder()); } -describe("Encoder", () => { +describe("Encoder", { concurrency }, () => { test("encodes empty source file", () => { const sf = makeSF("", "/test.ts", []); @@ -348,7 +351,7 @@ describe("Encoder", () => { }); }); -describe("UTF-8 vs UTF-16 position encoding", () => { +describe("UTF-8 vs UTF-16 position encoding", { concurrency }, () => { // Positions in the encoded AST must be UTF-16 code unit offsets so that // file.text.slice(node.pos, node.end) works correctly on JS strings. // This is the same convention TypeScript uses. @@ -409,7 +412,7 @@ describe("UTF-8 vs UTF-16 position encoding", () => { }); }); -describe("Line and character mapping", () => { +describe("Line and character mapping", { concurrency }, () => { function makeSourceFile(text: string): RemoteSourceFile { return decode(encodeSourceFile(makeSF(text, "/test.ts", []))); } diff --git a/packages/typescript/test/spanMap.test.ts b/packages/typescript/test/spanMap.test.ts index c0dc8314185c7..29e80e7a87bd6 100644 --- a/packages/typescript/test/spanMap.test.ts +++ b/packages/typescript/test/spanMap.test.ts @@ -9,8 +9,9 @@ import { describe, test, } from "node:test"; +import { areTestsFiltered } from "./testUtils.ts"; -describe("SpanMap", () => { +describe("SpanMap", { concurrency: areTestsFiltered() }, () => { const map = new SpanMap([ { virtualStart: 2, virtualEnd: 6, originalStart: 10, originalEnd: 14, kind: SpanMapKind.Verbatim }, { virtualStart: 8, virtualEnd: 11, originalStart: 20, originalEnd: 27, kind: SpanMapKind.Atom }, diff --git a/packages/typescript/test/sync/api-generators.test.ts b/packages/typescript/test/sync/api-generators.test.ts index f9bacf9a96206..5eccff4568fa0 100644 --- a/packages/typescript/test/sync/api-generators.test.ts +++ b/packages/typescript/test/sync/api-generators.test.ts @@ -67,6 +67,7 @@ import { type APIRequestGenerator, executeRequestGenerators, } from "../../src/api/sync/generatorSupport.ts"; +import { areTestsFiltered } from "../testUtils.ts"; import { spawnAPI } from "./api.testUtils.ts"; const parityFiles = { @@ -415,7 +416,7 @@ function assertPublicGeneratorCoverage(owners: readonly { readonly name: string; assert.deepEqual(missing, [], `Uncovered public generator getters: ${missing.join(", ")}`); } -describe("API - generator batching", () => { +describe("API - generator batching", { concurrency: areTestsFiltered() }, () => { test("batches source file requests", context => { const api = spawnAPI(parityFiles); context.after(() => api.close()); diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index 969c2885ceb35..3817add5a0a0d 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -118,12 +118,15 @@ import { } from "node:test"; import { fileURLToPath } from "node:url"; import { isSignatureDeclaration } from "../../src/ast/is.ts"; +import { areTestsFiltered } from "../testUtils.ts"; import { defaultFiles, spawnAPI, } from "./api.testUtils.ts"; -describe("API", () => { +const concurrency = areTestsFiltered(); + +describe("API", { concurrency }, () => { test("getCurrentLanguageServerSnapshot is LSP-only", () => { if (!!false) { const standalone = new API(); @@ -706,7 +709,7 @@ declare module "augmentation" {}`, }); }); -describe("Checker - getImmediateAliasedSymbol", () => { +describe("Checker - getImmediateAliasedSymbol", { concurrency }, () => { test("resolves one level of alias indirection", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -725,7 +728,7 @@ describe("Checker - getImmediateAliasedSymbol", () => { }); }); -describe("Checker - getTargetSymbol", () => { +describe("Checker - getTargetSymbol", { concurrency }, () => { test("gets the target symbol of instantiated symbol", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -763,7 +766,7 @@ test(); }); }); -describe("Snapshot", () => { +describe("Snapshot", { concurrency }, () => { test("createSnapshot returns snapshot with projects", () => { using api = spawnAPI(); @@ -917,7 +920,7 @@ describe("Snapshot", () => { }); }); -describe("LanguageService - imports", () => { +describe("LanguageService - imports", { concurrency }, () => { test("getImportEditsForSymbols adds a named import", () => { const source = `const value = foo;\n`; using api = spawnAPI({ @@ -1018,7 +1021,7 @@ describe("LanguageService - imports", () => { }); }); -describe("LanguageService - getCompletionsAtPosition", () => { +describe("LanguageService - getCompletionsAtPosition", { concurrency }, () => { test("returns member completions after a dot", () => { const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; using api = spawnAPI({ @@ -1085,7 +1088,7 @@ describe("LanguageService - getCompletionsAtPosition", () => { }); }); -describe("LanguageService - getReferencedSymbolsForNode", () => { +describe("LanguageService - getReferencedSymbolsForNode", { concurrency }, () => { test("getReferencedSymbolsForNode", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -1107,7 +1110,7 @@ describe("LanguageService - getReferencedSymbolsForNode", () => { }); }); -describe("LanguageService - getSignatureUsage", () => { +describe("LanguageService - getSignatureUsage", { concurrency }, () => { test("getSignatureUsage", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -1127,7 +1130,7 @@ describe("LanguageService - getSignatureUsage", () => { }); }); -describe("Checker - getApparentType", () => { +describe("Checker - getApparentType", { concurrency }, () => { test("returns the apparent type of a literal type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1150,7 +1153,7 @@ describe("Checker - getApparentType", () => { }); }); -describe("Checker - getReducedType", () => { +describe("Checker - getReducedType", { concurrency }, () => { test("returns the reduced type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1187,7 +1190,7 @@ export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, }); }); -describe("Checker - getMemberInModuleExports", () => { +describe("Checker - getMemberInModuleExports", { concurrency }, () => { test("returns a named export when present", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1208,7 +1211,7 @@ describe("Checker - getMemberInModuleExports", () => { }); }); -describe("SourceFile", () => { +describe("SourceFile", { concurrency }, () => { test("getSourceFile rejects invalid document identifiers", () => { using api = spawnAPI(); @@ -1398,7 +1401,7 @@ describe("SourceFile", () => { }); }); -describe("NodeArray", () => { +describe("NodeArray", { concurrency }, () => { test("hasTrailingComma", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -1417,98 +1420,100 @@ describe("NodeArray", () => { }); }); -test("unicode escapes", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/1.ts": `"😃"`, - "/src/2.ts": `"\\ud83d\\ude03"`, - "/src/3.ts": `"\\ud800a\\udc00"`, +describe("API objects", { concurrency }, () => { + test("unicode escapes", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/1.ts": `"😃"`, + "/src/2.ts": `"\\ud83d\\ude03"`, + "/src/3.ts": `"\\ud800a\\udc00"`, + }); + + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const expectedTexts = new Map([ + ["/src/1.ts", "😃"], + ["/src/2.ts", "😃"], + ["/src/3.ts", "\ud800a\udc00"], + ]); + + for (const file of expectedTexts.keys()) { + const sourceFile = project.program.getSourceFile(file); + assert.ok(sourceFile); + + sourceFile.forEachChild(function visit(node) { + if (isStringLiteral(node)) { + assert.equal(node.text, expectedTexts.get(file)); + } + node.forEachChild(visit); + }); + } }); - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const expectedTexts = new Map([ - ["/src/1.ts", "😃"], - ["/src/2.ts", "😃"], - ["/src/3.ts", "\ud800a\udc00"], - ]); + test("template unicode escapes", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": "`\\ud800${0}\\udc00`", + }); - for (const file of expectedTexts.keys()) { - const sourceFile = project.program.getSourceFile(file); + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); assert.ok(sourceFile); + let sawHead = false; + let sawTail = false; sourceFile.forEachChild(function visit(node) { - if (isStringLiteral(node)) { - assert.equal(node.text, expectedTexts.get(file)); + if (isTemplateHead(node)) { + assert.equal(node.text, "\ud800"); + sawHead = true; + } + else if (isTemplateTail(node)) { + assert.equal(node.text, "\udc00"); + sawTail = true; } node.forEachChild(visit); }); - } -}); - -test("template unicode escapes", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": "`\\ud800${0}\\udc00`", + assert.ok(sawHead); + assert.ok(sawTail); }); - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); + test("Object equality", () => { + using api = spawnAPI(); - let sawHead = false; - let sawTail = false; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "\ud800"); - sawHead = true; - } - else if (isTemplateTail(node)) { - assert.equal(node.text, "\udc00"); - sawTail = true; - } - node.forEachChild(visit); + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + // Same symbol returned from same snapshot's checker + assert.strictEqual( + project.checker.getSymbolAtPosition("/src/index.ts", 9), + project.checker.getSymbolAtPosition("/src/index.ts", 10), + ); }); - assert.ok(sawHead); - assert.ok(sawTail); -}); -test("Object equality", () => { - using api = spawnAPI(); - - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - // Same symbol returned from same snapshot's checker - assert.strictEqual( - project.checker.getSymbolAtPosition("/src/index.ts", 9), - project.checker.getSymbolAtPosition("/src/index.ts", 10), - ); -}); - -test("Snapshot dispose", () => { - using api = spawnAPI(); + test("Snapshot dispose", () => { + using api = spawnAPI(); - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); - // Snapshot dispose should release server-side resources - assert.ok(snapshot.isDisposed() === false); - snapshot.dispose(); - assert.ok(snapshot.isDisposed() === true); + // Snapshot dispose should release server-side resources + assert.ok(snapshot.isDisposed() === false); + snapshot.dispose(); + assert.ok(snapshot.isDisposed() === true); - // After dispose, snapshot methods should throw - assert.throws(() => { - snapshot.getConfiguredProject("/tsconfig.json"); - }, { - name: "Error", - message: "Snapshot is disposed", + // After dispose, snapshot methods should throw + assert.throws(() => { + snapshot.getConfiguredProject("/tsconfig.json"); + }, { + name: "Error", + message: "Snapshot is disposed", + }); }); }); -describe("Multiple snapshots", () => { +describe("Multiple snapshots", { concurrency }, () => { test("two snapshots work independently", () => { using api = spawnAPI(); @@ -1714,7 +1719,7 @@ describe("Multiple snapshots", () => { }); }); -describe("Source file caching", () => { +describe("Source file caching", { concurrency }, () => { test("same file from same snapshot returns cached object", () => { using api = spawnAPI(); @@ -1882,7 +1887,7 @@ describe("Source file caching", () => { }); }); -describe("Snapshot disposal", () => { +describe("Snapshot disposal", { concurrency }, () => { test("dispose is idempotent", () => { using api = spawnAPI(); @@ -1921,7 +1926,7 @@ describe("Snapshot disposal", () => { }); }); -describe("Source file cache keying across projects", () => { +describe("Source file cache keying across projects", { concurrency }, () => { // Three projects share the same file (/src/shared.ts). // The file sits inside a package.json scope with "type": "module". // @@ -2000,7 +2005,7 @@ describe("Source file cache keying across projects", () => { }); }); -describe("Checker - symbol identity across projects", () => { +describe("Checker - symbol identity across projects", { concurrency }, () => { const sharedSymbolFiles = { "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), @@ -2054,7 +2059,7 @@ describe("Checker - symbol identity across projects", () => { }); }); -describe("Checker - types and signatures", () => { +describe("Checker - types and signatures", { concurrency }, () => { const checkerFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -2482,7 +2487,7 @@ export class Cache { }); }); -describe("Symbol - parent, members, exports", () => { +describe("Symbol - parent, members, exports", { concurrency }, () => { const symbolFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/mod.ts": ` @@ -2553,7 +2558,7 @@ export const value = 1; }); }); -describe("Type - getSymbol", () => { +describe("Type - getSymbol", { concurrency }, () => { test("getSymbol returns the symbol of a type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -2579,7 +2584,7 @@ export const instance: Foo = new Foo(); }); }); -describe("Type - sub-property fetchers", () => { +describe("Type - sub-property fetchers", { concurrency }, () => { const typeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), "/src/types.ts": ` @@ -2917,7 +2922,7 @@ export function gh1449(a: T): T { }); }); -describe("Checker - intrinsic type getters", () => { +describe("Checker - intrinsic type getters", { concurrency }, () => { const intrinsicFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": `export const x = 1;`, @@ -3044,7 +3049,7 @@ describe("Checker - intrinsic type getters", () => { }); }); -describe("Checker - multi-project type ID uniqueness", () => { +describe("Checker - multi-project type ID uniqueness", { concurrency }, () => { test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", () => { using api = spawnAPI({ "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3150,7 +3155,7 @@ describe("Checker - multi-project type ID uniqueness", () => { }); }); -describe("Checker - getBaseTypeOfLiteralType", () => { +describe("Checker - getBaseTypeOfLiteralType", { concurrency }, () => { test("number literal widens to number", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3192,7 +3197,7 @@ describe("Checker - getBaseTypeOfLiteralType", () => { }); }); -describe("Checker - getContextualType", () => { +describe("Checker - getContextualType", { concurrency }, () => { test("contextual type from function parameter", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3227,7 +3232,7 @@ foo(42); }); }); -describe("Checker - getTypeOfSymbolAtLocation", () => { +describe("Checker - getTypeOfSymbolAtLocation", { concurrency }, () => { test("narrowed type via typeof check", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3282,7 +3287,7 @@ export function check(x: string | number) { }); }); -describe("Checker - getShorthandAssignmentValueSymbol", () => { +describe("Checker - getShorthandAssignmentValueSymbol", { concurrency }, () => { test("shorthand property symbol resolves to variable", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3314,7 +3319,7 @@ export const obj = { name }; }); }); -describe("readFile callback semantics", () => { +describe("readFile callback semantics", { concurrency }, () => { test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", () => { const virtualFiles: Record = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -3364,7 +3369,7 @@ describe("readFile callback semantics", () => { }); }); -describe("updateSnapshot file systems", () => { +describe("updateSnapshot file systems", { concurrency }, () => { test("request filesystem factories normalize files and preserve explicit listings", () => { const memory = createFileSystem([ ["/src/index.ts", "posix"], @@ -3962,7 +3967,7 @@ describe("updateSnapshot file systems", () => { // do not model modification times. }); -describe("Checker - isArrayType / isTupleType", () => { +describe("Checker - isArrayType / isTupleType", { concurrency }, () => { test("number[] is array, not tuple", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4072,7 +4077,7 @@ describe("Checker - isArrayType / isTupleType", () => { }); }); -describe("Checker - isReadonlySymbol", () => { +describe("Checker - isReadonlySymbol", { concurrency }, () => { test("properties with a 'readonly' modifier", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4163,7 +4168,7 @@ export type B = InstanceType; }); }); -describe("Checker - getReturnTypeOfSignature", () => { +describe("Checker - getReturnTypeOfSignature", { concurrency }, () => { test("returns the return type of a function signature", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4186,7 +4191,7 @@ describe("Checker - getReturnTypeOfSignature", () => { }); }); -describe("Checker - getRestTypeOfSignature", () => { +describe("Checker - getRestTypeOfSignature", { concurrency }, () => { test("returns the rest type of a signature with rest parameter", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4209,7 +4214,7 @@ describe("Checker - getRestTypeOfSignature", () => { }); }); -describe("Checker - getTypePredicateOfSignature", () => { +describe("Checker - getTypePredicateOfSignature", { concurrency }, () => { test("returns type predicate for 'x is T' guard", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4309,7 +4314,7 @@ export class Dog extends Animal { }); }); -describe("Checker - getBaseTypes", () => { +describe("Checker - getBaseTypes", { concurrency }, () => { test("returns base types of a class", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4392,7 +4397,7 @@ export type BoxOfString = Box; }); }); -describe("Type - getBaseTypes", () => { +describe("Type - getBaseTypes", { concurrency }, () => { test("returns base types for a class type and undefined for a non-class/interface", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4430,7 +4435,7 @@ export const n: number = 0; }); }); -describe("Type - isErrorType", () => { +describe("Type - isErrorType", { concurrency }, () => { test("identifies the error type from an unresolvable annotation", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4460,7 +4465,7 @@ declare const bad: ThisTypeDoesNotExist; }); }); -describe("Checker - well-known symbols", () => { +describe("Checker - well-known symbols", { concurrency }, () => { test("isUnknownSymbol identifies the aliased unknown symbol", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4482,7 +4487,7 @@ export type Alias = typeof value; }); }); -describe("Checker - well-known signatures", () => { +describe("Checker - well-known signatures", { concurrency }, () => { test("isUnknownSignature identifies an unresolvable call", () => { const src = ` const ok = (x: number) => x; @@ -4516,7 +4521,7 @@ notCallable(); }); }); -describe("Symbol - escaped names and tables", () => { +describe("Symbol - escaped names and tables", { concurrency }, () => { test("getExports/getMembers return a cached Map keyed by escaped name", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4615,7 +4620,7 @@ export { x as '${maliciousName}' }; }); }); -describe("ast - escapeLeadingUnderscores", () => { +describe("ast - escapeLeadingUnderscores", { concurrency }, () => { test("round-trips display and escaped names", () => { assert.equal(escapeLeadingUnderscores("foo"), "foo"); assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); @@ -4626,7 +4631,7 @@ describe("ast - escapeLeadingUnderscores", () => { }); }); -describe("ast - tryGetAmbientModuleNameFromSymbolName", () => { +describe("ast - tryGetAmbientModuleNameFromSymbolName", { concurrency }, () => { test("gets ambient module names from escaped symbol names", () => { assert.equal(tryGetAmbientModuleNameFromSymbolName('"pkg"' as __String), "pkg"); assert.equal(tryGetAmbientModuleNameFromSymbolName('__"*.css"pattern@1234' as __String), "*.css"); @@ -4636,7 +4641,7 @@ describe("ast - tryGetAmbientModuleNameFromSymbolName", () => { }); }); -describe("ast - getJSDocTags", () => { +describe("ast - getJSDocTags", { concurrency }, () => { test("returns a node's own tags, and inherited @param / @template tags", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4778,7 +4783,7 @@ const cast = /** @type {number} */ (someValue); }); }); -describe("Checker - getPropertiesOfType", () => { +describe("Checker - getPropertiesOfType", { concurrency }, () => { test("returns properties of an object type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4809,7 +4814,7 @@ export declare const p: Person; }); }); -describe("Checker - getIndexInfosOfType", () => { +describe("Checker - getIndexInfosOfType", { concurrency }, () => { test("returns index signatures of an indexed type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4864,7 +4869,7 @@ export declare const m: ReadonlyMap; }); }); -describe("Checker - TypeScript API parity", () => { +describe("Checker - TypeScript API parity", { concurrency }, () => { const files = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -4953,7 +4958,7 @@ export type Exported = number; }); }); -describe("Checker - getConstraintOfTypeParameter", () => { +describe("Checker - getConstraintOfTypeParameter", { concurrency }, () => { test("returns constraint of a type parameter", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -4978,7 +4983,7 @@ describe("Checker - getConstraintOfTypeParameter", () => { }); }); -describe("Checker - TypeParameter getters", () => { +describe("Checker - TypeParameter getters", { concurrency }, () => { test("getConstraint() and getDefault() return the constraint and default types", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5033,7 +5038,7 @@ describe("Checker - TypeParameter getters", () => { }); }); -describe("Checker - getTypeArguments", () => { +describe("Checker - getTypeArguments", { concurrency }, () => { test("returns type arguments of a generic instantiation", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5082,7 +5087,7 @@ describe("Checker - getTypeArguments", () => { }); }); -describe("Checker - getBaseConstraintOfType", () => { +describe("Checker - getBaseConstraintOfType", { concurrency }, () => { test("returns the base constraint of a type parameter", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5123,7 +5128,7 @@ describe("Checker - getBaseConstraintOfType", () => { }); }); -describe("Checker - getPropertyOfType", () => { +describe("Checker - getPropertyOfType", { concurrency }, () => { test("returns a named property symbol of a type", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5152,7 +5157,7 @@ export declare const p: Person; }); }); -describe("Checker - getConstantValue", () => { +describe("Checker - getConstantValue", { concurrency }, () => { test("returns numeric value of an enum member", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5231,7 +5236,7 @@ describe("Checker - getConstantValue", () => { }); }); -describe("Checker - getSignatureFromDeclaration", () => { +describe("Checker - getSignatureFromDeclaration", { concurrency }, () => { test("returns the signature of a function declaration", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5257,7 +5262,7 @@ describe("Checker - getSignatureFromDeclaration", () => { }); }); -describe("Checker - getExportSpecifierLocalTargetSymbol", () => { +describe("Checker - getExportSpecifierLocalTargetSymbol", { concurrency }, () => { test("resolves the local target of an export specifier", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5283,7 +5288,7 @@ export { value as renamed }; }); }); -describe("Checker - getAliasedSymbol", () => { +describe("Checker - getAliasedSymbol", { concurrency }, () => { test("resolves an import alias to its target symbol", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5304,7 +5309,7 @@ describe("Checker - getAliasedSymbol", () => { }); }); -describe("Checker - getFullyQualifiedName", () => { +describe("Checker - getFullyQualifiedName", { concurrency }, () => { test("returns module-qualified names for exported symbols and dotted names for members", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5334,7 +5339,7 @@ export class Standalone {} }); }); -describe("Checker - getExportsOfModule", () => { +describe("Checker - getExportsOfModule", { concurrency }, () => { test("returns all exports including re-exports via 'export *'", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5358,7 +5363,7 @@ export * from "./inner"; }); }); -describe("Checker - getSymbolsInScope", () => { +describe("Checker - getSymbolsInScope", { concurrency }, () => { const scopeFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -5419,7 +5424,7 @@ function f() { }); }); -describe("Symbol - getDocumentationComment and getJsDocTags", () => { +describe("Symbol - getDocumentationComment and getJsDocTags", { concurrency }, () => { const docFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -5463,7 +5468,7 @@ export function add(a: number, b: number): number { return a + b; } }); }); -describe("TypeParameter - isThisType", () => { +describe("TypeParameter - isThisType", { concurrency }, () => { test("isThisType is true for the polymorphic 'this' type in a class method", () => { const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; using api = spawnAPI({ @@ -5503,7 +5508,7 @@ describe("TypeParameter - isThisType", () => { }); }); -describe("Type - getAliasTypeArguments", () => { +describe("Type - getAliasTypeArguments", { concurrency }, () => { test("returns the type arguments of a single-param generic type alias", () => { const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; using api = spawnAPI({ @@ -5562,7 +5567,7 @@ describe("Type - getAliasTypeArguments", () => { }); }); -describe("Type - getAliasSymbol", () => { +describe("Type - getAliasSymbol", { concurrency }, () => { test("returns the symbol for a non-generic type alias", () => { // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; @@ -5621,7 +5626,7 @@ describe("Type - getAliasSymbol", () => { }); }); -describe("IntrinsicType - intrinsicName", () => { +describe("IntrinsicType - intrinsicName", { concurrency }, () => { test("intrinsicName matches the primitive type name", () => { const src = `\nexport const x: string = "hello";\n`; using api = spawnAPI({ @@ -5647,7 +5652,7 @@ describe("IntrinsicType - intrinsicName", () => { }); }); -describe("FreshableType - getFreshType and getRegularType", () => { +describe("FreshableType - getFreshType and getRegularType", { concurrency }, () => { test("LiteralType.value is empty string for the empty-string literal type", () => { const src = `\nexport const empty: "" = "";\n`; using api = spawnAPI({ @@ -5830,7 +5835,7 @@ describe("FreshableType - getFreshType and getRegularType", () => { }); }); -describe("Checker - isContextSensitive", () => { +describe("Checker - isContextSensitive", { concurrency }, () => { test("arrow function with no type annotation is context sensitive", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), @@ -5855,7 +5860,7 @@ describe("Checker - isContextSensitive", () => { }); }); -describe("Checker - isTypeAssignableTo", () => { +describe("Checker - isTypeAssignableTo", { concurrency }, () => { test("returns true when source is assignable to target", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -5908,7 +5913,7 @@ describe("Checker - isTypeAssignableTo", () => { }); }); -describe("Printer", () => { +describe("Printer", { concurrency }, () => { const emitterFiles = { "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), "/src/main.ts": ` @@ -6151,7 +6156,7 @@ export const obj = { m: 1, s: "hi", b: true }; }); }); -describe("Program - selected file emit", () => { +describe("Program - selected file emit", { concurrency }, () => { const files = { "/tsconfig.json": JSON.stringify({ compilerOptions: { @@ -6213,7 +6218,7 @@ describe("Program - selected file emit", () => { }); }); -describe("SnapshotInternalAPI - formatNodeForInsertion", () => { +describe("SnapshotInternalAPI - formatNodeForInsertion", { concurrency }, () => { test("formats a synthesized statement with correct indentation for insertion inside a function body", () => { const files = { "/tsconfig.json": "{}", @@ -6291,7 +6296,7 @@ describe("SnapshotInternalAPI - formatNodeForInsertion", () => { }); }); -describe("modifierFlags", () => { +describe("modifierFlags", { concurrency }, () => { test("export async function has Export | Async flags", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6339,7 +6344,7 @@ describe("modifierFlags", () => { }); }); -describe("Checker - getResolvedSymbol", () => { +describe("Checker - getResolvedSymbol", { concurrency }, () => { test("resolves variable reference to its declaration symbol", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6368,7 +6373,7 @@ describe("Checker - getResolvedSymbol", () => { }); }); -describe("VariableDeclarationList - BlockScoped flags", () => { +describe("VariableDeclarationList - BlockScoped flags", { concurrency }, () => { test("let declaration has Let flag", () => { using api = spawnAPI({ "/tsconfig.json": "{}", @@ -6414,161 +6419,163 @@ describe("VariableDeclarationList - BlockScoped flags", () => { }); }); -test("TypeOperator operator kind", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function test(arg: readonly number[]) { }\n`, - }); - - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; - assert(param); - const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; - assert(type); - assert.equal(type.kind, SyntaxKind.TypeOperator); - assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); - const printed = api.printer.printFile(sourceFile); - assert.equal(sourceFile.text, printed); -}); - -test("SpreadAssignment roundtrip", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `var thing = { ...other };\n`, - }); - - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; - const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; - assert(assignment); - assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); - const expr = assignment.expression; - assert(expr); - assert.equal(expr.kind, SyntaxKind.Identifier); - const printed = api.printer.printFile(sourceFile); - assert.equal(sourceFile.text, printed); -}); +describe("AST roundtrips", { concurrency }, () => { + test("TypeOperator operator kind", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function test(arg: readonly number[]) { }\n`, + }); -test("VariableDeclarationList const flag clone", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const thing = 123;\n`, + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; + assert(param); + const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; + assert(type); + assert.equal(type.kind, SyntaxKind.TypeOperator); + assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); + const printed = api.printer.printFile(sourceFile); + assert.equal(sourceFile.text, printed); }); - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - { + test("SpreadAssignment roundtrip", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `var thing = { ...other };\n`, + }); + + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const cloned = getSynthesizedDeepClone(sourceFile); - { - const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const printed = api.printer.printFile(cloned); - assert.equal(sourceFile.text, printed); -}); + const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; + const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; + assert(assignment); + assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); + const expr = assignment.expression; + assert(expr); + assert.equal(expr.kind, SyntaxKind.Identifier); + const printed = api.printer.printFile(sourceFile); + assert.equal(sourceFile.text, printed); + }); -test("JSDoc before ExpressionStatement allowed", () => { - using api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": ` + test("VariableDeclarationList const flag clone", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const thing = 123;\n`, + }); + + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + { + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const cloned = getSynthesizedDeepClone(sourceFile); + { + const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const printed = api.printer.printFile(cloned); + assert.equal(sourceFile.text, printed); + }); + + test("JSDoc before ExpressionStatement allowed", () => { + using api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": ` /** * A doc. */ doThing(); `, - }); - - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const printed = api.printer.printFile(sourceFile); - assert.equal(sourceFile.text.trim(), printed.trim()); -}); + }); -test("Factory ModifierList auto-conversion", () => { - using api = spawnAPI(); + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const printed = api.printer.printFile(sourceFile); + assert.equal(sourceFile.text.trim(), printed.trim()); + }); - const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getConfiguredProject("/tsconfig.json")!; - const node = createTypeAliasDeclaration( - [createToken(SyntaxKind.ExportKeyword)], - createIdentifier("Test"), - undefined, - createKeywordTypeNode(SyntaxKind.AnyKeyword), - ); + test("Factory ModifierList auto-conversion", () => { + using api = spawnAPI(); - assert.equal(api.printer.printNode(node), "export type Test = any;"); + const snapshot = api.createSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getConfiguredProject("/tsconfig.json")!; + const node = createTypeAliasDeclaration( + [createToken(SyntaxKind.ExportKeyword)], + createIdentifier("Test"), + undefined, + createKeywordTypeNode(SyntaxKind.AnyKeyword), + ); - const cloned = getSynthesizedDeepClone(node); - assert.equal(api.printer.printNode(cloned), "export type Test = any;"); -}); + assert.equal(api.printer.printNode(node), "export type Test = any;"); -test("Parse-clone-emit roundtrip", () => { - const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); - using api = new API({ - cwd: tsSource, + const cloned = getSynthesizedDeepClone(node); + assert.equal(api.printer.printNode(cloned), "export type Test = any;"); }); - const target = { - cloneCrashed: 0, - printCrashed: 0, - clonePrintCrashed: 0, - }; - const errors = { ...target }; - - for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { - const snapshot = api.createSnapshot({ openProject: resolve(tsSource, tsconfig) }); - const project = snapshot.getConfiguredProject(tsconfig); - assert(project); - for (const file of project.rootFiles) { - const source = project.program.getSourceFile(file); - assert(source); - let clone: typeof source; - - try { - api.printer.printNode(source); - } - catch { - errors.printCrashed++; - continue; - } - try { - clone = getSynthesizedDeepClone(source); - } - catch { - errors.cloneCrashed++; - continue; - } + test("Parse-clone-emit roundtrip", () => { + const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); + using api = new API({ + cwd: tsSource, + }); + const target = { + cloneCrashed: 0, + printCrashed: 0, + clonePrintCrashed: 0, + }; + const errors = { ...target }; + + for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { + const snapshot = api.createSnapshot({ openProject: resolve(tsSource, tsconfig) }); + const project = snapshot.getConfiguredProject(tsconfig); + assert(project); + for (const file of project.rootFiles) { + const source = project.program.getSourceFile(file); + assert(source); + let clone: typeof source; + + try { + api.printer.printNode(source); + } + catch { + errors.printCrashed++; + continue; + } - try { - api.printer.printNode(clone); - } - catch { - errors.clonePrintCrashed++; - continue; + try { + clone = getSynthesizedDeepClone(source); + } + catch { + errors.cloneCrashed++; + continue; + } + + try { + api.printer.printNode(clone); + } + catch { + errors.clonePrintCrashed++; + continue; + } } } - } - assert.deepEqual(errors, target); + assert.deepEqual(errors, target); + }); }); -describe("Program - diagnostics", () => { +describe("Program - diagnostics", { concurrency }, () => { test("getSyntacticDiagnostics", () => { const source = `const x: = 1;`; using api = spawnAPI({ @@ -6898,7 +6905,7 @@ describe("Program - diagnostics", () => { }); }); -describe("getDefaultProjectForFile", () => { +describe("getDefaultProjectForFile", { concurrency }, () => { test("snapshot opens reject unreadable virtual files without panicking", () => { const fileName = "/src/App.vue.ts"; const source = `export const component = 1;`; @@ -7080,7 +7087,7 @@ describe("getDefaultProjectForFile", () => { }); }); -describe("Program - emit", () => { +describe("Program - emit", { concurrency }, () => { const files = { "/tsconfig.json": `{ "compilerOptions": { @@ -7321,7 +7328,7 @@ describe("Program - emit", () => { }); }); -describe("Timing", () => { +describe("Timing", { concurrency }, () => { test("collects combined client, server, and transport timing info", () => { using api = new API({ cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), @@ -7457,7 +7464,7 @@ describe("Timing", () => { }); }); -describe("runWithTemporaryFileUpdate", () => { +describe("runWithTemporaryFileUpdate", { concurrency }, () => { test("temporary file update is visible in the callback and reverted afterward", () => { using api = spawnAPI({ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), diff --git a/packages/typescript/test/sync/ast.test.ts b/packages/typescript/test/sync/ast.test.ts index f5284f5b223e7..fad091bbb7732 100644 --- a/packages/typescript/test/sync/ast.test.ts +++ b/packages/typescript/test/sync/ast.test.ts @@ -56,8 +56,11 @@ import { test, } from "node:test"; import { fileURLToPath } from "node:url"; +import { areTestsFiltered } from "../testUtils.ts"; import { runBenchmarks } from "./ast.bench.ts"; +const concurrency = areTestsFiltered(); + // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- @@ -71,7 +74,7 @@ function collectKinds(node: Node): SyntaxKind[] { return kinds; } -describe("NodeObject + childrenIter", () => { +describe("NodeObject + childrenIter", { concurrency }, () => { test("skips absent optional child lists", () => { const node = createMissingDeclaration(); assert.deepStrictEqual([...node.childrenIter()], []); @@ -86,7 +89,7 @@ test("Benchmarks", async () => { // cloneNode // --------------------------------------------------------------------------- -describe("cloneNode", () => { +describe("cloneNode", { concurrency }, () => { test("clones an identifier", () => { const id = createIdentifier("hello"); const clone = cloneNode(id); @@ -156,7 +159,7 @@ describe("cloneNode", () => { // visitNode / visitNodes // --------------------------------------------------------------------------- -describe("visitNode", () => { +describe("visitNode", { concurrency }, () => { test("returns undefined for undefined input", () => { const nothing: Node | undefined = undefined; const result = visitNode(nothing, () => undefined); @@ -177,7 +180,7 @@ describe("visitNode", () => { }); }); -describe("visitNodes", () => { +describe("visitNodes", { concurrency }, () => { test("returns undefined for undefined input", () => { const nothing: NodeArray | undefined = undefined; const result = visitNodes(nothing, () => undefined); @@ -222,7 +225,7 @@ describe("visitNodes", () => { // visitEachChild // --------------------------------------------------------------------------- -describe("visitEachChild", () => { +describe("visitEachChild", { concurrency }, () => { test("returns same node if nothing changed (identity visitor)", () => { const left = createIdentifier("a"); const right = createIdentifier("b"); @@ -307,7 +310,7 @@ describe("visitEachChild", () => { // getSynthesizedDeepClone // --------------------------------------------------------------------------- -describe("getSynthesizedDeepClone", () => { +describe("getSynthesizedDeepClone", { concurrency }, () => { test("deeply clones identifier", () => { const id = createIdentifier("hello"); const clone = getSynthesizedDeepClone(id); @@ -440,7 +443,7 @@ describe("getSynthesizedDeepClone", () => { // getSynthesizedDeepClones (NodeArray) // --------------------------------------------------------------------------- -describe("getSynthesizedDeepClones", () => { +describe("getSynthesizedDeepClones", { concurrency }, () => { test("deeply clones a NodeArray", () => { const a = createIdentifier("a"); const b = createIdentifier("b"); @@ -475,7 +478,7 @@ describe("getSynthesizedDeepClones", () => { // Type-only import use sites // --------------------------------------------------------------------------- -describe("isValidTypeOnlyAliasUseSite", () => { +describe("isValidTypeOnlyAliasUseSite", { concurrency }, () => { test("classifies syntactic type-only import use sites", () => { const source = ` type TypeUse = TypeOnlyName; @@ -527,7 +530,7 @@ class JSDocAugmentsUse {} // Integration: visitor transformation // --------------------------------------------------------------------------- -describe("visitor transformation", () => { +describe("visitor transformation", { concurrency }, () => { test("rename all identifiers via recursive visitor", () => { const a = createIdentifier("oldName"); const b = createIdentifier("oldName"); @@ -602,7 +605,7 @@ function getRemoteSourceFile(api: API, configPath: string, filePath: string) { return getRemoteSourceFileAndChecker(api, configPath, filePath)[0]; } -describe("RemoteNode + cloneNode", () => { +describe("RemoteNode + cloneNode", { concurrency }, () => { test("does not read a sibling as an invalid JSDoc link name", () => { const api = spawnAPI({ "/tsconfig.json": "{}", @@ -718,7 +721,7 @@ interface I extends Parent {} }); }); -describe("RemoteNode + visitEachChild", () => { +describe("RemoteNode + visitEachChild", { concurrency }, () => { test("identity visitor returns same remote node", () => { const api = spawnAPI(); try { @@ -760,7 +763,7 @@ describe("RemoteNode + visitEachChild", () => { }); }); -describe("RemoteNodeList inherited array methods", () => { +describe("RemoteNodeList inherited array methods", { concurrency }, () => { test("filter/map/slice return plain arrays without throwing", () => { const api = spawnAPI(); try { @@ -793,7 +796,7 @@ describe("RemoteNodeList inherited array methods", () => { }); }); -describe("RemoteNode + getSynthesizedDeepClone", () => { +describe("RemoteNode + getSynthesizedDeepClone", { concurrency }, () => { test("deep clones a remote import declaration", () => { const api = spawnAPI(); try { @@ -907,7 +910,7 @@ function assertGetterInvariants(node: Node, sf: SourceFile) { }); } -describe("RemoteNode + position/text getters", () => { +describe("RemoteNode + position/text getters", { concurrency }, () => { const source = "/* lead */ const value = 123;"; const files = { "/tsconfig.json": "{}", @@ -1060,7 +1063,7 @@ describe("RemoteNode + position/text getters", () => { // RemoteNode: child/token getters // --------------------------------------------------------------------------- -describe("RemoteNode + child/token getters", () => { +describe("RemoteNode + child/token getters", { concurrency }, () => { function withFirstStatement(source: string, fn: (stmt: Node, sf: SourceFile) => void) { const api = spawnAPI({ "/tsconfig.json": "{}", "/src/children.ts": source }); try { diff --git a/packages/typescript/test/sync/astnav.test.ts b/packages/typescript/test/sync/astnav.test.ts index 3747e638d013b..206bc9936a5d0 100644 --- a/packages/typescript/test/sync/astnav.test.ts +++ b/packages/typescript/test/sync/astnav.test.ts @@ -29,6 +29,7 @@ import { test, } from "node:test"; import { fileURLToPath } from "node:url"; +import { areTestsFiltered } from "../testUtils.ts"; // --------------------------------------------------------------------------- // Go JSON baseline format @@ -81,7 +82,7 @@ const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav") // Tests // --------------------------------------------------------------------------- -describe("astnav", () => { +describe("astnav", { concurrency: areTestsFiltered() }, () => { let fileText: string; try { diff --git a/packages/typescript/test/testUtils.ts b/packages/typescript/test/testUtils.ts new file mode 100644 index 0000000000000..3760c6ed098b7 --- /dev/null +++ b/packages/typescript/test/testUtils.ts @@ -0,0 +1,3 @@ +export function areTestsFiltered(): boolean { + return process.execArgv.some(arg => arg === "--test-name-pattern" || arg.startsWith("--test-name-pattern=")); +} diff --git a/packages/typescript/test/wtf8.test.ts b/packages/typescript/test/wtf8.test.ts index 60cc878b0fd52..01998913edade 100644 --- a/packages/typescript/test/wtf8.test.ts +++ b/packages/typescript/test/wtf8.test.ts @@ -11,8 +11,11 @@ import { encodeWtf8, Wtf8Decoder, } from "../src/api/node/wtf8.ts"; +import { areTestsFiltered } from "./testUtils.ts"; -describe("encodeWtf8", () => { +const concurrency = areTestsFiltered(); + +describe("encodeWtf8", { concurrency }, () => { test("matches UTF-8 for scalar values", () => { assert.deepEqual(encodeWtf8("hello 🦀"), new TextEncoder().encode("hello 🦀")); }); @@ -48,7 +51,7 @@ describe("encodeWtf8", () => { }); }); -describe("Wtf8Decoder", () => { +describe("Wtf8Decoder", { concurrency }, () => { test("decodes standard UTF-8", () => { const decoder = new Wtf8Decoder(); assert.strictEqual(decoder.decode(new TextEncoder().encode("hello 🦀")), "hello 🦀"); @@ -86,7 +89,7 @@ describe("Wtf8Decoder", () => { }); }); -describe("Msgpack strings", () => { +describe("Msgpack strings", { concurrency }, () => { test("round-trip lone surrogates", () => { const text = `high:${String.fromCharCode(0xD800)} low:${String.fromCharCode(0xDFFF)}`; const writer = new MsgpackWriter(); diff --git a/packages/vscode-typescript/test/contentMapperContributions.test.ts b/packages/vscode-typescript/test/contentMapperContributions.test.ts index 74c4dd07116f9..60db78fb9d403 100644 --- a/packages/vscode-typescript/test/contentMapperContributions.test.ts +++ b/packages/vscode-typescript/test/contentMapperContributions.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import test, { describe } from "node:test"; import { type ContentMapperContribution, documentMatchesContentMapperContributions, @@ -20,35 +20,37 @@ const documentedContribution = { }, } satisfies ContentMapperContribution; -test("content mapper extensions match document paths case-insensitively", () => { - const registrations = new Map([[ - "publisher.extension", - [{ extensions: [".vue"] }], - ]]); - const document = { uri: { path: "/workspace/Component.VUE" } }; +describe("content mapper contributions", { concurrency: true }, () => { + test("content mapper extensions match document paths case-insensitively", () => { + const registrations = new Map([[ + "publisher.extension", + [{ extensions: [".vue"] }], + ]]); + const document = { uri: { path: "/workspace/Component.VUE" } }; - assert.equal(documentMatchesContentMapperContributions(document, registrations), true); -}); + assert.equal(documentMatchesContentMapperContributions(document, registrations), true); + }); -test("serializes the documented inferred project contribution", () => { - const registrations = new Map([[ - "publisher.extension", - [documentedContribution], - ]]); + test("serializes the documented inferred project contribution", () => { + const registrations = new Map([[ + "publisher.extension", + [documentedContribution], + ]]); - assert.deepEqual(serializeContentMapperContributions(registrations), [{ - contributorId: "publisher.extension", - extensions: [".vue"], - inferredProjectContribution: { - options: { strictTemplates: true }, - manifest: { - name: "Vue mapper", - version: "1.2.3", - exec: ["node", "mapper.js"], - cwd: undefined, - compilerOptions: ["strict"], - dynamicConfig: true, + assert.deepEqual(serializeContentMapperContributions(registrations), [{ + contributorId: "publisher.extension", + extensions: [".vue"], + inferredProjectContribution: { + options: { strictTemplates: true }, + manifest: { + name: "Vue mapper", + version: "1.2.3", + exec: ["node", "mapper.js"], + cwd: undefined, + compilerOptions: ["strict"], + dynamicConfig: true, + }, }, - }, - }]); + }]); + }); }); diff --git a/packages/vscode-typescript/test/tsdkPackage.test.ts b/packages/vscode-typescript/test/tsdkPackage.test.ts index ab633ed55fc98..5a96fa1246110 100644 --- a/packages/vscode-typescript/test/tsdkPackage.test.ts +++ b/packages/vscode-typescript/test/tsdkPackage.test.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import test from "node:test"; +import test, { describe } from "node:test"; import { resolvePackageExecutable } from "../src/tsdkPackage"; const platformPackage = `typescript-${process.platform}-${process.arch}`; @@ -59,69 +59,71 @@ function testResolution(name: string, resolutionCase: ResolutionCase): void { }); } -testResolution("resolves the TypeScript 7 package", { - packagePath: "node_modules/typescript", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); +describe("tsdk package resolution", { concurrency: true }, () => { + testResolution("resolves the TypeScript 7 package", { + packagePath: "node_modules/typescript", + platformPath: `node_modules/@typescript/${platformPackage}`, + }); -testResolution("resolves the TypeScript 7 scoped alias recommended in the 7.0 release post", { - packagePath: "node_modules/@typescript/native", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); + testResolution("resolves the TypeScript 7 scoped alias recommended in the 7.0 release post", { + packagePath: "node_modules/@typescript/native", + platformPath: `node_modules/@typescript/${platformPackage}`, + }); -testResolution("resolves an unscoped TypeScript 7 alias", { - packagePath: "node_modules/typescript-next", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); + testResolution("resolves an unscoped TypeScript 7 alias", { + packagePath: "node_modules/typescript-next", + platformPath: `node_modules/@typescript/${platformPackage}`, + }); -const nativePlatformPackage = `native-preview-${process.platform}-${process.arch}`; -testResolution("resolves the native-preview package", { - packagePath: "node_modules/@typescript/typescript", - platformPath: `node_modules/@typescript/${nativePlatformPackage}`, - platformPackage: nativePlatformPackage, - exeName: nativeExeName, -}); + const nativePlatformPackage = `native-preview-${process.platform}-${process.arch}`; + testResolution("resolves the native-preview package", { + packagePath: "node_modules/@typescript/typescript", + platformPath: `node_modules/@typescript/${nativePlatformPackage}`, + platformPackage: nativePlatformPackage, + exeName: nativeExeName, + }); -testResolution("resolves a non-hoisted platform package", { - packagePath: "node_modules/@typescript/native", - platformPath: `node_modules/@typescript/native/node_modules/@typescript/${platformPackage}`, -}); + testResolution("resolves a non-hoisted platform package", { + packagePath: "node_modules/@typescript/native", + platformPath: `node_modules/@typescript/native/node_modules/@typescript/${platformPackage}`, + }); -testResolution("resolves a platform package hoisted above a workspace", { - packagePath: "packages/app/node_modules/@typescript/native", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); + testResolution("resolves a platform package hoisted above a workspace", { + packagePath: "packages/app/node_modules/@typescript/native", + platformPath: `node_modules/@typescript/${platformPackage}`, + }); -const pnpmStore = "node_modules/.pnpm/typescript@7.0.2/node_modules"; -testResolution("resolves a pnpm virtual-store package", { - packagePath: `${pnpmStore}/typescript`, - platformPath: `node_modules/.pnpm/@typescript+${platformPackage}@7.0.2/node_modules/@typescript/${platformPackage}`, - dependencyLink: `${pnpmStore}/@typescript/${platformPackage}`, -}); + const pnpmStore = "node_modules/.pnpm/typescript@7.0.2/node_modules"; + testResolution("resolves a pnpm virtual-store package", { + packagePath: `${pnpmStore}/typescript`, + platformPath: `node_modules/.pnpm/@typescript+${platformPackage}@7.0.2/node_modules/@typescript/${platformPackage}`, + dependencyLink: `${pnpmStore}/@typescript/${platformPackage}`, + }); -const nativePreviewVersion = "7.0.0-dev.20260707.2"; -const pnpmNativePreviewStore = `node_modules/.pnpm/@typescript+native-preview@${nativePreviewVersion}/node_modules`; -testResolution("resolves a pnpm native-preview virtual-store package", { - packagePath: `${pnpmNativePreviewStore}/@typescript/typescript`, - platformPath: `node_modules/.pnpm/@typescript+${nativePlatformPackage}@${nativePreviewVersion}/node_modules/@typescript/${nativePlatformPackage}`, - dependencyLink: `${pnpmNativePreviewStore}/@typescript/${nativePlatformPackage}`, - platformPackage: nativePlatformPackage, - exeName: nativeExeName, -}); + const nativePreviewVersion = "7.0.0-dev.20260707.2"; + const pnpmNativePreviewStore = `node_modules/.pnpm/@typescript+native-preview@${nativePreviewVersion}/node_modules`; + testResolution("resolves a pnpm native-preview virtual-store package", { + packagePath: `${pnpmNativePreviewStore}/@typescript/typescript`, + platformPath: `node_modules/.pnpm/@typescript+${nativePlatformPackage}@${nativePreviewVersion}/node_modules/@typescript/${nativePlatformPackage}`, + dependencyLink: `${pnpmNativePreviewStore}/@typescript/${nativePlatformPackage}`, + platformPackage: nativePlatformPackage, + exeName: nativeExeName, + }); -const npmStore = "node_modules/.store/typescript@7.0.2-hash/node_modules"; -testResolution("resolves an npm linked-store package", { - packagePath: `${npmStore}/typescript`, - platformPath: `node_modules/.store/@typescript+${platformPackage}@7.0.2-hash/node_modules/@typescript/${platformPackage}`, - dependencyLink: `${npmStore}/@typescript/${platformPackage}`, -}); + const npmStore = "node_modules/.store/typescript@7.0.2-hash/node_modules"; + testResolution("resolves an npm linked-store package", { + packagePath: `${npmStore}/typescript`, + platformPath: `node_modules/.store/@typescript+${platformPackage}@7.0.2-hash/node_modules/@typescript/${platformPackage}`, + dependencyLink: `${npmStore}/@typescript/${platformPackage}`, + }); -test("throws when the platform package is missing", t => { - const root = createFixture(t); - const packageJsonPath = createPackage(root, "node_modules/@typescript/native"); + test("throws when the platform package is missing", t => { + const root = createFixture(t); + const packageJsonPath = createPackage(root, "node_modules/@typescript/native"); - assert.throws( - () => resolvePackageExecutable(packageJsonPath, platformPackage, exeName), - { code: "MODULE_NOT_FOUND" }, - ); + assert.throws( + () => resolvePackageExecutable(packageJsonPath, platformPackage, exeName), + { code: "MODULE_NOT_FOUND" }, + ); + }); });