• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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