Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Undecorated classes with decorated fields migration', () => {
shx.rm('-r', tmpDirPath);
});

it(`should add an import for Directive if there isn't one already`, async() => {
it(`should add an import for Directive if there isn't one already`, async () => {
writeFile('/index.ts', `
import { Input } from '@angular/core';

Expand All @@ -56,7 +56,7 @@ describe('Undecorated classes with decorated fields migration', () => {
.toContain(`import { Input, Directive } from '@angular/core';`);
});

it('should not change the imports if there is an import for Directive already', async() => {
it('should not change the imports if there is an import for Directive already', async () => {
writeFile('/index.ts', `
import { Directive, Input } from '@angular/core';

Expand All @@ -74,8 +74,9 @@ describe('Undecorated classes with decorated fields migration', () => {
.toContain(`import { Directive, Input } from '@angular/core';`);
});

it('should not generate conflicting imports there is a different `Directive` symbol', async() => {
writeFile('/index.ts', `
it('should not generate conflicting imports there is a different `Directive` symbol',
async () => {
writeFile('/index.ts', `
import { HostBinding } from '@angular/core';

export class Directive {
Expand All @@ -88,14 +89,14 @@ describe('Undecorated classes with decorated fields migration', () => {
}
`);

await runMigration();
const fileContent = tree.readContent('/index.ts');
expect(fileContent)
.toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`);
expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/);
});
await runMigration();
const fileContent = tree.readContent('/index.ts');
expect(fileContent)
.toContain(`import { HostBinding, Directive as Directive_1 } from '@angular/core';`);
expect(fileContent).toMatch(/@Directive_1\(\)\s+export class MyLibrarySharedBaseClass/);
});

it('should add @Directive to undecorated classes that have @Input', async() => {
it('should add @Directive to undecorated classes that have @Input', async () => {
writeFile('/index.ts', `
import { Input } from '@angular/core';

Expand All @@ -108,7 +109,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should not change decorated classes', async() => {
it('should not change decorated classes', async () => {
writeFile('/index.ts', `
import { Input, Component, Output, EventEmitter } from '@angular/core';

Expand All @@ -130,7 +131,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(content).toContain(`@Directive()\nexport class Child extends Base {`);
});

it('should add @Directive to undecorated classes that have @Output', async() => {
it('should add @Directive to undecorated classes that have @Output', async () => {
writeFile('/index.ts', `
import { Output, EventEmitter } from '@angular/core';

Expand All @@ -143,7 +144,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a host binding', async() => {
it('should add @Directive to undecorated classes that have a host binding', async () => {
writeFile('/index.ts', `
import { HostBinding } from '@angular/core';

Expand All @@ -159,7 +160,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a host listener', async() => {
it('should add @Directive to undecorated classes that have a host listener', async () => {
writeFile('/index.ts', `
import { HostListener } from '@angular/core';

Expand All @@ -175,7 +176,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a ViewChild query', async() => {
it('should add @Directive to undecorated classes that have a ViewChild query', async () => {
writeFile('/index.ts', `
import { ViewChild, ElementRef } from '@angular/core';

Expand All @@ -188,7 +189,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a ViewChildren query', async() => {
it('should add @Directive to undecorated classes that have a ViewChildren query', async () => {
writeFile('/index.ts', `
import { ViewChildren, ElementRef } from '@angular/core';

Expand All @@ -201,7 +202,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a ContentChild query', async() => {
it('should add @Directive to undecorated classes that have a ContentChild query', async () => {
writeFile('/index.ts', `
import { ContentChild, ElementRef } from '@angular/core';

Expand All @@ -214,7 +215,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated classes that have a ContentChildren query', async() => {
it('should add @Directive to undecorated classes that have a ContentChildren query', async () => {
writeFile('/index.ts', `
import { ContentChildren, ElementRef } from '@angular/core';

Expand All @@ -227,7 +228,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(tree.readContent('/index.ts')).toContain(`@Directive()\nexport class Base {`);
});

it('should add @Directive to undecorated derived classes of a migrated class', async() => {
it('should add @Directive to undecorated derived classes of a migrated class', async () => {
writeFile('/index.ts', `
import { Input, Directive, NgModule } from '@angular/core';

Expand Down Expand Up @@ -259,7 +260,7 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(fileContent).toMatch(/}\s+export class MyCompWrapped/);
});

it('should add @Directive to derived undecorated classes of abstract directives', async() => {
it('should add @Directive to derived undecorated classes of abstract directives', async () => {
writeFile('/index.ts', `
import { Input, Directive, NgModule } from '@angular/core';

Expand Down Expand Up @@ -292,6 +293,27 @@ describe('Undecorated classes with decorated fields migration', () => {
expect(fileContent).toMatch(/}\s+export class MyCompWrapped/);
});

it('should not throw if undecorated class extends from unresolved declaration', async () => {
writeFile('/lib.d.ts', `
// Fakes the ES5 error default lib types. Since we are in a virtual tree,
// the default lib types from TypeScript are not available.
interface ErrorConstructor {}
declare var Error: ErrorConstructor;
`);
writeFile('/index.ts', `
export class MyCustomErrorClass extends Error {}
`);

let error: any = null;
try {
await runMigration();
} catch (e) {
error = e;
}

expect(error).toBe(null);
});

function writeFile(filePath: string, contents: string) {
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export function findBaseClassDeclarations(node: ts.ClassDeclaration, typeChecker
break;
}
const symbol = typeChecker.getTypeAtLocation(baseTypes[0]).getSymbol();
if (!symbol || !ts.isClassDeclaration(symbol.valueDeclaration)) {
// Note: `ts.Symbol#valueDeclaration` can be undefined. TypeScript has an incorrect type
// for this: https://github.com/microsoft/TypeScript/issues/24706.
if (!symbol || !symbol.valueDeclaration || !ts.isClassDeclaration(symbol.valueDeclaration)) {
break;
}
result.push({identifier: baseTypes[0], node: symbol.valueDeclaration});
Expand Down