test(language-service): remove MockData from MockTypescriptHost (#32752)
Remove MockData from the constructor parameters of MockTypescriptHost since the entire Tour of Heroes (TOH) project is now loaded from disk. Added a new method `reset()` to MockTypescriptHost that is necessary to reset the state of the project before each spec if run to make sure previous overrides are cleared. PR Close #32752
This commit is contained in:
parent
0450f39625
commit
f3859ff2b9
|
@ -11,15 +11,16 @@ import * as ts from 'typescript';
|
|||
import {createLanguageService} from '../src/language_service';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('completions', () => {
|
||||
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
let service = ts.createLanguageService(mockHost);
|
||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||
let ngService = createLanguageService(ngHost);
|
||||
|
||||
beforeEach(() => { mockHost.reset(); });
|
||||
|
||||
it('should be able to get entity completions',
|
||||
() => { expectContains('/app/test.ng', 'entity-amp', '&', '>', '<', 'ι'); });
|
||||
|
||||
|
@ -44,7 +45,6 @@ describe('completions', () => {
|
|||
const fileName = '/app/test.ng';
|
||||
mockHost.override(fileName, ' > {{tle<\n {{retl ><bel/beled}}di>\n la</b </d &a ');
|
||||
expect(() => ngService.getCompletionsAt(fileName, 31)).not.toThrow();
|
||||
mockHost.override(fileName, undefined !);
|
||||
});
|
||||
|
||||
it('should be able to infer the type of a ngForOf', () => {
|
||||
|
@ -68,7 +68,7 @@ describe('completions', () => {
|
|||
street: string
|
||||
}
|
||||
|
||||
@Component({template: '<div *ngFor="let person of people | async">{{person.~{name}name}}</div'})
|
||||
@Component({template: '<div *ngFor="let person of people | async">{{person.~{name}name}}</div>'})
|
||||
export class MyComponent {
|
||||
people: Promise<Person[]>;
|
||||
}`);
|
||||
|
@ -92,41 +92,38 @@ describe('completions', () => {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const originalContent = mockHost.getFileContent(fileName) !;
|
||||
|
||||
// For each character in the file, add it to the file and request a completion after it.
|
||||
for (let index = 0, len = originalContent.length; index < len; index++) {
|
||||
const content = originalContent.substr(0, index);
|
||||
mockHost.override(fileName, content);
|
||||
tryCompletionsAt(index);
|
||||
}
|
||||
const originalContent = mockHost.getFileContent(fileName) !;
|
||||
|
||||
// For the complete file, try to get a completion at every character.
|
||||
mockHost.override(fileName, originalContent);
|
||||
for (let index = 0, len = originalContent.length; index < len; index++) {
|
||||
tryCompletionsAt(index);
|
||||
}
|
||||
|
||||
// Delete random characters in the file until we get an empty file.
|
||||
let content = originalContent;
|
||||
while (content.length > 0) {
|
||||
const deleteIndex = Math.floor(Math.random() * content.length);
|
||||
content = content.slice(0, deleteIndex - 1) + content.slice(deleteIndex + 1);
|
||||
mockHost.override(fileName, content);
|
||||
|
||||
const requestIndex = Math.floor(Math.random() * content.length);
|
||||
tryCompletionsAt(requestIndex);
|
||||
}
|
||||
|
||||
// Build up the string from zero asking for a completion after every char
|
||||
buildUp(originalContent, (text, position) => {
|
||||
mockHost.override(fileName, text);
|
||||
tryCompletionsAt(position);
|
||||
});
|
||||
} finally {
|
||||
mockHost.override(fileName, undefined !);
|
||||
// For each character in the file, add it to the file and request a completion after it.
|
||||
for (let index = 0, len = originalContent.length; index < len; index++) {
|
||||
const content = originalContent.substr(0, index);
|
||||
mockHost.override(fileName, content);
|
||||
tryCompletionsAt(index);
|
||||
}
|
||||
|
||||
// For the complete file, try to get a completion at every character.
|
||||
mockHost.override(fileName, originalContent);
|
||||
for (let index = 0, len = originalContent.length; index < len; index++) {
|
||||
tryCompletionsAt(index);
|
||||
}
|
||||
|
||||
// Delete random characters in the file until we get an empty file.
|
||||
let content = originalContent;
|
||||
while (content.length > 0) {
|
||||
const deleteIndex = Math.floor(Math.random() * content.length);
|
||||
content = content.slice(0, deleteIndex - 1) + content.slice(deleteIndex + 1);
|
||||
mockHost.override(fileName, content);
|
||||
|
||||
const requestIndex = Math.floor(Math.random() * content.length);
|
||||
tryCompletionsAt(requestIndex);
|
||||
}
|
||||
|
||||
// Build up the string from zero asking for a completion after every char
|
||||
buildUp(originalContent, (text, position) => {
|
||||
mockHost.override(fileName, text);
|
||||
tryCompletionsAt(position);
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
|
@ -138,7 +135,7 @@ describe('completions', () => {
|
|||
template: '~{inside-template}'
|
||||
})
|
||||
export class MyComponent {
|
||||
|
||||
|
||||
}`);
|
||||
|
||||
expectContains(fileName, 'inside-template', 'h1');
|
||||
|
|
|
@ -12,7 +12,6 @@ import {createLanguageService} from '../src/language_service';
|
|||
import {LanguageService} from '../src/types';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('definitions', () => {
|
||||
|
@ -23,7 +22,7 @@ describe('definitions', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
// Create a new mockHost every time to reset any files that are overridden.
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
service = ts.createLanguageService(mockHost);
|
||||
ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||
ngService = createLanguageService(ngHost);
|
||||
|
|
|
@ -10,7 +10,6 @@ import * as ts from 'typescript';
|
|||
import {createLanguageService} from '../src/language_service';
|
||||
import * as ng from '../src/types';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
/**
|
||||
|
@ -32,7 +31,7 @@ describe('diagnostics', () => {
|
|||
let ngLS: ng.LanguageService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
tsLS = ts.createLanguageService(mockHost);
|
||||
ngHost = new TypeScriptServiceHost(mockHost, tsLS);
|
||||
ngLS = createLanguageService(ngHost);
|
||||
|
|
|
@ -12,7 +12,6 @@ import {createLanguageService} from '../src/language_service';
|
|||
import {LanguageService} from '../src/types';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('hover', () => {
|
||||
|
@ -22,7 +21,7 @@ describe('hover', () => {
|
|||
let ngLS: LanguageService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
tsLS = ts.createLanguageService(mockHost);
|
||||
ngLSHost = new TypeScriptServiceHost(mockHost, tsLS);
|
||||
ngLS = createLanguageService(ngLSHost);
|
||||
|
|
|
@ -11,11 +11,10 @@ import * as ts from 'typescript';
|
|||
import {createLanguageService} from '../src/language_service';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('service without angular', () => {
|
||||
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
mockHost.forgetAngular();
|
||||
let service = ts.createLanguageService(mockHost);
|
||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||
|
@ -32,4 +31,4 @@ describe('service without angular', () => {
|
|||
it('should not crash a get definition',
|
||||
() => expect(() => ngService.getDefinitionAt(fileName, position)).not.toThrow());
|
||||
it('should not crash a hover', () => expect(() => ngService.getHoverAt(fileName, position)));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,11 +9,9 @@
|
|||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {createLanguageService} from '../src/language_service';
|
||||
import {ReflectorHost} from '../src/reflector_host';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('reflector_host_spec', () => {
|
||||
|
@ -23,7 +21,7 @@ describe('reflector_host_spec', () => {
|
|||
const originalJoin = path.join;
|
||||
const originalPosixJoin = path.posix.join;
|
||||
const mockHost =
|
||||
new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh, 'node_modules', {
|
||||
new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], 'node_modules', {
|
||||
...path,
|
||||
join: (...args: string[]) => originalJoin.apply(path, args),
|
||||
posix:
|
||||
|
@ -42,7 +40,7 @@ describe('reflector_host_spec', () => {
|
|||
});
|
||||
|
||||
it('should use module resolution cache', () => {
|
||||
const mockHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const mockHost = new MockTypescriptHost(['/app/main.ts']);
|
||||
// TypeScript relies on `ModuleResolutionHost.fileExists()` to perform
|
||||
// module resolution, so spy on this method to determine how many times
|
||||
// it's called.
|
||||
|
|
|
@ -12,7 +12,6 @@ import {createLanguageService} from '../src/language_service';
|
|||
import {LanguageService} from '../src/types';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('references', () => {
|
||||
|
@ -23,7 +22,7 @@ describe('references', () => {
|
|||
let ngService: LanguageService = createLanguageService(undefined !);
|
||||
|
||||
beforeEach(() => {
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
service = ts.createLanguageService(mockHost, documentRegistry);
|
||||
ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||
ngService = createLanguageService(ngHost);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import * as ts from 'typescript';
|
||||
import {getClassDeclFromDecoratorProp} from '../src/template';
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('getClassDeclFromTemplateNode', () => {
|
||||
|
@ -35,7 +34,7 @@ describe('getClassDeclFromTemplateNode', () => {
|
|||
|
||||
|
||||
it('should return class declaration for AppComponent', () => {
|
||||
const host = new MockTypescriptHost(['/app/app.component.ts'], toh);
|
||||
const host = new MockTypescriptHost(['/app/app.component.ts']);
|
||||
const tsLS = ts.createLanguageService(host);
|
||||
const sourceFile = tsLS.getProgram() !.getSourceFile('/app/app.component.ts');
|
||||
expect(sourceFile).toBeTruthy();
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export const toh = {
|
||||
'foo.ts': `export * from './app/app.component.ts';`,
|
||||
app: {
|
||||
'app.component.ts': `import { Component, NgModule } from '@angular/core';
|
||||
|
||||
export class Hero {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: \`~{empty}
|
||||
<~{start-tag}h~{start-tag-after-h}1~{start-tag-h1} ~{h1-after-space}>~{h1-content} {{~{sub-start}title~{sub-end}}}</h1>
|
||||
~{after-h1}<h2>{{~{h2-hero}hero.~{h2-name}name}} details!</h2>
|
||||
<div><label>id: </label>{{~{label-hero}hero.~{label-id}id}}</div>
|
||||
<div ~{div-attributes}>
|
||||
<label>name: </label>
|
||||
</div>
|
||||
&~{entity-amp}amp;
|
||||
\`
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Tour of Heroes';
|
||||
hero: Hero = {
|
||||
id: 1,
|
||||
name: 'Windstorm'
|
||||
};
|
||||
private internal: string;
|
||||
}`,
|
||||
'main.ts': `
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AppComponent } from './app.component';
|
||||
import { CaseIncompleteOpen, CaseMissingClosing, CaseUnknown, Pipes, TemplateReference, NoValueAttribute,
|
||||
AttributeBinding, StringModel, NumberModel, PropertyBinding, EventBinding, TwoWayBinding, EmptyInterpolation,
|
||||
ForOfEmpty, ForLetIEqual, ForOfLetEmpty, ForUsingComponent, References, TestComponent} from './parsing-cases';
|
||||
import { WrongFieldReference, WrongSubFieldReference, PrivateReference, ExpectNumericType, LowercasePipe } from './expression-cases';
|
||||
import { UnknownPeople, UnknownEven, UnknownTrackBy } from './ng-for-cases';
|
||||
import { ShowIf } from './ng-if-cases';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, FormsModule],
|
||||
declarations: [AppComponent, CaseIncompleteOpen, CaseMissingClosing, CaseUnknown, Pipes, TemplateReference, NoValueAttribute,
|
||||
AttributeBinding, StringModel, NumberModel, PropertyBinding, EventBinding, TwoWayBinding, EmptyInterpolation, ForOfEmpty, ForOfLetEmpty,
|
||||
ForLetIEqual, ForUsingComponent, References, TestComponent, WrongFieldReference, WrongSubFieldReference, PrivateReference,
|
||||
ExpectNumericType, UnknownPeople, UnknownEven, UnknownTrackBy, ShowIf, LowercasePipe]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
declare function bootstrap(v: any): void;
|
||||
|
||||
bootstrap(AppComponent);
|
||||
`,
|
||||
'parsing-cases.ts': `
|
||||
import {Component, Directive, Input, Output, EventEmitter} from '@angular/core';
|
||||
import {Hero} from './app.component';
|
||||
|
||||
@Component({template: '<h1>Some <~{incomplete-open-lt}a~{incomplete-open-a} ~{incomplete-open-attr} text</h1>'})
|
||||
export class CaseIncompleteOpen {}
|
||||
|
||||
@Component({template: '<h1>Some <a> ~{missing-closing} text</h1>'})
|
||||
export class CaseMissingClosing {}
|
||||
|
||||
@Component({template: '<h1>Some <unknown ~{unknown-element}> text</h1>'})
|
||||
export class CaseUnknown {}
|
||||
|
||||
@Component({template: '<h1>{{data | ~{before-pipe}lowe~{in-pipe}rcase~{after-pipe} }}'})
|
||||
export class Pipes {
|
||||
data = 'Some string';
|
||||
}
|
||||
|
||||
@Component({template: '<h1 h~{no-value-attribute}></h1>'})
|
||||
export class NoValueAttribute {}
|
||||
|
||||
|
||||
@Component({template: '<h1 model="~{attribute-binding-model}test"></h1>'})
|
||||
export class AttributeBinding {
|
||||
test: string;
|
||||
}
|
||||
|
||||
@Component({template: '<h1 [model]="~{property-binding-model}test"></h1>'})
|
||||
export class PropertyBinding {
|
||||
test: string;
|
||||
}
|
||||
|
||||
@Component({template: '<h1 (model)="~{event-binding-model}modelChanged()"></h1>'})
|
||||
export class EventBinding {
|
||||
test: string;
|
||||
|
||||
modelChanged() {}
|
||||
}
|
||||
|
||||
@Component({template: '<h1 [(model)]="~{two-way-binding-model}test"></h1>'})
|
||||
export class TwoWayBinding {
|
||||
test: string;
|
||||
}
|
||||
|
||||
@Directive({selector: '[string-model]'})
|
||||
export class StringModel {
|
||||
@Input() model: string;
|
||||
@Output() modelChanged: EventEmitter<string>;
|
||||
}
|
||||
|
||||
@Directive({selector: '[number-model]'})
|
||||
export class NumberModel {
|
||||
@Input('inputAlias') model: number;
|
||||
@Output('outputAlias') modelChanged: EventEmitter<number>;
|
||||
}
|
||||
|
||||
interface Person {
|
||||
name: string;
|
||||
age: number
|
||||
}
|
||||
|
||||
@Component({template: '<div *ngFor="~{for-empty}"></div>'})
|
||||
export class ForOfEmpty {}
|
||||
|
||||
@Component({template: '<div *ngFor="let ~{for-let-empty}"></div>'})
|
||||
export class ForOfLetEmpty {}
|
||||
|
||||
@Component({template: '<div *ngFor="let i = ~{for-let-i-equal}"></div>'})
|
||||
export class ForLetIEqual {}
|
||||
|
||||
@Component({template: '<div *ngFor="~{for-let}let ~{for-person}person ~{for-of}of ~{for-people}people"> <span>Name: {{~{for-interp-person}person.~{for-interp-name}name}}</span><span>Age: {{person.~{for-interp-age}age}}</span></div>'})
|
||||
export class ForUsingComponent {
|
||||
people: Person[];
|
||||
}
|
||||
|
||||
@Component({template: '<div #div> <test-comp #test1> {{~{test-comp-content}}} {{test1.~{test-comp-after-test}name}} {{div.~{test-comp-after-div}.innerText}} </test-comp> </div> <test-comp #test2></test-comp>'})
|
||||
export class References {}
|
||||
|
||||
~{start-test-comp}@Component({selector: 'test-comp', template: '<div>Testing: {{name}}</div>'})
|
||||
export class TestComponent {
|
||||
«@Input('ᐱtcNameᐱ') name = 'test';»
|
||||
«@Output('ᐱtestᐱ') testEvent = new EventEmitter();»
|
||||
}~{end-test-comp}
|
||||
|
||||
@Component({templateUrl: 'test.ng'})
|
||||
export class TemplateReference {
|
||||
title = 'Some title';
|
||||
hero: Hero = {
|
||||
id: 1,
|
||||
name: 'Windstorm'
|
||||
};
|
||||
anyValue: any;
|
||||
myClick(event: any) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Component({template: '{{~{empty-interpolation}}}'})
|
||||
export class EmptyInterpolation {
|
||||
title = 'Some title';
|
||||
subTitle = 'Some sub title';
|
||||
}
|
||||
`,
|
||||
'expression-cases.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
export interface Person {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
@Component({template: '{{~{foo}foo~{foo-end}}}'})
|
||||
export class WrongFieldReference {
|
||||
bar = 'bar';
|
||||
}
|
||||
|
||||
@Component({template: '{{~{nam}person.nam~{nam-end}}}'})
|
||||
export class WrongSubFieldReference {
|
||||
person: Person = { name: 'Bob', age: 23 };
|
||||
}
|
||||
|
||||
@Component({template: '{{~{myField}myField~{myField-end}}}'})
|
||||
export class PrivateReference {
|
||||
private myField = 'My Field';
|
||||
}
|
||||
|
||||
@Component({template: '{{~{mod}"a" ~{mod-end}% 2}}'})
|
||||
export class ExpectNumericType {}
|
||||
|
||||
@Component({template: '{{ (name | lowercase).~{string-pipe}substring }}'})
|
||||
export class LowercasePipe {
|
||||
name: string;
|
||||
}
|
||||
`,
|
||||
'ng-for-cases.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
export interface Person {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
@Component({template: '<div *ngFor="let person of ~{people_1}people_1~{people_1-end}"> <span>{{person.name}}</span> </div>'})
|
||||
export class UnknownPeople {}
|
||||
|
||||
@Component({template: '<div ~{even_1}*ngFor="let person of people; let e = even_1"~{even_1-end}><span>{{person.name}}</span> </div>'})
|
||||
export class UnknownEven {
|
||||
people: Person[];
|
||||
}
|
||||
|
||||
@Component({template: '<div *ngFor="let person of people; trackBy ~{trackBy_1}trackBy_1~{trackBy_1-end}"><span>{{person.name}}</span> </div>'})
|
||||
export class UnknownTrackBy {
|
||||
people: Person[];
|
||||
}
|
||||
`,
|
||||
'ng-if-cases.ts': `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({template: '<div ~{implicit}*ngIf="show; let l=unknown"~{implicit-end}>Showing now!</div>'})
|
||||
export class ShowIf {
|
||||
show = false;
|
||||
}
|
||||
`,
|
||||
'test.ng': `~{empty}
|
||||
<~{start-tag}h~{start-tag-after-h}1~{start-tag-h1} ~{h1-after-space}>~{h1-content} {{~{sub-start}title~{sub-end}}}</h1>
|
||||
~{after-h1}<h2>{{~{h2-hero}hero.~{h2-name}name}} details!</h2>
|
||||
<div><label>id: </label>{{~{label-hero}hero.~{label-id}id}}</div>
|
||||
<div ~{div-attributes}>
|
||||
<label>name: </label>
|
||||
</div>
|
||||
&~{entity-amp}amp;
|
||||
`,
|
||||
'test.css': `
|
||||
body, html {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
}
|
||||
};
|
|
@ -14,12 +14,6 @@ import * as ts from 'typescript';
|
|||
|
||||
import {Span} from '../src/types';
|
||||
|
||||
export type MockData = string | MockDirectory;
|
||||
|
||||
export type MockDirectory = {
|
||||
[name: string]: MockData | undefined;
|
||||
};
|
||||
|
||||
const angularts = /@angular\/(\w|\/|-)+\.tsx?$/;
|
||||
const rxjsts = /rxjs\/(\w|\/)+\.tsx?$/;
|
||||
const rxjsmetadata = /rxjs\/(\w|\/)+\.metadata\.json?$/;
|
||||
|
@ -41,29 +35,6 @@ const missingCache = new Set<string>([
|
|||
'/node_modules/@angular/core/src/reflection/platform_reflection_capabilities.metadata.json',
|
||||
'/node_modules/@angular/forms/src/directives/form_interface.metadata.json',
|
||||
]);
|
||||
const cacheUsed = new Set<string>();
|
||||
const reportedMissing = new Set<string>();
|
||||
|
||||
/**
|
||||
* The cache is valid if all the returned entries are empty.
|
||||
*/
|
||||
export function validateCache(): {exists: string[], unused: string[], reported: string[]} {
|
||||
const exists: string[] = [];
|
||||
const unused: string[] = [];
|
||||
for (const fileName of missingCache) {
|
||||
if (fs.existsSync(fileName)) {
|
||||
exists.push(fileName);
|
||||
}
|
||||
if (!cacheUsed.has(fileName)) {
|
||||
unused.push(fileName);
|
||||
}
|
||||
}
|
||||
return {
|
||||
exists,
|
||||
unused,
|
||||
reported: Array.from(reportedMissing),
|
||||
};
|
||||
}
|
||||
|
||||
function isFile(path: string) {
|
||||
return fs.statSync(path).isFile();
|
||||
|
@ -121,7 +92,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
|||
private readonly fileCache = new Map<string, string|undefined>();
|
||||
|
||||
constructor(
|
||||
private readonly scriptNames: string[], _: MockData,
|
||||
private readonly scriptNames: string[],
|
||||
private readonly node_modules: string = 'node_modules',
|
||||
private readonly myPath: typeof path = path) {
|
||||
const support = setup();
|
||||
|
@ -222,6 +193,14 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
|||
if (content) return removeReferenceMarkers(removeLocationMarkers(content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the project to its original state, effectively removing all overrides.
|
||||
*/
|
||||
reset() {
|
||||
this.overrides.clear();
|
||||
this.overrideDirectory.clear();
|
||||
}
|
||||
|
||||
private getRawFileContent(fileName: string): string|undefined {
|
||||
if (this.overrides.has(fileName)) {
|
||||
return this.overrides.get(fileName);
|
||||
|
@ -232,7 +211,6 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
|||
return fs.readFileSync(this.myPath.join(path.dirname(libPath), basename), 'utf8');
|
||||
}
|
||||
if (missingCache.has(fileName)) {
|
||||
cacheUsed.add(fileName);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -250,8 +228,6 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
|
|||
return content;
|
||||
} else {
|
||||
missingCache.add(fileName);
|
||||
reportedMissing.add(fileName);
|
||||
cacheUsed.add(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
import 'reflect-metadata';
|
||||
import * as ts from 'typescript';
|
||||
import {create} from '../src/ts_plugin';
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
|
||||
describe('plugin', () => {
|
||||
const mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
||||
const mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']);
|
||||
const service = ts.createLanguageService(mockHost);
|
||||
const program = service.getProgram();
|
||||
const plugin = createPlugin(service, mockHost);
|
||||
|
|
|
@ -11,13 +11,12 @@ import * as ts from 'typescript';
|
|||
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost, findDirectiveMetadataByName} from './test_utils';
|
||||
|
||||
|
||||
describe('TypeScriptServiceHost', () => {
|
||||
it('should be able to create a typescript host and analyze modules', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const analyzedModules = ngLSHost.getAnalyzedModules();
|
||||
|
@ -34,7 +33,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should be able to analyze modules without a tsconfig.json file', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['foo.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['foo.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const analyzedModules = ngLSHost.getAnalyzedModules();
|
||||
|
@ -45,7 +44,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should not throw if there is no script names', () => {
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLSHost = new MockTypescriptHost([]);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const analyzedModules = ngLSHost.getAnalyzedModules();
|
||||
|
@ -57,7 +56,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
|
||||
it('should clear the caches if program changes', () => {
|
||||
// First create a TypescriptHost with empty script names
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLSHost = new MockTypescriptHost([]);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(ngLSHost.getAnalyzedModules().ngModules).toEqual([]);
|
||||
|
@ -71,7 +70,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should throw if getSourceFile is called on non-TS file', () => {
|
||||
const tsLSHost = new MockTypescriptHost([], toh);
|
||||
const tsLSHost = new MockTypescriptHost([]);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
expect(() => {
|
||||
|
@ -80,7 +79,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should be able to find a single inline template', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/app.component.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/app.component.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const templates = ngLSHost.getTemplates('/app/app.component.ts');
|
||||
|
@ -90,7 +89,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should be able to find multiple inline templates', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/parsing-cases.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/parsing-cases.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const templates = ngLSHost.getTemplates('/app/parsing-cases.ts');
|
||||
|
@ -98,7 +97,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should be able to find external template', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
ngLSHost.getAnalyzedModules();
|
||||
|
@ -110,7 +109,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
|
||||
// https://github.com/angular/angular/issues/32301
|
||||
it('should clear caches when program changes', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const fileName = '/app/app.component.ts';
|
||||
|
@ -162,7 +161,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should not clear caches when external template changes', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/main.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const oldModules = ngLSHost.getAnalyzedModules();
|
||||
|
@ -172,7 +171,7 @@ describe('TypeScriptServiceHost', () => {
|
|||
});
|
||||
|
||||
it('should get the correct StaticSymbol for a Directive', () => {
|
||||
const tsLSHost = new MockTypescriptHost(['/app/app.component.ts', '/app/main.ts'], toh);
|
||||
const tsLSHost = new MockTypescriptHost(['/app/app.component.ts', '/app/main.ts']);
|
||||
const tsLS = ts.createLanguageService(tsLSHost);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
ngLSHost.getAnalyzedModules(); // modules are analyzed lazily
|
||||
|
|
Loading…
Reference in New Issue