1describe("unittests:: Public APIs", () => { 2 function verifyApi(fileName: string) { 3 const builtFile = `built/local/${fileName}`; 4 const api = `api/${fileName}`; 5 let fileContent: string; 6 before(() => { 7 fileContent = Harness.IO.readFile(builtFile)!; 8 if (!fileContent) throw new Error(`File ${fileName} was not present in built/local`); 9 fileContent = fileContent.replace(/\r\n/g, "\n"); 10 }); 11 12 it("should be acknowledged when they change", () => { 13 Harness.Baseline.runBaseline(api, fileContent, { PrintDiff: true }); 14 }); 15 16 it("should compile", () => { 17 const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false); 18 fs.linkSync(`${vfs.builtFolder}/${fileName}`, `${vfs.srcFolder}/${fileName}`); 19 const sys = new fakes.System(fs); 20 const options: ts.CompilerOptions = { 21 ...ts.getDefaultCompilerOptions(), 22 strict: true, 23 exactOptionalPropertyTypes: true, 24 }; 25 const host = new fakes.CompilerHost(sys, options); 26 const result = compiler.compileFiles(host, [`${vfs.srcFolder}/${fileName}`], options); 27 assert(!result.diagnostics || !result.diagnostics.length, Harness.Compiler.minimalDiagnosticsToString(result.diagnostics, /*pretty*/ true)); 28 }); 29 } 30 31 describe("for the language service and compiler", () => { 32 verifyApi("typescript.d.ts"); 33 }); 34 35 describe("for the language server", () => { 36 verifyApi("tsserverlibrary.d.ts"); 37 }); 38}); 39 40describe("unittests:: Public APIs:: token to string", () => { 41 function assertDefinedTokenToString(initial: ts.SyntaxKind, last: ts.SyntaxKind) { 42 for (let t = initial; t <= last; t++) { 43 assert.isDefined(ts.tokenToString(t), `Expected tokenToString defined for ${ts.Debug.formatSyntaxKind(t)}`); 44 } 45 } 46 47 it("for punctuations", () => { 48 assertDefinedTokenToString(ts.SyntaxKind.FirstPunctuation, ts.SyntaxKind.LastPunctuation); 49 }); 50 it("for keywords", () => { 51 assertDefinedTokenToString(ts.SyntaxKind.FirstKeyword, ts.SyntaxKind.LastKeyword); 52 }); 53}); 54 55describe("unittests:: Public APIs:: createPrivateIdentifier", () => { 56 it("throws when name doesn't start with #", () => { 57 assert.throw(() => ts.factory.createPrivateIdentifier("not"), "Debug Failure. First character of private identifier must be #: not"); 58 }); 59}); 60 61describe("unittests:: Public APIs:: JSDoc newlines", () => { 62 it("are preserved verbatim", () => { 63 const testFilePath = "/file.ts"; 64 const testFileText = ` 65/** 66* @example 67* Some\n * text\r\n * with newlines. 68*/ 69function test() {}`; 70 71 const testSourceFile = ts.createSourceFile(testFilePath, testFileText, ts.ScriptTarget.Latest, /*setParentNodes*/ true); 72 const funcDec = testSourceFile.statements.find(ts.isFunctionDeclaration)!; 73 const tags = ts.getJSDocTags(funcDec); 74 assert.isDefined(tags[0].comment); 75 assert.isDefined(tags[0].comment![0]); 76 assert.isString(tags[0].comment); 77 assert.equal(tags[0].comment as string, "Some\n text\r\n with newlines."); 78 }); 79}); 80 81describe("unittests:: Public APIs:: isPropertyName", () => { 82 it("checks if a PrivateIdentifier is a valid property name", () => { 83 const prop = ts.factory.createPrivateIdentifier("#foo"); 84 assert.isTrue(ts.isPropertyName(prop), "PrivateIdentifier must be a valid property name."); 85 }); 86}); 87 88describe("unittests:: Public APIs:: getTypeAtLocation", () => { 89 it("works on PropertyAccessExpression in implements clause", () => { 90 const content = `namespace Test { 91 export interface Test {} 92 } 93 class Foo implements Test.Test {}`; 94 95 const host = new fakes.CompilerHost(vfs.createFromFileSystem( 96 Harness.IO, 97 /*ignoreCase*/ true, 98 { documents: [new documents.TextDocument("/file.ts", content)], cwd: "/" })); 99 100 const program = ts.createProgram({ 101 host, 102 rootNames: ["/file.ts"], 103 options: { noLib: true } 104 }); 105 106 const checker = program.getTypeChecker(); 107 const file = program.getSourceFile("/file.ts")!; 108 const classDeclaration = file.statements.find(ts.isClassDeclaration)!; 109 const propertyAccess = classDeclaration.heritageClauses![0].types[0].expression as ts.PropertyAccessExpression; 110 const type = checker.getTypeAtLocation(propertyAccess); 111 assert.ok(!(type.flags & ts.TypeFlags.Any)); 112 assert.equal(type, checker.getTypeAtLocation(propertyAccess.name)); 113 }); 114 115 it("works on SourceFile", () => { 116 const content = `const foo = 1;`; 117 const host = new fakes.CompilerHost(vfs.createFromFileSystem( 118 Harness.IO, 119 /*ignoreCase*/ true, 120 { documents: [new documents.TextDocument("/file.ts", content)], cwd: "/" })); 121 122 const program = ts.createProgram({ 123 host, 124 rootNames: ["/file.ts"], 125 options: { noLib: true } 126 }); 127 128 const checker = program.getTypeChecker(); 129 const file = program.getSourceFile("/file.ts")!; 130 const type = checker.getTypeAtLocation(file); 131 assert.equal(type.flags, ts.TypeFlags.Any); 132 }); 133 134 it("works on ExpressionWithTypeArguments", () => { 135 const content = ` 136 function fn<T>(value: T) { 137 return { value }; 138 } 139 const foo = fn<string>; 140 `; 141 const host = new fakes.CompilerHost(vfs.createFromFileSystem( 142 Harness.IO, 143 /*ignoreCase*/ true, 144 { documents: [new documents.TextDocument("/file.ts", content)], cwd: "/" })); 145 146 const program = ts.createProgram({ 147 host, 148 rootNames: ["/file.ts"], 149 options: { noLib: true } 150 }); 151 152 const checker = program.getTypeChecker(); 153 const file = program.getSourceFile("/file.ts")!; 154 const [declaration] = (ts.findLast(file.statements, ts.isVariableStatement) as ts.VariableStatement).declarationList.declarations; 155 assert.equal(checker.getTypeAtLocation(declaration.initializer!).flags, ts.TypeFlags.Object); 156 }); 157 158 it("returns an errorType for VariableDeclaration with BindingPattern name", () => { 159 const content = "const foo = [1];\n" + "const [a] = foo;"; 160 161 const host = new fakes.CompilerHost(vfs.createFromFileSystem( 162 Harness.IO, 163 /*ignoreCase*/ true, 164 { documents: [new documents.TextDocument("/file.ts", content)], cwd: "/" })); 165 166 const program = ts.createProgram({ 167 host, 168 rootNames: ["/file.ts"], 169 options: { noLib: true } 170 }); 171 172 const checker = program.getTypeChecker(); 173 const file = program.getSourceFile("/file.ts")!; 174 const [declaration] = (ts.findLast(file.statements, ts.isVariableStatement) as ts.VariableStatement).declarationList.declarations; 175 assert.equal(checker.getTypeAtLocation(declaration).flags, ts.TypeFlags.Any); 176 }); 177}); 178 179describe("unittests:: Public APIs:: validateLocaleAndSetLanguage", () => { 180 let savedUILocale: string | undefined; 181 beforeEach(() => savedUILocale = ts.getUILocale()); 182 afterEach(() => ts.setUILocale(savedUILocale)); 183 184 function verifyValidateLocale(locale: string, expectedToReadFile: boolean) { 185 it(`Verifying ${locale} ${expectedToReadFile ? "reads" : "does not read"} file`, () => { 186 const errors: ts.Diagnostic[] = []; 187 ts.validateLocaleAndSetLanguage(locale, { 188 getExecutingFilePath: () => "/tsc.js", 189 resolvePath: ts.identity, 190 fileExists: fileName => { 191 assert.isTrue(expectedToReadFile, `Locale : ${locale} ${expectedToReadFile ? "should" : "should not"} check if ${fileName} exists.`); 192 return expectedToReadFile; 193 }, 194 readFile: fileName => { 195 assert.isTrue(expectedToReadFile, `Locale : ${locale} ${expectedToReadFile ? "should" : "should not"} read ${fileName}.`); 196 // Throw error here so that actual change to localized diagnostics messages doesnt take place 197 throw new Error("cannot read file"); 198 } 199 }, errors); 200 }); 201 } 202 ts.supportedLocaleDirectories.forEach(locale => verifyValidateLocale(locale, /*expectedToReadFile*/ true)); 203 ["en", "en-us"].forEach(locale => verifyValidateLocale(locale, /*expectedToReadFile*/ false)); 204}); 205 206describe("unittests:: Public APIs :: forEachChild of @param comments in JSDoc", () => { 207 it("finds correct children", () => { 208 const content = ` 209/** 210 * @param The {@link TypeReferencesInAedoc}. 211 */ 212var x 213`; 214 const sourceFile = ts.createSourceFile("/file.ts", content, ts.ScriptTarget.ESNext, /*setParentNodes*/ true); 215 const paramTag = sourceFile.getChildren()[0].getChildren()[0].getChildren()[0].getChildren()[0]; 216 const kids = paramTag.getChildren(); 217 const seen: Set<ts.Node> = new Set(); 218 ts.forEachChild(paramTag, n => { 219 assert.strictEqual(/*actual*/ false, seen.has(n), "Found a duplicate-added child"); 220 seen.add(n); 221 }); 222 assert.equal(5, kids.length); 223 }); 224}); 225 226describe("unittests:: Public APIs:: getChild* methods on EndOfFileToken with JSDoc", () => { 227 it("finds correct children", () => { 228 const content = ` 229/** jsdoc comment attached to EndOfFileToken */ 230`; 231 const sourceFile = ts.createSourceFile("/file.ts", content, ts.ScriptTarget.ESNext, /*setParentNodes*/ true); 232 const endOfFileToken = sourceFile.getChildren()[1]; 233 assert.equal(endOfFileToken.getChildren().length, 1); 234 assert.equal(endOfFileToken.getChildCount(), 1); 235 assert.notEqual(endOfFileToken.getChildAt(0), /*expected*/ undefined); 236 }); 237}); 238