fix(language-service): Turn on strict mode for test project (#32783)

This is the last part in refactoring of the test project.
This PR turns on strict mode for typechecking and fixed tests that
fail under this mode.

PR Close #32783
This commit is contained in:
Keen Yee Liau 2019-09-19 17:04:02 -07:00 committed by atscott
parent 9e7aa60ae7
commit 28358b6395
8 changed files with 70 additions and 44 deletions

View File

@ -152,10 +152,9 @@ describe('completions', () => {
});
it('should respect paths configuration', () => {
mockHost.overrideOptions(options => {
options.baseUrl = '/app';
options.paths = {'bar/*': ['foo/bar/*']};
return options;
mockHost.overrideOptions({
baseUrl: '/app',
paths: {'bar/*': ['foo/bar/*']},
});
mockHost.addScript('/app/foo/bar/shared.ts', `
export interface Node {

View File

@ -191,7 +191,7 @@ describe('diagnostics', () => {
expect(() => ngLS.getDiagnostics(fileName)).not.toThrow();
});
it('should not report an error for sub-types of string', () => {
it('should not report an error for sub-types of string in non-strict mode', () => {
const fileName = '/app/app.component.ts';
mockHost.override(fileName, `
import { Component } from '@angular/core';
@ -202,13 +202,16 @@ describe('diagnostics', () => {
export class AppComponent {
something: 'foo' | 'bar';
}`);
mockHost.overrideOptions({
strict: false, // TODO: this test fails in strict mode
});
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(fileName);
expect(ngDiags).toEqual([]);
});
it('should not report an error for sub-types of number', () => {
it('should not report an error for sub-types of number in non-strict mode', () => {
const fileName = '/app/app.component.ts';
mockHost.override(fileName, `
import { Component } from '@angular/core';
@ -219,6 +222,9 @@ describe('diagnostics', () => {
export class AppComponent {
something: 123 | 456;
}`);
mockHost.overrideOptions({
strict: false, // TODO: This test fails in strict mode
});
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(fileName);
@ -233,7 +239,7 @@ describe('diagnostics', () => {
@Component({
template: '<div (click)="onClick"></div>'
})
export class MyComponent {
export class AppComponent {
onClick() { }
}`);
const diagnostics = ngLS.getDiagnostics(fileName) !;
@ -245,7 +251,7 @@ describe('diagnostics', () => {
});
// #13412
it('should not report an error for using undefined', () => {
it('should not report an error for using undefined under non-strict mode', () => {
const fileName = '/app/app.component.ts';
mockHost.override(fileName, `
import { Component } from '@angular/core';
@ -256,6 +262,9 @@ describe('diagnostics', () => {
export class AppComponent {
something = 'foo';
}`);
mockHost.overrideOptions({
strict: false, // TODO: This test fails in strict mode
});
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getDiagnostics(fileName);
@ -342,7 +351,10 @@ describe('diagnostics', () => {
})
export class AppComponent {}`);
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
expect(tsDiags.length).toBe(1);
const msgText = ts.flattenDiagnosticMessageText(tsDiags[0].messageText, '\n');
expect(msgText).toBe(
`Type 'null[]' is not assignable to type 'Provider[]'.\n Type 'null' is not assignable to type 'Provider'.`);
const ngDiags = ngLS.getDiagnostics(fileName);
expect(ngDiags.length).toBe(1);
const {messageText, start, length} = ngDiags[0];
@ -391,7 +403,7 @@ describe('diagnostics', () => {
\`
})
export class AppComponent {
fieldCount: number;
fieldCount: number = 0;
}`);
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
@ -401,9 +413,8 @@ describe('diagnostics', () => {
// Issue #15885
it('should be able to remove null and undefined from a type', () => {
mockHost.overrideOptions(options => {
options.strictNullChecks = true;
return options;
mockHost.overrideOptions({
strictNullChecks: true,
});
const fileName = '/app/app.component.ts';
mockHost.override(fileName, `
@ -441,9 +452,8 @@ describe('diagnostics', () => {
onSubmit(form: NgForm) {}
}`);
mockHost.addScript('/other/files/app/server.ts', 'export class Server {}');
mockHost.overrideOptions(options => {
options.baseUrl = '/other/files';
return options;
mockHost.overrideOptions({
baseUrl: '/other/files',
});
const tsDiags = tsLS.getSemanticDiagnostics(fileName);
expect(tsDiags).toEqual([]);
@ -568,10 +578,13 @@ describe('diagnostics', () => {
it('should not report errors for valid styleUrls', () => {
const fileName = '/app/app.component.ts';
mockHost.override(fileName, `
import {Component} from '@angular/core';
@Component({
template: '<div></div>',
styleUrls: ['./test.css', './test.css'],
})
export class MyComponent {}`);
export class AppComponent {}`);
const diagnostics = ngLS.getDiagnostics(fileName) !;
expect(diagnostics.length).toBe(0);

View File

@ -8,7 +8,7 @@
import {Component} from '@angular/core';
export class Hero {
export interface Hero {
id: number;
name: string;
}
@ -30,5 +30,5 @@ export class Hero {
export class AppComponent {
title = 'Tour of Heroes';
hero: Hero = {id: 1, name: 'Windstorm'};
private internal: string;
private internal: string = 'internal';
}

View File

@ -44,5 +44,5 @@ export class ExpectNumericType {
template: '{{ (name | lowercase).~{string-pipe}substring }}',
})
export class LowercasePipe {
name: string;
name: string = 'name';
}

View File

@ -29,7 +29,7 @@ export class UnknownPeople {
</div>`,
})
export class UnknownEven {
people: Person[];
people: Person[] = [];
}
@Component({
@ -39,5 +39,5 @@ export class UnknownEven {
</div>`,
})
export class UnknownTrackBy {
people: Person[];
people: Person[] = [];
}

View File

@ -49,21 +49,21 @@ export class NoValueAttribute {
template: '<h1 model="~{attribute-binding-model}test"></h1>',
})
export class AttributeBinding {
test: string;
test: string = 'test';
}
@Component({
template: '<h1 [model]="~{property-binding-model}test"></h1>',
})
export class PropertyBinding {
test: string;
test: string = 'test';
}
@Component({
template: '<h1 (model)="~{event-binding-model}modelChanged()"></h1>',
})
export class EventBinding {
test: string;
test: string = 'test';
modelChanged() {}
}
@ -72,23 +72,23 @@ export class EventBinding {
template: '<h1 [(model)]="~{two-way-binding-model}test"></h1>',
})
export class TwoWayBinding {
test: string;
test: string = 'test';
}
@Directive({
selector: '[string-model]',
})
export class StringModel {
@Input() model: string;
@Output() modelChanged: EventEmitter<string>;
@Input() model: string = 'model';
@Output() modelChanged: EventEmitter<string> = new EventEmitter();
}
@Directive({
selector: '[number-model]',
})
export class NumberModel {
@Input('inputAlias') model: number;
@Output('outputAlias') modelChanged: EventEmitter<number>;
@Input('inputAlias') model: number = 0;
@Output('outputAlias') modelChanged: EventEmitter<number> = new EventEmitter();
}
interface Person {
@ -122,7 +122,7 @@ export class ForLetIEqual {
</div>`,
})
export class ForUsingComponent {
people: Person[];
people: Person[] = [];
}
@Component({

View File

@ -0,0 +1,11 @@
{
"//00": "This file is used for IDE only, actual compilation options is in MockTypescriptHost in test_utils.ts",
"compilerOptions": {
"strict": true,
"experimentalDecorators": true,
"baseUrl": "../../../..",
"paths": {
"@angular/*": ["packages/*"]
}
}
}

View File

@ -79,6 +79,17 @@ function loadTourOfHeroes(): ReadonlyMap<string, string> {
}
const TOH = loadTourOfHeroes();
const COMPILER_OPTIONS: Readonly<ts.CompilerOptions> = {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
emitDecoratorMetadata: true,
experimentalDecorators: true,
removeComments: false,
noImplicitAny: false,
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
strict: true,
};
export class MockTypescriptHost implements ts.LanguageServiceHost {
private angularPath?: string;
@ -98,16 +109,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
const support = setup();
this.nodeModulesPath = path.posix.join(support.basePath, 'node_modules');
this.angularPath = path.posix.join(this.nodeModulesPath, '@angular');
this.options = {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
emitDecoratorMetadata: true,
experimentalDecorators: true,
removeComments: false,
noImplicitAny: false,
lib: ['lib.es2015.d.ts', 'lib.dom.d.ts'],
};
this.options = COMPILER_OPTIONS;
}
override(fileName: string, content: string) {
@ -133,12 +135,12 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
forgetAngular() { this.angularPath = undefined; }
overrideOptions(cb: (options: ts.CompilerOptions) => ts.CompilerOptions) {
this.options = cb((Object as any).assign({}, this.options));
overrideOptions(options: Partial<ts.CompilerOptions>) {
this.options = {...this.options, ...options};
this.projectVersion++;
}
getCompilationSettings(): ts.CompilerOptions { return this.options; }
getCompilationSettings(): ts.CompilerOptions { return {...this.options}; }
getProjectVersion(): string { return this.projectVersion.toString(); }
@ -199,6 +201,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
reset() {
this.overrides.clear();
this.overrideDirectory.clear();
this.options = COMPILER_OPTIONS;
}
private getRawFileContent(fileName: string): string|undefined {