diff --git a/packages/language-service/test/diagnostics_spec.ts b/packages/language-service/test/diagnostics_spec.ts index d0ffac41cf..0b33ae49a6 100644 --- a/packages/language-service/test/diagnostics_spec.ts +++ b/packages/language-service/test/diagnostics_spec.ts @@ -24,6 +24,10 @@ import {MockTypescriptHost} from './test_utils'; * as well. */ +const EXPRESSION_CASES = '/app/expression-cases.ts'; +const NG_FOR_CASES = '/app/ng-for-cases.ts'; +const NG_IF_CASES = '/app/ng-if-cases.ts'; + describe('diagnostics', () => { let mockHost: MockTypescriptHost; let ngHost: TypeScriptServiceHost; @@ -57,6 +61,62 @@ describe('diagnostics', () => { } }); + describe('in expression-cases.ts', () => { + it('should report access to an unknown field', () => { + const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'foo' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); + }); + + it('should report access to an unknown sub-field', () => { + const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'nam' is not defined. 'Person' does not contain such a member`); + }); + + it('should report access to a private member', () => { + const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText); + expect(diags).toContain(`Identifier 'myField' refers to a private member of the component`); + }); + + it('should report numeric operator errors', () => { + const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText); + expect(diags).toContain('Expected a numeric type'); + }); + }); + + describe('in ng-for-cases.ts', () => { + it('should report an unknown field', () => { + const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'people_1' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); + }); + + it('should report an unknown context reference', () => { + const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText); + expect(diags).toContain(`The template context does not define a member called 'even_1'`); + }); + + it('should report an unknown value in a key expression', () => { + const diags = ngLS.getDiagnostics(NG_FOR_CASES).map(d => d.messageText); + expect(diags).toContain( + `Identifier 'trackBy_1' is not defined. ` + + `The component declaration, template variable declarations, ` + + `and element references do not contain such a member`); + }); + }); + + describe('in ng-if-cases.ts', () => { + it('should report an implicit context reference', () => { + const diags = ngLS.getDiagnostics(NG_IF_CASES).map(d => d.messageText); + expect(diags).toContain(`The template context does not define a member called 'unknown'`); + }); + }); + // #17611 it('should not report diagnostic on iteration of any', () => { const fileName = '/app/test.ng'; @@ -487,7 +547,7 @@ describe('diagnostics', () => { describe('templates', () => { it('should report errors for invalid templateUrls', () => { const fileName = mockHost.addCode(` - @Component({ + @Component({ templateUrl: '«notAFile»', }) export class MyComponent {}`); @@ -506,10 +566,10 @@ describe('diagnostics', () => { it('should not report errors for valid templateUrls', () => { const fileName = mockHost.addCode(` - @Component({ + @Component({ templateUrl: './test.ng', - }) - export class MyComponent {}`); + }) + export class MyComponent {}`); const diagnostics = ngLS.getDiagnostics(fileName) !; const urlDiagnostic = diff --git a/packages/language-service/test/language_service_spec.ts b/packages/language-service/test/language_service_spec.ts index 1898a22038..66bc5b9634 100644 --- a/packages/language-service/test/language_service_spec.ts +++ b/packages/language-service/test/language_service_spec.ts @@ -14,21 +14,31 @@ import {TypeScriptServiceHost} from '../src/typescript_host'; import {MockTypescriptHost} from './test_utils'; describe('service without angular', () => { - let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']); - mockHost.forgetAngular(); - let service = ts.createLanguageService(mockHost); - let ngHost = new TypeScriptServiceHost(mockHost, service); - let ngService = createLanguageService(ngHost); + const mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts']); + const service = ts.createLanguageService(mockHost); + const ngHost = new TypeScriptServiceHost(mockHost, service); + const ngService = createLanguageService(ngHost); const fileName = '/app/test.ng'; const position = mockHost.getLocationMarkerFor(fileName, 'h1-content').start; + beforeEach(() => { mockHost.reset(); }); + it('should not crash a get template references', - () => expect(() => ngService.getTemplateReferences())); + () => { expect(() => ngService.getTemplateReferences()).not.toThrow(); }); it('should not crash a get diagnostics', - () => expect(() => ngService.getDiagnostics(fileName)).not.toThrow()); + () => { expect(() => ngService.getDiagnostics(fileName)).not.toThrow(); }); + it('should not crash a completion', - () => expect(() => ngService.getCompletionsAt(fileName, position)).not.toThrow()); + () => { expect(() => ngService.getCompletionsAt(fileName, position)).not.toThrow(); }); + 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))); + () => { expect(() => ngService.getDefinitionAt(fileName, position)).not.toThrow(); }); + + it('should not crash a hover', + () => { expect(() => ngService.getHoverAt(fileName, position)).not.toThrow(); }); + + it('should not crash with an incomplete class', () => { + mockHost.addCode('\nexport class'); + expect(() => ngHost.getAnalyzedModules()).not.toThrow(); + }); }); diff --git a/packages/language-service/test/ts_plugin_spec.ts b/packages/language-service/test/ts_plugin_spec.ts index 781db1a9fa..fbe2063a8f 100644 --- a/packages/language-service/test/ts_plugin_spec.ts +++ b/packages/language-service/test/ts_plugin_spec.ts @@ -139,51 +139,6 @@ describe('plugin', () => { }); describe('for semantic errors', () => { - it('should report access to an unknown field', () => { - expectSemanticError( - '/app/expression-cases.ts', 'foo', - 'Identifier \'foo\' is not defined. The component declaration, template variable declarations, and element references do not contain such a member'); - }); - it('should report access to an unknown sub-field', () => { - expectSemanticError( - '/app/expression-cases.ts', 'nam', - 'Identifier \'nam\' is not defined. \'Person\' does not contain such a member'); - }); - it('should report access to a private member', () => { - expectSemanticError( - '/app/expression-cases.ts', 'myField', - 'Identifier \'myField\' refers to a private member of the component'); - }); - it('should report numeric operator errors', () => { - expectSemanticError('/app/expression-cases.ts', 'mod', 'Expected a numeric type'); - }); - describe('in ngFor', () => { - function expectError(locationMarker: string, message: string) { - expectSemanticError('/app/ng-for-cases.ts', locationMarker, message); - } - it('should report an unknown field', () => { - expectError( - 'people_1', - 'Identifier \'people_1\' is not defined. The component declaration, template variable declarations, and element references do not contain such a member'); - }); - it('should report an unknown context reference', () => { - expectError('even_1', `The template context does not define a member called 'even_1'`); - }); - it('should report an unknown value in a key expression', () => { - expectError( - 'trackBy_1', - 'Identifier \'trackBy_1\' is not defined. The component declaration, template variable declarations, and element references do not contain such a member'); - }); - }); - describe('in ngIf', () => { - function expectError(locationMarker: string, message: string) { - expectSemanticError('/app/ng-if-cases.ts', locationMarker, message); - } - it('should report an implicit context reference', () => { - expectError('implicit', `The template context does not define a member called 'unknown'`); - }); - }); - describe(`with config 'angularOnly = true`, () => { const ngLS = createPlugin(service, mockHost, {angularOnly: true}); it('should not report template errors on TOH', () => { @@ -236,21 +191,6 @@ describe('plugin', () => { locationMarker, plugin.getCompletionsAtPosition(fileName, marker.start, undefined) !, ...names); } - - function expectSemanticError(fileName: string, locationMarker: string, message: string) { - const marker = mockHost.getLocationMarkerFor(fileName, locationMarker); - const errors = plugin.getSemanticDiagnostics(fileName); - for (const error of errors) { - if (error.messageText.toString().indexOf(message) >= 0) { - expect(error.start).toEqual(marker.start); - expect(error.length).toEqual(marker.length); - return; - } - } - throw new Error(`Expected error messages to contain ${message}, in messages:\n ${errors - .map(e => e.messageText.toString()) - .join(',\n ')}`); - } });