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