From c986d3dcf4ba33cec6a448d17d1d344e8641bde3 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 13 Dec 2018 18:07:24 +0000 Subject: [PATCH] fix(upgrade): upgrade Directive facade should not return different instance from constructor (#27660) In ngUpgrade (dynamic) we create a dynamic Angular `Directive` that wraps AngularJS components that are being upgraded. The constructor of this `Directive` class returns a different instance than `this`. It is this instance that actually contains the life-cycle hook handlers. This would break in ivy, since the methods on the prototype of the original class are wired up, rather than the instance methods. This results in hooks like `ngOnInit` not being called. This commit refactors the code to extend the inner class that was being returned so that the prototype chain is correct for both ViewEngine and ivy. This change resolves a number of failing ivy tests, but also exposes other failures that were masked by this issue. The tests have been updated accordingly. (FW-812) PR Close #27660 --- .../src/dynamic/upgrade_ng1_adapter.ts | 25 +- packages/upgrade/test/dynamic/upgrade_spec.ts | 2196 ++++++++--------- 2 files changed, 1075 insertions(+), 1146 deletions(-) diff --git a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts index f52e178640..66a188576f 100644 --- a/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts +++ b/packages/upgrade/src/dynamic/upgrade_ng1_adapter.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, ElementRef, EventEmitter, Inject, Injector, OnChanges, OnInit, SimpleChange, SimpleChanges, Type} from '@angular/core'; +import {Directive, DoCheck, ElementRef, EventEmitter, Inject, Injector, OnChanges, OnDestroy, OnInit, SimpleChange, SimpleChanges, Type} from '@angular/core'; import * as angular from '../common/angular1'; import {$SCOPE} from '../common/constants'; @@ -46,23 +46,14 @@ export class UpgradeNg1ComponentAdapterBuilder { const directive = {selector: selector, inputs: this.inputsRename, outputs: this.outputsRename}; @Directive({jit: true, ...directive}) - class MyClass { - // TODO(issue/24571): remove '!'. - directive !: angular.IDirective; + class MyClass extends UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck, + OnDestroy { constructor( @Inject($SCOPE) scope: angular.IScope, injector: Injector, elementRef: ElementRef) { - const helper = new UpgradeHelper(injector, name, elementRef, this.directive); - return new UpgradeNg1ComponentAdapter( - helper, scope, self.template, self.inputs, self.outputs, self.propertyOutputs, - self.checkProperties, self.propertyMap) as any; - } - ngOnInit() { /* needs to be here for ng2 to properly detect it */ - } - ngOnChanges() { /* needs to be here for ng2 to properly detect it */ - } - ngDoCheck() { /* needs to be here for ng2 to properly detect it */ - } - ngOnDestroy() { /* needs to be here for ng2 to properly detect it */ + super( + new UpgradeHelper(injector, name, elementRef, self.directive || undefined), scope, + self.template, self.inputs, self.outputs, self.propertyOutputs, self.checkProperties, + self.propertyMap) as any; } } this.type = MyClass; @@ -149,7 +140,7 @@ class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck { private controllerInstance: IControllerInstance|null = null; destinationObj: IBindingDestination|null = null; checkLastValues: any[] = []; - private directive: angular.IDirective; + directive: angular.IDirective; element: Element; $element: any = null; componentScope: angular.IScope; diff --git a/packages/upgrade/test/dynamic/upgrade_spec.ts b/packages/upgrade/test/dynamic/upgrade_spec.ts index 672ac9a29d..cb25b76046 100644 --- a/packages/upgrade/test/dynamic/upgrade_spec.ts +++ b/packages/upgrade/test/dynamic/upgrade_spec.ts @@ -56,40 +56,36 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should instantiate ng1 in ng2 template and project content', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + it('should instantiate ng1 in ng2 template and project content', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); - @Component({ - selector: 'ng2', - template: `{{ 'ng2(' }}{{'transclude'}}{{ ')' }}`, - }) - class Ng2 { - } + @Component({ + selector: 'ng2', + template: `{{ 'ng2(' }}{{'transclude'}}{{ ')' }}`, + }) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng1', () => { - return { - transclude: true, - template: '{{ "ng1" }}()' - }; - }); - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + ng1Module.directive('ng1', () => { + return {transclude: true, template: '{{ "ng1" }}()'}; + }); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html('
{{\'ng1(\'}}{{\')\'}}
'); + const element = html('
{{\'ng1(\'}}{{\')\'}}
'); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))'); - ref.dispose(); - }); - })); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))'); + ref.dispose(); + }); + })); it('supports the compilerOptions argument', async(() => { const platformRef = platformBrowserDynamic(); @@ -657,49 +653,48 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') - .it('should properly run cleanup with multiple levels of nesting', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let destroyed = false; + it('should properly run cleanup with multiple levels of nesting', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let destroyed = false; - @Component( - {selector: 'ng2-outer', template: '
'}) - class Ng2OuterComponent { - @Input() destroyIt = false; - } + @Component( + {selector: 'ng2-outer', template: '
'}) + class Ng2OuterComponent { + @Input() destroyIt = false; + } - @Component({selector: 'ng2-inner', template: 'test'}) - class Ng2InnerComponent implements OnDestroy { - ngOnDestroy() { destroyed = true; } - } + @Component({selector: 'ng2-inner', template: 'test'}) + class Ng2InnerComponent implements OnDestroy { + ngOnDestroy() { destroyed = true; } + } - @NgModule({ - imports: [BrowserModule], - declarations: - [Ng2InnerComponent, Ng2OuterComponent, adapter.upgradeNg1Component('ng1')], - schemas: [NO_ERRORS_SCHEMA], - }) - class Ng2Module { - } + @NgModule({ + imports: [BrowserModule], + declarations: + [Ng2InnerComponent, Ng2OuterComponent, adapter.upgradeNg1Component('ng1')], + schemas: [NO_ERRORS_SCHEMA], + }) + class Ng2Module { + } - const ng1Module = - angular.module('ng1', []) - .directive('ng1', () => ({template: ''})) - .directive('ng2Inner', adapter.downgradeNg2Component(Ng2InnerComponent)) - .directive('ng2Outer', adapter.downgradeNg2Component(Ng2OuterComponent)); + const ng1Module = + angular.module('ng1', []) + .directive('ng1', () => ({template: ''})) + .directive('ng2Inner', adapter.downgradeNg2Component(Ng2InnerComponent)) + .directive('ng2Outer', adapter.downgradeNg2Component(Ng2OuterComponent)); - const element = html(''); + const element = html(''); - adapter.bootstrap(element, [ng1Module.name]).ready(ref => { - expect(element.textContent).toBe('test'); - expect(destroyed).toBe(false); + adapter.bootstrap(element, [ng1Module.name]).ready(ref => { + expect(element.textContent).toBe('test'); + expect(destroyed).toBe(false); - $apply(ref, 'destroyIt = true'); + $apply(ref, 'destroyIt = true'); - expect(element.textContent).toBe(''); - expect(destroyed).toBe(true); - }); - })); + expect(element.textContent).toBe(''); + expect(destroyed).toBe(true); + }); + })); it('should fallback to the root ng2.injector when compiled outside the dom', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -1035,61 +1030,59 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support `&` bindings', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + it('should support `&` bindings', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: 'Inside: -', - bindings: {outputA: '&outputAttrA', outputB: '&'} - }; + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: 'Inside: -', + bindings: {outputA: '&outputAttrA', outputB: '&'} + }; - // Define `Ng2Component` - @Component({ - selector: 'ng2', - template: ` + // Define `Ng2Component` + @Component({ + selector: 'ng2', + template: ` | Outside: {{ dataA }}, {{ dataB }} ` - }) - class Ng2Component { - dataA = 'foo'; - dataB = 'bar'; - } + }) + class Ng2Component { + dataA = 'foo'; + dataB = 'bar'; + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - imports: [BrowserModule] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + imports: [BrowserModule] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - const ng1 = element.querySelector('ng1') !; - const ng1Controller = angular.element(ng1).controller !('ng1'); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + const ng1 = element.querySelector('ng1') !; + const ng1Controller = angular.element(ng1).controller !('ng1'); - expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar'); + expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar'); - ng1Controller.outputA('baz'); - ng1Controller.outputB('qux'); - $digest(ref); + ng1Controller.outputA('baz'); + ng1Controller.outputB('qux'); + $digest(ref); - expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: baz, qux'); + expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: baz, qux'); - ref.dispose(); - }); - })); + ref.dispose(); + }); + })); fixmeIvy('FW-724: upgraded ng1 components are not being rendered') .it('should bind properties, events', async(() => { @@ -1294,65 +1287,63 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support templateUrl fetched from $httpBackend', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); - ng1Module.value( - '$httpBackend', (method: string, url: string, post: any, cbFn: Function) => { - cbFn(200, `${method}:${url}`); - }); + it('should support templateUrl fetched from $httpBackend', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + ng1Module.value( + '$httpBackend', (method: string, url: string, post: any, cbFn: Function) => { + cbFn(200, `${method}:${url}`); + }); - const ng1 = () => { return {templateUrl: 'url.html'}; }; - ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + const ng1 = () => { return {templateUrl: 'url.html'}; }; + ng1Module.directive('ng1', ng1); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); + ref.dispose(); + }); + })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support templateUrl as a function', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); - ng1Module.value( - '$httpBackend', (method: string, url: string, post: any, cbFn: Function) => { - cbFn(200, `${method}:${url}`); - }); + it('should support templateUrl as a function', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + ng1Module.value( + '$httpBackend', (method: string, url: string, post: any, cbFn: Function) => { + cbFn(200, `${method}:${url}`); + }); - const ng1 = () => { return {templateUrl() { return 'url.html'; }}; }; - ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + const ng1 = () => { return {templateUrl() { return 'url.html'; }}; }; + ng1Module.directive('ng1', ng1); + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); + ref.dispose(); + }); + })); it('should support empty template', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -1406,81 +1397,76 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support templateUrl fetched from $templateCache', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); - ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS')); + it('should support templateUrl fetched from $templateCache', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS')); - const ng1 = () => { return {templateUrl: 'url.html'}; }; - ng1Module.directive('ng1', ng1); + const ng1 = () => { return {templateUrl: 'url.html'}; }; + ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('WORKS'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('WORKS'); + ref.dispose(); + }); + })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support controller with controllerAs', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + it('should support controller with controllerAs', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); - const ng1 = () => { - return { - scope: true, - template: - '{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}', - controllerAs: 'ctl', - controller: class { - scope: any; hasElement: string; $element: any; isClass: any; - constructor($scope: any, $element: any) { - this.verifyIAmAClass(); - this.scope = - $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope'; - this.hasElement = $element[0].nodeName; - this.$element = $element; - } verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() { - return this.$element.controller('ng1') == this ? 'published' : - 'not-published'; - } - } - }; - }; - ng1Module.directive('ng1', ng1); + const ng1 = () => { + return { + scope: true, + template: + '{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}', + controllerAs: 'ctl', + controller: class { + scope: any; hasElement: string; $element: any; isClass: any; + constructor($scope: any, $element: any) { + this.verifyIAmAClass(); + this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope'; + this.hasElement = $element[0].nodeName; + this.$element = $element; + } verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() { + return this.$element.controller('ng1') == this ? 'published' : 'not-published'; + } + } + }; + }; + ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)) - .toEqual('scope; isClass; NG1; published'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('scope; isClass; NG1; published'); + ref.dispose(); + }); + })); fixmeIvy('FW-724: upgraded ng1 components are not being rendered') .it('should support bindToController', async(() => { @@ -1552,140 +1538,136 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support single require in linking fn', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + it('should support single require in linking fn', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); - const ng1 = ($rootScope: any) => { - return { - scope: {title: '@'}, - bindToController: true, - template: '{{ctl.status}}', - require: 'ng1', - controllerAs: 'ctrl', - controller: class {status = 'WORKS';}, - link: function(scope: any, element: any, attrs: any, linkController: any) { - expect(scope.$root).toEqual($rootScope); - expect(element[0].nodeName).toEqual('NG1'); - expect(linkController.status).toEqual('WORKS'); - scope.ctl = linkController; - } - }; - }; - ng1Module.directive('ng1', ng1); + const ng1 = ($rootScope: any) => { + return { + scope: {title: '@'}, + bindToController: true, + template: '{{ctl.status}}', + require: 'ng1', + controllerAs: 'ctrl', + controller: class {status = 'WORKS';}, + link: function(scope: any, element: any, attrs: any, linkController: any) { + expect(scope.$root).toEqual($rootScope); + expect(element[0].nodeName).toEqual('NG1'); + expect(linkController.status).toEqual('WORKS'); + scope.ctl = linkController; + } + }; + }; + ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('WORKS'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('WORKS'); + ref.dispose(); + }); + })); - fixmeIvy('FW-724: upgraded ng1 components are not being rendered') - .it('should support array require in linking fn', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + it('should support array require in linking fn', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); - const parent = () => { return {controller: class {parent = 'PARENT';}}; }; - const ng1 = () => { - return { - scope: {title: '@'}, - bindToController: true, - template: '{{parent.parent}}:{{ng1.status}}', - require: ['ng1', '^parent', '?^^notFound'], - controllerAs: 'ctrl', - controller: class {status = 'WORKS';}, - link: function(scope: any, element: any, attrs: any, linkControllers: any) { - expect(linkControllers[0].status).toEqual('WORKS'); - expect(linkControllers[1].parent).toEqual('PARENT'); - expect(linkControllers[2]).toBe(undefined); - scope.ng1 = linkControllers[0]; - scope.parent = linkControllers[1]; - } - }; - }; - ng1Module.directive('parent', parent); - ng1Module.directive('ng1', ng1); + const parent = () => { return {controller: class {parent = 'PARENT';}}; }; + const ng1 = () => { + return { + scope: {title: '@'}, + bindToController: true, + template: '{{parent.parent}}:{{ng1.status}}', + require: ['ng1', '^parent', '?^^notFound'], + controllerAs: 'ctrl', + controller: class {status = 'WORKS';}, + link: function(scope: any, element: any, attrs: any, linkControllers: any) { + expect(linkControllers[0].status).toEqual('WORKS'); + expect(linkControllers[1].parent).toEqual('PARENT'); + expect(linkControllers[2]).toBe(undefined); + scope.ng1 = linkControllers[0]; + scope.parent = linkControllers[1]; + } + }; + }; + ng1Module.directive('parent', parent); + ng1Module.directive('ng1', ng1); - @Component({selector: 'ng2', template: ''}) - class Ng2 { - } + @Component({selector: 'ng2', template: ''}) + class Ng2 { + } - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + }) + class Ng2Module { + } - ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS'); - ref.dispose(); - }); - })); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS'); + ref.dispose(); + }); + })); describe('with life-cycle hooks', () => { - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should call `$onInit()` on controller', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const $onInitSpyA = jasmine.createSpy('$onInitA'); - const $onInitSpyB = jasmine.createSpy('$onInitB'); + it('should call `$onInit()` on controller', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const $onInitSpyA = jasmine.createSpy('$onInitA'); + const $onInitSpyB = jasmine.createSpy('$onInitB'); - @Component({selector: 'ng2', template: ' | '}) - class Ng2Component { - } + @Component({selector: 'ng2', template: ' | '}) + class Ng2Component { + } - angular.module('ng1', []) - .directive('ng1A', () => ({ - template: '', - scope: {}, - bindToController: true, - controllerAs: '$ctrl', - controller: class {$onInit() { $onInitSpyA(); }} - })) - .directive('ng1B', () => ({ - template: '', - scope: {}, - bindToController: false, - controllerAs: '$ctrl', - controller: function() { this.$onInit = $onInitSpyB; } - })) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + angular.module('ng1', []) + .directive('ng1A', () => ({ + template: '', + scope: {}, + bindToController: true, + controllerAs: '$ctrl', + controller: class {$onInit() { $onInitSpyA(); }} + })) + .directive('ng1B', () => ({ + template: '', + scope: {}, + bindToController: false, + controllerAs: '$ctrl', + controller: function() { this.$onInit = $onInitSpyB; } + })) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [ - adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), - Ng2Component - ], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [ + adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), + Ng2Component + ], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect($onInitSpyA).toHaveBeenCalled(); - expect($onInitSpyB).toHaveBeenCalled(); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect($onInitSpyA).toHaveBeenCalled(); + expect($onInitSpyB).toHaveBeenCalled(); - ref.dispose(); - }); - })); + ref.dispose(); + }); + })); it('should not call `$onInit()` on scope', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -1733,61 +1715,59 @@ withEachNg1Version(() => { }); })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should call `$doCheck()` on controller', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const $doCheckSpyA = jasmine.createSpy('$doCheckA'); - const $doCheckSpyB = jasmine.createSpy('$doCheckB'); - let changeDetector: ChangeDetectorRef; + it('should call `$doCheck()` on controller', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const $doCheckSpyA = jasmine.createSpy('$doCheckA'); + const $doCheckSpyB = jasmine.createSpy('$doCheckB'); + let changeDetector: ChangeDetectorRef; - @Component({selector: 'ng2', template: ' | '}) - class Ng2Component { - constructor(cd: ChangeDetectorRef) { changeDetector = cd; } - } + @Component({selector: 'ng2', template: ' | '}) + class Ng2Component { + constructor(cd: ChangeDetectorRef) { changeDetector = cd; } + } - angular.module('ng1', []) - .directive('ng1A', () => ({ - template: '', - scope: {}, - bindToController: true, - controllerAs: '$ctrl', - controller: class {$doCheck() { $doCheckSpyA(); }} - })) - .directive('ng1B', () => ({ - template: '', - scope: {}, - bindToController: false, - controllerAs: '$ctrl', - controller: function() { this.$doCheck = $doCheckSpyB; } - })) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + angular.module('ng1', []) + .directive('ng1A', () => ({ + template: '', + scope: {}, + bindToController: true, + controllerAs: '$ctrl', + controller: class {$doCheck() { $doCheckSpyA(); }} + })) + .directive('ng1B', () => ({ + template: '', + scope: {}, + bindToController: false, + controllerAs: '$ctrl', + controller: function() { this.$doCheck = $doCheckSpyB; } + })) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [ - adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), - Ng2Component - ], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [ + adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), + Ng2Component + ], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect($doCheckSpyA).toHaveBeenCalled(); - expect($doCheckSpyB).toHaveBeenCalled(); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect($doCheckSpyA).toHaveBeenCalled(); + expect($doCheckSpyB).toHaveBeenCalled(); - $doCheckSpyA.calls.reset(); - $doCheckSpyB.calls.reset(); - changeDetector.detectChanges(); + $doCheckSpyA.calls.reset(); + $doCheckSpyB.calls.reset(); + changeDetector.detectChanges(); - expect($doCheckSpyA).toHaveBeenCalled(); - expect($doCheckSpyB).toHaveBeenCalled(); + expect($doCheckSpyA).toHaveBeenCalled(); + expect($doCheckSpyB).toHaveBeenCalled(); - ref.dispose(); - }); - })); + ref.dispose(); + }); + })); it('should not call `$doCheck()` on scope', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -1844,53 +1824,50 @@ withEachNg1Version(() => { }); })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should call `$postLink()` on controller', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const $postLinkSpyA = jasmine.createSpy('$postLinkA'); - const $postLinkSpyB = jasmine.createSpy('$postLinkB'); + it('should call `$postLink()` on controller', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const $postLinkSpyA = jasmine.createSpy('$postLinkA'); + const $postLinkSpyB = jasmine.createSpy('$postLinkB'); - @Component({selector: 'ng2', template: ' | '}) - class Ng2Component { - } + @Component({selector: 'ng2', template: ' | '}) + class Ng2Component { + } - angular.module('ng1', []) - .directive('ng1A', () => ({ - template: '', - scope: {}, - bindToController: true, - controllerAs: '$ctrl', - controller: class {$postLink() { $postLinkSpyA(); }} - })) - .directive( - 'ng1B', () => ({ - template: '', - scope: {}, - bindToController: false, - controllerAs: '$ctrl', - controller: function() { this.$postLink = $postLinkSpyB; } - })) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + angular.module('ng1', []) + .directive('ng1A', () => ({ + template: '', + scope: {}, + bindToController: true, + controllerAs: '$ctrl', + controller: class {$postLink() { $postLinkSpyA(); }} + })) + .directive('ng1B', () => ({ + template: '', + scope: {}, + bindToController: false, + controllerAs: '$ctrl', + controller: function() { this.$postLink = $postLinkSpyB; } + })) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [ - adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), - Ng2Component - ], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [ + adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), + Ng2Component + ], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect($postLinkSpyA).toHaveBeenCalled(); - expect($postLinkSpyB).toHaveBeenCalled(); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect($postLinkSpyA).toHaveBeenCalled(); + expect($postLinkSpyB).toHaveBeenCalled(); - ref.dispose(); - }); - })); + ref.dispose(); + }); + })); it('should not call `$postLink()` on scope', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -1939,7 +1916,7 @@ withEachNg1Version(() => { })); fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') + 'FW-844: Directive input bindings cannot be assigned after the `@Directive` decorator has been compiled') .it('should call `$onChanges()` on binding destination', fakeAsync(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const $onChangesControllerSpyA = jasmine.createSpy('$onChangesControllerA'); @@ -2035,7 +2012,7 @@ withEachNg1Version(() => { })); fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') + 'FW-843: destroy hooks are not registered on upgraded ng1 components contained in ng2 component templates under ivy') .it('should call `$onDestroy()` on controller', fakeAsync(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const $onDestroySpyA = jasmine.createSpy('$onDestroyA'); @@ -2127,540 +2104,514 @@ withEachNg1Version(() => { }); })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should not call `$onDestroy()` on scope', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const $onDestroySpy = jasmine.createSpy('$onDestroy'); - let ng2ComponentInstance: Ng2Component; + it('should not call `$onDestroy()` on scope', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const $onDestroySpy = jasmine.createSpy('$onDestroy'); + let ng2ComponentInstance: Ng2Component; - @Component({ - selector: 'ng2', - template: ` + @Component({ + selector: 'ng2', + template: `
|
` - }) - class Ng2Component { - ng2Destroy: boolean = false; - constructor() { ng2ComponentInstance = this; } - } + }) + class Ng2Component { + ng2Destroy: boolean = false; + constructor() { ng2ComponentInstance = this; } + } - // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), - // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be - // on - // the queue at the end of the test, causing it to fail. - // Mocking animations (via `ngAnimateMock`) avoids the issue. - angular.module('ng1', ['ngAnimateMock']) - .directive('ng1A', () => ({ - template: '', - scope: {}, - bindToController: true, - controllerAs: '$ctrl', - controller: function($scope: angular.IScope) { - Object.getPrototypeOf($scope).$onDestroy = - $onDestroySpy; - } - })) - .directive('ng1B', () => ({ - template: '', - scope: {}, - bindToController: false, - controllerAs: '$ctrl', - controller: function($scope: angular.IScope) { - $scope['$onDestroy'] = $onDestroySpy; - } - })) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), + // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be + // on + // the queue at the end of the test, causing it to fail. + // Mocking animations (via `ngAnimateMock`) avoids the issue. + angular.module('ng1', ['ngAnimateMock']) + .directive('ng1A', () => ({ + template: '', + scope: {}, + bindToController: true, + controllerAs: '$ctrl', + controller: function($scope: angular.IScope) { + Object.getPrototypeOf($scope).$onDestroy = $onDestroySpy; + } + })) + .directive('ng1B', () => ({ + template: '', + scope: {}, + bindToController: false, + controllerAs: '$ctrl', + controller: function($scope: angular.IScope) { + $scope['$onDestroy'] = $onDestroySpy; + } + })) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [ - adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), - Ng2Component - ], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [ + adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'), + Ng2Component + ], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - const $rootScope = ref.ng1RootScope as any; + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + const $rootScope = ref.ng1RootScope as any; - $rootScope.ng1Destroy = false; - tick(); - $rootScope.$digest(); + $rootScope.ng1Destroy = false; + tick(); + $rootScope.$digest(); - $rootScope.ng1Destroy = true; - tick(); - $rootScope.$digest(); + $rootScope.ng1Destroy = true; + tick(); + $rootScope.$digest(); - $rootScope.ng1Destroy = false; - tick(); - $rootScope.$digest(); + $rootScope.ng1Destroy = false; + tick(); + $rootScope.$digest(); - ng2ComponentInstance.ng2Destroy = true; - tick(); - $rootScope.$digest(); + ng2ComponentInstance.ng2Destroy = true; + tick(); + $rootScope.$digest(); - expect($onDestroySpy).not.toHaveBeenCalled(); + expect($onDestroySpy).not.toHaveBeenCalled(); - ref.dispose(); - }); - })); + ref.dispose(); + }); + })); }); describe('destroying the upgraded component', () => { - fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') - .it('should destroy `componentScope`', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const scopeDestroyListener = jasmine.createSpy('scopeDestroyListener'); - let ng2ComponentInstance: Ng2Component; + it('should destroy `componentScope`', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const scopeDestroyListener = jasmine.createSpy('scopeDestroyListener'); + let ng2ComponentInstance: Ng2Component; - @Component( - {selector: 'ng2', template: '
'}) - class Ng2Component { - ng2Destroy: boolean = false; - constructor() { ng2ComponentInstance = this; } - } + @Component({selector: 'ng2', template: '
'}) + class Ng2Component { + ng2Destroy: boolean = false; + constructor() { ng2ComponentInstance = this; } + } - // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), - // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be - // on - // the queue at the end of the test, causing it to fail. - // Mocking animations (via `ngAnimateMock`) avoids the issue. - angular.module('ng1', ['ngAnimateMock']) - .component('ng1', { - controller: function($scope: angular.IScope) { - $scope.$on('$destroy', scopeDestroyListener); - }, - }) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), + // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be + // on + // the queue at the end of the test, causing it to fail. + // Mocking animations (via `ngAnimateMock`) avoids the issue. + angular.module('ng1', ['ngAnimateMock']) + .component('ng1', { + controller: function($scope: angular.IScope) { + $scope.$on('$destroy', scopeDestroyListener); + }, + }) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(''); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - const $rootScope = ref.ng1RootScope as any; + const element = html(''); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + const $rootScope = ref.ng1RootScope as any; - expect(scopeDestroyListener).not.toHaveBeenCalled(); + expect(scopeDestroyListener).not.toHaveBeenCalled(); - ng2ComponentInstance.ng2Destroy = true; - tick(); - $rootScope.$digest(); + ng2ComponentInstance.ng2Destroy = true; + tick(); + $rootScope.$digest(); - expect(scopeDestroyListener).toHaveBeenCalledTimes(1); - }); - })); + expect(scopeDestroyListener).toHaveBeenCalledTimes(1); + }); + })); - fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') - .it('should emit `$destroy` on `$element` and descendants', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const elementDestroyListener = jasmine.createSpy('elementDestroyListener'); - const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener'); - let ng2ComponentInstance: Ng2Component; + it('should emit `$destroy` on `$element` and descendants', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const elementDestroyListener = jasmine.createSpy('elementDestroyListener'); + const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener'); + let ng2ComponentInstance: Ng2Component; - @Component( - {selector: 'ng2', template: '
'}) - class Ng2Component { - ng2Destroy: boolean = false; - constructor() { ng2ComponentInstance = this; } - } + @Component({selector: 'ng2', template: '
'}) + class Ng2Component { + ng2Destroy: boolean = false; + constructor() { ng2ComponentInstance = this; } + } - // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), - // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be - // on - // the queue at the end of the test, causing it to fail. - // Mocking animations (via `ngAnimateMock`) avoids the issue. - angular.module('ng1', ['ngAnimateMock']) - .component('ng1', { - controller: class { - constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { - this.$element.on !('$destroy', elementDestroyListener); - this.$element.contents !().on !('$destroy', descendantDestroyListener); - } - }, - template: '
' - }) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3), + // `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be + // on + // the queue at the end of the test, causing it to fail. + // Mocking animations (via `ngAnimateMock`) avoids the issue. + angular.module('ng1', ['ngAnimateMock']) + .component('ng1', { + controller: class { + constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { + this.$element.on !('$destroy', elementDestroyListener); + this.$element.contents !().on !('$destroy', descendantDestroyListener); + } + }, + template: '
' + }) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(''); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - const $rootScope = ref.ng1RootScope as any; - tick(); - $rootScope.$digest(); + const element = html(''); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + const $rootScope = ref.ng1RootScope as any; + tick(); + $rootScope.$digest(); - expect(elementDestroyListener).not.toHaveBeenCalled(); - expect(descendantDestroyListener).not.toHaveBeenCalled(); + expect(elementDestroyListener).not.toHaveBeenCalled(); + expect(descendantDestroyListener).not.toHaveBeenCalled(); - ng2ComponentInstance.ng2Destroy = true; - tick(); - $rootScope.$digest(); + ng2ComponentInstance.ng2Destroy = true; + tick(); + $rootScope.$digest(); - expect(elementDestroyListener).toHaveBeenCalledTimes(1); - expect(descendantDestroyListener).toHaveBeenCalledTimes(1); - }); - })); + expect(elementDestroyListener).toHaveBeenCalledTimes(1); + expect(descendantDestroyListener).toHaveBeenCalledTimes(1); + }); + })); - fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') - .it('should clear data on `$element` and descendants', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let ng1ComponentElement: angular.IAugmentedJQuery; - let ng2ComponentAInstance: Ng2ComponentA; + it('should clear data on `$element` and descendants', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let ng1ComponentElement: angular.IAugmentedJQuery; + let ng2ComponentAInstance: Ng2ComponentA; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - controller: class { - constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { - this.$element.data !('test', 1); - this.$element.contents !().data !('test', 2); + // Define `ng1Component` + const ng1Component: angular.IComponent = { + controller: class { + constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { + this.$element.data !('test', 1); + this.$element.contents !().data !('test', 2); - ng1ComponentElement = this.$element; - } - }, - template: '
' - }; + ng1ComponentElement = this.$element; + } + }, + template: '
' + }; - // Define `Ng2Component` - @Component({selector: 'ng2A', template: ''}) - class Ng2ComponentA { - destroyIt = false; + // Define `Ng2Component` + @Component({selector: 'ng2A', template: ''}) + class Ng2ComponentA { + destroyIt = false; - constructor() { ng2ComponentAInstance = this; } - } + constructor() { ng2ComponentAInstance = this; } + } - @Component({selector: 'ng2B', template: ''}) - class Ng2ComponentB { - } + @Component({selector: 'ng2B', template: ''}) + class Ng2ComponentB { + } - // Define `ng1Module` - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA)); + // Define `ng1Module` + angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA)); - // Define `Ng2Module` - @NgModule({ - declarations: - [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB], - entryComponents: [Ng2ComponentA], - imports: [BrowserModule] - }) - class Ng2Module { - ngDoBootstrap() {} - } + // Define `Ng2Module` + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB], + entryComponents: [Ng2ComponentA], + imports: [BrowserModule] + }) + class Ng2Module { + ngDoBootstrap() {} + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready((ref) => { - const $rootScope = ref.ng1RootScope as any; - tick(); - $rootScope.$digest(); - expect(ng1ComponentElement.data !('test')).toBe(1); - expect(ng1ComponentElement.contents !().data !('test')).toBe(2); + adapter.bootstrap(element, ['ng1Module']).ready((ref) => { + const $rootScope = ref.ng1RootScope as any; + tick(); + $rootScope.$digest(); + expect(ng1ComponentElement.data !('test')).toBe(1); + expect(ng1ComponentElement.contents !().data !('test')).toBe(2); - ng2ComponentAInstance.destroyIt = true; - tick(); - $rootScope.$digest(); + ng2ComponentAInstance.destroyIt = true; + tick(); + $rootScope.$digest(); - expect(ng1ComponentElement.data !('test')).toBeUndefined(); - expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined(); - }); - })); + expect(ng1ComponentElement.data !('test')).toBeUndefined(); + expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined(); + }); + })); - fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') - .it('should clear dom listeners on `$element` and descendants`', fakeAsync(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const elementClickListener = jasmine.createSpy('elementClickListener'); - const descendantClickListener = jasmine.createSpy('descendantClickListener'); - let ng1DescendantElement: angular.IAugmentedJQuery; - let ng2ComponentAInstance: Ng2ComponentA; + it('should clear dom listeners on `$element` and descendants`', fakeAsync(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const elementClickListener = jasmine.createSpy('elementClickListener'); + const descendantClickListener = jasmine.createSpy('descendantClickListener'); + let ng1DescendantElement: angular.IAugmentedJQuery; + let ng2ComponentAInstance: Ng2ComponentA; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - controller: class { - constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { - ng1DescendantElement = this.$element.contents !(); + // Define `ng1Component` + const ng1Component: angular.IComponent = { + controller: class { + constructor(private $element: angular.IAugmentedJQuery) {} $onInit() { + ng1DescendantElement = this.$element.contents !(); - this.$element.on !('click', elementClickListener); - ng1DescendantElement.on !('click', descendantClickListener); - } - }, - template: '
' - }; + this.$element.on !('click', elementClickListener); + ng1DescendantElement.on !('click', descendantClickListener); + } + }, + template: '
' + }; - // Define `Ng2Component` - @Component({selector: 'ng2A', template: ''}) - class Ng2ComponentA { - destroyIt = false; + // Define `Ng2Component` + @Component({selector: 'ng2A', template: ''}) + class Ng2ComponentA { + destroyIt = false; - constructor() { ng2ComponentAInstance = this; } - } + constructor() { ng2ComponentAInstance = this; } + } - @Component({selector: 'ng2B', template: ''}) - class Ng2ComponentB { - } + @Component({selector: 'ng2B', template: ''}) + class Ng2ComponentB { + } - // Define `ng1Module` - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA)); + // Define `ng1Module` + angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA)); - // Define `Ng2Module` - @NgModule({ - declarations: - [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB], - entryComponents: [Ng2ComponentA], - imports: [BrowserModule] - }) - class Ng2Module { - ngDoBootstrap() {} - } + // Define `Ng2Module` + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB], + entryComponents: [Ng2ComponentA], + imports: [BrowserModule] + }) + class Ng2Module { + ngDoBootstrap() {} + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready((ref) => { - const $rootScope = ref.ng1RootScope as any; - tick(); - $rootScope.$digest(); - (ng1DescendantElement[0] as HTMLElement).click(); - expect(elementClickListener).toHaveBeenCalledTimes(1); - expect(descendantClickListener).toHaveBeenCalledTimes(1); + adapter.bootstrap(element, ['ng1Module']).ready((ref) => { + const $rootScope = ref.ng1RootScope as any; + tick(); + $rootScope.$digest(); + (ng1DescendantElement[0] as HTMLElement).click(); + expect(elementClickListener).toHaveBeenCalledTimes(1); + expect(descendantClickListener).toHaveBeenCalledTimes(1); - ng2ComponentAInstance.destroyIt = true; - tick(); - $rootScope.$digest(); + ng2ComponentAInstance.destroyIt = true; + tick(); + $rootScope.$digest(); - (ng1DescendantElement[0] as HTMLElement).click(); - expect(elementClickListener).toHaveBeenCalledTimes(1); - expect(descendantClickListener).toHaveBeenCalledTimes(1); - }); - })); + (ng1DescendantElement[0] as HTMLElement).click(); + expect(elementClickListener).toHaveBeenCalledTimes(1); + expect(descendantClickListener).toHaveBeenCalledTimes(1); + }); + })); }); describe('linking', () => { - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should run the pre-linking after instantiating the controller', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const log: string[] = []; + it('should run the pre-linking after instantiating the controller', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const log: string[] = []; - // Define `ng1Directive` - const ng1Directive: angular.IDirective = { - template: '', - link: {pre: () => log.push('ng1-pre')}, - controller: class {constructor() { log.push('ng1-ctrl'); }} - }; + // Define `ng1Directive` + const ng1Directive: angular.IDirective = { + template: '', + link: {pre: () => log.push('ng1-pre')}, + controller: class {constructor() { log.push('ng1-ctrl'); }} + }; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1', []) - .directive('ng1', () => ng1Directive) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1', () => ng1Directive) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1']).ready(() => { - setTimeout(() => expect(log).toEqual(['ng1-ctrl', 'ng1-pre']), 1000); - }); - })); + adapter.bootstrap(element, ['ng1']).ready(() => { + setTimeout(() => expect(log).toEqual(['ng1-ctrl', 'ng1-pre']), 1000); + }); + })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should run the pre-linking function before linking', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const log: string[] = []; + it('should run the pre-linking function before linking', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const log: string[] = []; - // Define `ng1Directive` - const ng1DirectiveA: angular.IDirective = { - template: '', - link: {pre: () => log.push('ng1A-pre')} - }; + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: {pre: () => log.push('ng1A-pre')} + }; - const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1', []) - .directive('ng1A', () => ng1DirectiveA) - .directive('ng1B', () => ng1DirectiveB) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1']).ready(() => { - expect(log).toEqual(['ng1A-pre', 'ng1B-post']); - }); - })); + adapter.bootstrap(element, ['ng1']).ready(() => { + expect(log).toEqual(['ng1A-pre', 'ng1B-post']); + }); + })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should run the post-linking function after linking (link: object)', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const log: string[] = []; + it('should run the post-linking function after linking (link: object)', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const log: string[] = []; - // Define `ng1Directive` - const ng1DirectiveA: angular.IDirective = { - template: '', - link: {post: () => log.push('ng1A-post')} - }; + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: {post: () => log.push('ng1A-post')} + }; - const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1', []) - .directive('ng1A', () => ng1DirectiveA) - .directive('ng1B', () => ng1DirectiveB) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1']).ready(() => { - expect(log).toEqual(['ng1B-post', 'ng1A-post']); - }); - })); + adapter.bootstrap(element, ['ng1']).ready(() => { + expect(log).toEqual(['ng1B-post', 'ng1A-post']); + }); + })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should run the post-linking function after linking (link: function)', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const log: string[] = []; + it('should run the post-linking function after linking (link: function)', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const log: string[] = []; - // Define `ng1Directive` - const ng1DirectiveA: angular.IDirective = { - template: '', - link: () => log.push('ng1A-post') - }; + // Define `ng1Directive` + const ng1DirectiveA: angular.IDirective = { + template: '', + link: () => log.push('ng1A-post') + }; - const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; + const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')}; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1', []) - .directive('ng1A', () => ng1DirectiveA) - .directive('ng1B', () => ng1DirectiveB) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1A', () => ng1DirectiveA) + .directive('ng1B', () => ng1DirectiveB) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1']).ready(() => { - expect(log).toEqual(['ng1B-post', 'ng1A-post']); - }); - })); + adapter.bootstrap(element, ['ng1']).ready(() => { + expect(log).toEqual(['ng1B-post', 'ng1A-post']); + }); + })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should run the post-linking function before `$postLink`', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const log: string[] = []; + it('should run the post-linking function before `$postLink`', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const log: string[] = []; - // Define `ng1Directive` - const ng1Directive: angular.IDirective = { - template: '', - link: () => log.push('ng1-post'), - controller: class {$postLink() { log.push('ng1-$post'); }} - }; + // Define `ng1Directive` + const ng1Directive: angular.IDirective = { + template: '', + link: () => log.push('ng1-post'), + controller: class {$postLink() { log.push('ng1-$post'); }} + }; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1', []) - .directive('ng1', () => ng1Directive) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1', []) + .directive('ng1', () => ng1Directive) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1']).ready(() => { - expect(log).toEqual(['ng1-post', 'ng1-$post']); - }); - })); + adapter.bootstrap(element, ['ng1']).ready(() => { + expect(log).toEqual(['ng1-post', 'ng1-$post']); + }); + })); }); describe('transclusion', () => { @@ -2727,25 +2678,24 @@ withEachNg1Version(() => { }); })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should support single-slot transclusion with fallback content', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let ng1ControllerInstances: any[] = []; - let ng2ComponentInstance: Ng2Component; + it('should support single-slot transclusion with fallback content', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let ng1ControllerInstances: any[] = []; + let ng2ComponentInstance: Ng2Component; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: 'ng1(
{{ $ctrl.value }}
)', - transclude: true, - controller: class { - value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); } - } - }; + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: 'ng1(
{{ $ctrl.value }}
)', + transclude: true, + controller: class { + value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); } + } + }; - // Define `Ng2Component` - @Component({ - selector: 'ng2', - template: ` + // Define `Ng2Component` + @Component({ + selector: 'ng2', + template: ` ng2(
{{ value }}
| @@ -2754,58 +2704,56 @@ withEachNg1Version(() => { )` - }) - class Ng2Component { - value = 'from-ng2'; - constructor() { ng2ComponentInstance = this; } - } + }) + class Ng2Component { + value = 'from-ng2'; + constructor() { ng2ComponentInstance = this; } + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(from-ng2)|ng1(from-ng2)|ng1(from-ng1))'); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(from-ng2)|ng1(from-ng2)|ng1(from-ng1))'); - ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-foo'); - ng2ComponentInstance.value = 'ng2-bar'; - $digest(ref); + ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-foo'); + ng2ComponentInstance.value = 'ng2-bar'; + $digest(ref); - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(ng2-bar)|ng1(ng2-bar)|ng1(ng1-foo))'); - }); - })); + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(ng2-bar)|ng1(ng2-bar)|ng1(ng1-foo))'); + }); + })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should support multi-slot transclusion', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let ng2ComponentInstance: Ng2Component; + it('should support multi-slot transclusion', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let ng2ComponentInstance: Ng2Component; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: - 'ng1(x(
) | y(
))', - transclude: {slotX: 'contentX', slotY: 'contentY'} - }; + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: + 'ng1(x(
) | y(
))', + transclude: {slotX: 'contentX', slotY: 'contentY'} + }; - // Define `Ng2Component` - @Component({ - selector: 'ng2', - template: ` + // Define `Ng2Component` + @Component({ + selector: 'ng2', + template: ` ng2( {{ x }}1 @@ -2814,64 +2762,60 @@ withEachNg1Version(() => { {{ y }}2 )` - }) - class Ng2Component { - x = 'foo'; - y = 'bar'; - constructor() { ng2ComponentInstance = this; } - } + }) + class Ng2Component { + x = 'foo'; + y = 'bar'; + constructor() { ng2ComponentInstance = this; } + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(x(foo1foo2)|y(bar1bar2)))'); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(x(foo1foo2)|y(bar1bar2)))'); - ng2ComponentInstance.x = 'baz'; - ng2ComponentInstance.y = 'qux'; - $digest(ref); + ng2ComponentInstance.x = 'baz'; + ng2ComponentInstance.y = 'qux'; + $digest(ref); - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(x(baz1baz2)|y(qux1qux2)))'); - }); - })); + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(x(baz1baz2)|y(qux1qux2)))'); + }); + })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should support default slot (with fallback content)', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let ng1ControllerInstances: any[] = []; - let ng2ComponentInstance: Ng2Component; + it('should support default slot (with fallback content)', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let ng1ControllerInstances: any[] = []; + let ng2ComponentInstance: Ng2Component; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: - 'ng1(default(
fallback-{{ $ctrl.value }}
))', - transclude: {slotX: 'contentX', slotY: 'contentY'}, - controller: class { - value = 'ng1'; constructor() { ng1ControllerInstances.push(this); } - } - }; + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: 'ng1(default(
fallback-{{ $ctrl.value }}
))', + transclude: {slotX: 'contentX', slotY: 'contentY'}, + controller: + class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }} + }; - // Define `Ng2Component` - @Component({ - selector: 'ng2', - template: ` + // Define `Ng2Component` + @Component({ + selector: 'ng2', + template: ` ng2( ({{ x }}) @@ -2892,155 +2836,151 @@ withEachNg1Version(() => { --> {{ x }}ignored x{{ y + x }}ignored y{{ y }} )` - }) - class Ng2Component { - x = 'foo'; - y = 'bar'; - constructor() { ng2ComponentInstance = this; } - } + }) + class Ng2Component { + x = 'foo'; + y = 'bar'; + constructor() { ng2ComponentInstance = this; } + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - expect(multiTrim(element.textContent, true)) - .toBe( - 'ng2(ng1(default((foo)foo-bar(bar)))|ng1(default(fallback-ng1))|ng1(default(foobarfoobar)))'); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + expect(multiTrim(element.textContent, true)) + .toBe( + 'ng2(ng1(default((foo)foo-bar(bar)))|ng1(default(fallback-ng1))|ng1(default(foobarfoobar)))'); - ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-plus'); - ng2ComponentInstance.x = 'baz'; - ng2ComponentInstance.y = 'qux'; - $digest(ref); + ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-plus'); + ng2ComponentInstance.x = 'baz'; + ng2ComponentInstance.y = 'qux'; + $digest(ref); - expect(multiTrim(element.textContent, true)) - .toBe( - 'ng2(ng1(default((baz)baz-qux(qux)))|ng1(default(fallback-ng1-plus))|ng1(default(bazquxbazqux)))'); - }); - })); + expect(multiTrim(element.textContent, true)) + .toBe( + 'ng2(ng1(default((baz)baz-qux(qux)))|ng1(default(fallback-ng1-plus))|ng1(default(bazquxbazqux)))'); + }); + })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should support optional transclusion slots (with fallback content)', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let ng1ControllerInstances: any[] = []; - let ng2ComponentInstance: Ng2Component; + it('should support optional transclusion slots (with fallback content)', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let ng1ControllerInstances: any[] = []; + let ng2ComponentInstance: Ng2Component; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: ` + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: ` ng1( x(
{{ $ctrl.x }}
) | y(
{{ $ctrl.y }}
) )`, - transclude: {slotX: '?contentX', slotY: '?contentY'}, - controller: class { - x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); } - } - }; + transclude: {slotX: '?contentX', slotY: '?contentY'}, + controller: class { + x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); } + } + }; - // Define `Ng2Component` - @Component({ - selector: 'ng2', - template: ` + // Define `Ng2Component` + @Component({ + selector: 'ng2', + template: ` ng2( {{ x }} | {{ y }} )` - }) - class Ng2Component { - x = 'ng2X'; - y = 'ng2Y'; - constructor() { ng2ComponentInstance = this; } - } + }) + class Ng2Component { + x = 'ng2X'; + y = 'ng2Y'; + constructor() { ng2ComponentInstance = this; } + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = angular.module('ng1Module', []) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], - schemas: [NO_ERRORS_SCHEMA] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component], + schemas: [NO_ERRORS_SCHEMA] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(x(ng2X)|y(ng1Y))|ng1(x(ng1X)|y(ng2Y)))'); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(x(ng2X)|y(ng1Y))|ng1(x(ng1X)|y(ng2Y)))'); - ng1ControllerInstances.forEach(ctrl => { - ctrl.x = 'ng1X-foo'; - ctrl.y = 'ng1Y-bar'; - }); - ng2ComponentInstance.x = 'ng2X-baz'; - ng2ComponentInstance.y = 'ng2Y-qux'; - $digest(ref); + ng1ControllerInstances.forEach(ctrl => { + ctrl.x = 'ng1X-foo'; + ctrl.y = 'ng1Y-bar'; + }); + ng2ComponentInstance.x = 'ng2X-baz'; + ng2ComponentInstance.y = 'ng2Y-qux'; + $digest(ref); - expect(multiTrim(element.textContent, true)) - .toBe('ng2(ng1(x(ng2X-baz)|y(ng1Y-bar))|ng1(x(ng1X-foo)|y(ng2Y-qux)))'); - }); - })); + expect(multiTrim(element.textContent, true)) + .toBe('ng2(ng1(x(ng2X-baz)|y(ng1Y-bar))|ng1(x(ng1X-foo)|y(ng2Y-qux)))'); + }); + })); - fixmeIvy('FW-714: ng1 projected content is not being rendered') - .it('should throw if a non-optional slot is not filled', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - let errorMessage: string; + it('should throw if a non-optional slot is not filled', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + let errorMessage: string; - // Define `ng1Component` - const ng1Component: angular.IComponent = { - template: '', - transclude: {slotX: '?contentX', slotY: 'contentY'} - }; + // Define `ng1Component` + const ng1Component: angular.IComponent = { + template: '', + transclude: {slotX: '?contentX', slotY: 'contentY'} + }; - // Define `Ng2Component` - @Component({selector: 'ng2', template: ''}) - class Ng2Component { - } + // Define `Ng2Component` + @Component({selector: 'ng2', template: ''}) + class Ng2Component { + } - // Define `ng1Module` - const ng1Module = - angular.module('ng1Module', []) - .value($EXCEPTION_HANDLER, (error: Error) => errorMessage = error.message) - .component('ng1', ng1Component) - .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); + // Define `ng1Module` + const ng1Module = + angular.module('ng1Module', []) + .value($EXCEPTION_HANDLER, (error: Error) => errorMessage = error.message) + .component('ng1', ng1Component) + .directive('ng2', adapter.downgradeNg2Component(Ng2Component)); - // Define `Ng2Module` - @NgModule({ - imports: [BrowserModule], - declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] - }) - class Ng2Module { - } + // Define `Ng2Module` + @NgModule({ + imports: [BrowserModule], + declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component] + }) + class Ng2Module { + } - // Bootstrap - const element = html(``); + // Bootstrap + const element = html(``); - adapter.bootstrap(element, ['ng1Module']).ready(ref => { - expect(errorMessage) - .toContain('Required transclusion slot \'slotY\' on directive: ng1'); - }); - })); + adapter.bootstrap(element, ['ng1Module']).ready(ref => { + expect(errorMessage) + .toContain('Required transclusion slot \'slotY\' on directive: ng1'); + }); + })); fixmeIvy('FW-714: ng1 projected content is not being rendered') .it('should support structural directives in transcluded content', async(() => { @@ -3114,7 +3054,7 @@ withEachNg1Version(() => { }); fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') + 'FW-844: Directive input bindings cannot be assigned after the `@Directive` decorator has been compiled') .it('should bind input properties (<) of components', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []); @@ -3148,39 +3088,37 @@ withEachNg1Version(() => { }); })); - fixmeIvy( - 'FW-812: Life-cycle hooks not correctly wired up for components that return a different instance from their constructor') - .it('should support ng2 > ng1 > ng2', async(() => { - const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + it('should support ng2 > ng1 > ng2', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); - const ng1 = { - template: 'ng1()', - }; - ng1Module.component('ng1', ng1); + const ng1 = { + template: 'ng1()', + }; + ng1Module.component('ng1', ng1); - @Component({selector: 'ng2a', template: 'ng2a()'}) - class Ng2a { - } - ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a)); + @Component({selector: 'ng2a', template: 'ng2a()'}) + class Ng2a { + } + ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a)); - @Component({selector: 'ng2b', template: 'ng2b'}) - class Ng2b { - } - ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b)); + @Component({selector: 'ng2b', template: 'ng2b'}) + class Ng2b { + } + ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b)); - @NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b], - imports: [BrowserModule], - }) - class Ng2Module { - } + @NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b], + imports: [BrowserModule], + }) + class Ng2Module { + } - const element = html(`
`); - adapter.bootstrap(element, ['ng1']).ready((ref) => { - expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))'); - }); - })); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))'); + }); + })); }); describe('injection', () => {