test(upgrade): disable failing ngUpgrade tests when in ivy mode (#27132)
PR Close #27132
This commit is contained in:
		
							parent
							
								
									a4462c24fa
								
							
						
					
					
						commit
						bc0fbfc93e
					
				| @ -7,6 +7,7 @@ | ||||
|  */ | ||||
| import {Compiler, Component, ComponentFactory, Injector, NgModule, TestabilityRegistry} from '@angular/core'; | ||||
| import {TestBed} from '@angular/core/testing'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import * as angular from '@angular/upgrade/src/common/angular1'; | ||||
| import {DowngradeComponentAdapter, groupNodesBySelector} from '@angular/upgrade/src/common/downgrade_component_adapter'; | ||||
| 
 | ||||
| @ -177,26 +178,28 @@ withEachNg1Version(() => { | ||||
|       beforeEach(() => registry.unregisterAllApplications()); | ||||
|       afterEach(() => registry.unregisterAllApplications()); | ||||
| 
 | ||||
|       it('should add testabilities hook when creating components', () => { | ||||
|       fixmeIvy('FW-561: Runtime compiler is not loaded') && | ||||
|           it('should add testabilities hook when creating components', () => { | ||||
| 
 | ||||
|         let registry = TestBed.get(TestabilityRegistry); | ||||
|         adapter.createComponent([]); | ||||
|         expect(registry.getAllTestabilities().length).toEqual(1); | ||||
|             let registry = TestBed.get(TestabilityRegistry); | ||||
|             adapter.createComponent([]); | ||||
|             expect(registry.getAllTestabilities().length).toEqual(1); | ||||
| 
 | ||||
|         adapter = getAdaptor();  // get a new adaptor to creat a new component
 | ||||
|         adapter.createComponent([]); | ||||
|         expect(registry.getAllTestabilities().length).toEqual(2); | ||||
|       }); | ||||
|             adapter = getAdaptor();  // get a new adaptor to creat a new component
 | ||||
|             adapter.createComponent([]); | ||||
|             expect(registry.getAllTestabilities().length).toEqual(2); | ||||
|           }); | ||||
| 
 | ||||
|       it('should remove the testability hook when destroy a component', () => { | ||||
|         const registry = TestBed.get(TestabilityRegistry); | ||||
|         expect(registry.getAllTestabilities().length).toEqual(0); | ||||
|         adapter.createComponent([]); | ||||
|         expect(registry.getAllTestabilities().length).toEqual(1); | ||||
|         adapter.registerCleanup(); | ||||
|         element.remove !(); | ||||
|         expect(registry.getAllTestabilities().length).toEqual(0); | ||||
|       }); | ||||
|       fixmeIvy('FW-561: Runtime compiler is not loaded') && | ||||
|           it('should remove the testability hook when destroy a component', () => { | ||||
|             const registry = TestBed.get(TestabilityRegistry); | ||||
|             expect(registry.getAllTestabilities().length).toEqual(0); | ||||
|             adapter.createComponent([]); | ||||
|             expect(registry.getAllTestabilities().length).toEqual(1); | ||||
|             adapter.registerCleanup(); | ||||
|             element.remove !(); | ||||
|             expect(registry.getAllTestabilities().length).toEqual(0); | ||||
|           }); | ||||
|     }); | ||||
| 
 | ||||
|   }); | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, NgZone, Sim | ||||
| import {async} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| 
 | ||||
| @ -75,57 +76,60 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should propagate changes to a downgraded component inside the ngZone', async(() => { | ||||
|          let appComponent: AppComponent; | ||||
|     fixmeIvy( | ||||
|         'FW-712: Rendering is being run on next "animation frame" rather than "Zone.microTaskEmpty" trigger') && | ||||
|         it('should propagate changes to a downgraded component inside the ngZone', async(() => { | ||||
|              const element = html('<my-app></my-app>'); | ||||
|              let appComponent: AppComponent; | ||||
| 
 | ||||
|          @Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'}) | ||||
|          class AppComponent { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            value !: number; | ||||
|            constructor() { appComponent = this; } | ||||
|          } | ||||
|              @Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'}) | ||||
|              class AppComponent { | ||||
|                value?: number; | ||||
|                constructor() { appComponent = this; } | ||||
|              } | ||||
| 
 | ||||
|          @Component({ | ||||
|            selector: 'my-child', | ||||
|            template: '<div>{{ valueFromPromise }}</div>', | ||||
|          }) | ||||
|          class ChildComponent { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            valueFromPromise !: number; | ||||
|            @Input() | ||||
|            set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); } | ||||
|              @Component({ | ||||
|                selector: 'my-child', | ||||
|                template: '<div>{{ valueFromPromise }}</div>', | ||||
|              }) | ||||
|              class ChildComponent { | ||||
|                valueFromPromise?: number; | ||||
|                @Input() | ||||
|                set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); } | ||||
| 
 | ||||
|            constructor(private zone: NgZone) {} | ||||
|                constructor(private zone: NgZone) {} | ||||
| 
 | ||||
|            ngOnChanges(changes: SimpleChanges) { | ||||
|              if (changes['value'].isFirstChange()) return; | ||||
|                ngOnChanges(changes: SimpleChanges) { | ||||
|                  if (changes['value'].isFirstChange()) return; | ||||
| 
 | ||||
|              this.zone.onMicrotaskEmpty.subscribe( | ||||
|                  () => { expect(element.textContent).toEqual('5'); }); | ||||
|                  // HACK(ivy): Using setTimeout allows this test to pass but hides the ivy renderer
 | ||||
|                  // timing BC.
 | ||||
|                  //  setTimeout(() => expect(element.textContent).toEqual('5'), 0);
 | ||||
|                  this.zone.onMicrotaskEmpty.subscribe( | ||||
|                      () => { expect(element.textContent).toEqual('5'); }); | ||||
| 
 | ||||
|              Promise.resolve().then(() => this.valueFromPromise = changes['value'].currentValue); | ||||
|            } | ||||
|          } | ||||
|                  // Create a micro-task to update the value to be rendered asynchronously.
 | ||||
|                  Promise.resolve().then( | ||||
|                      () => this.valueFromPromise = changes['value'].currentValue); | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [AppComponent, ChildComponent], | ||||
|            entryComponents: [AppComponent], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [AppComponent, ChildComponent], | ||||
|                entryComponents: [AppComponent], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = angular.module('ng1', []).directive( | ||||
|              'myApp', downgradeComponent({component: AppComponent})); | ||||
|              const ng1Module = angular.module('ng1', []).directive( | ||||
|                  'myApp', downgradeComponent({component: AppComponent})); | ||||
| 
 | ||||
| 
 | ||||
|          const element = html('<my-app></my-app>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|            appComponent.value = 5; | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|                appComponent.value = 5; | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     // This test demonstrates https://github.com/angular/angular/issues/6385
 | ||||
|     // which was invalidly fixed by https://github.com/angular/angular/pull/6386
 | ||||
|  | ||||
| @ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, destroyPlat | ||||
| import {async} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| 
 | ||||
| @ -21,78 +22,82 @@ withEachNg1Version(() => { | ||||
|     beforeEach(() => destroyPlatform()); | ||||
|     afterEach(() => destroyPlatform()); | ||||
| 
 | ||||
|     it('should instantiate ng2 in ng1 template and project content', async(() => { | ||||
|     fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|         it('should instantiate ng2 in ng1 template and project content', async(() => { | ||||
| 
 | ||||
|          // the ng2 component that will be used in ng1 (downgraded)
 | ||||
|          @Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`}) | ||||
|          class Ng2Component { | ||||
|            prop = 'NG2'; | ||||
|            ngContent = 'ng2-content'; | ||||
|          } | ||||
|              // the ng2 component that will be used in ng1 (downgraded)
 | ||||
|              @Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`}) | ||||
|              class Ng2Component { | ||||
|                prop = 'NG2'; | ||||
|                ngContent = 'ng2-content'; | ||||
|              } | ||||
| 
 | ||||
|          // our upgrade module to host the component to downgrade
 | ||||
|          @NgModule({ | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              // our upgrade module to host the component to downgrade
 | ||||
|              @NgModule({ | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          // the ng1 app module that will consume the downgraded component
 | ||||
|          const ng1Module = angular | ||||
|                                .module('ng1', []) | ||||
|                                // create an ng1 facade of the ng2 component
 | ||||
|                                .directive('ng2', downgradeComponent({component: Ng2Component})) | ||||
|                                .run(($rootScope: angular.IRootScopeService) => { | ||||
|                                  $rootScope['prop'] = 'NG1'; | ||||
|                                  $rootScope['ngContent'] = 'ng1-content'; | ||||
|                                }); | ||||
|              // the ng1 app module that will consume the downgraded component
 | ||||
|              const ng1Module = angular | ||||
|                                    .module('ng1', []) | ||||
|                                    // create an ng1 facade of the ng2 component
 | ||||
|                                    .directive('ng2', downgradeComponent({component: Ng2Component})) | ||||
|                                    .run(($rootScope: angular.IRootScopeService) => { | ||||
|                                      $rootScope['prop'] = 'NG1'; | ||||
|                                      $rootScope['ngContent'] = 'ng1-content'; | ||||
|                                    }); | ||||
| 
 | ||||
|          const element = html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>'); | ||||
|              const element = | ||||
|                  html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|            expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]'); | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|                expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should correctly project structural directives', async(() => { | ||||
|          @Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'}) | ||||
|          class Ng2Component { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Input() itemId !: string; | ||||
|          } | ||||
|     fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|         it('should correctly project structural directives', async(() => { | ||||
|              @Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'}) | ||||
|              class Ng2Component { | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Input() itemId !: string; | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = angular.module('ng1', []) | ||||
|                                .directive('ng2', downgradeComponent({component: Ng2Component})) | ||||
|                                .run(($rootScope: angular.IRootScopeService) => { | ||||
|                                  $rootScope['items'] = [ | ||||
|                                    {id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]}, | ||||
|                                    {id: 'c', subitems: [7, 8, 9]} | ||||
|                                  ]; | ||||
|                                }); | ||||
|              const ng1Module = | ||||
|                  angular.module('ng1', []) | ||||
|                      .directive('ng2', downgradeComponent({component: Ng2Component})) | ||||
|                      .run(($rootScope: angular.IRootScopeService) => { | ||||
|                        $rootScope['items'] = [ | ||||
|                          {id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]}, | ||||
|                          {id: 'c', subitems: [7, 8, 9]} | ||||
|                        ]; | ||||
|                      }); | ||||
| 
 | ||||
|          const element = html(` | ||||
|              const element = html(` | ||||
|            <ng2 ng-repeat="item in items" [item-id]="item.id"> | ||||
|              <div ng-repeat="subitem in item.subitems">{{ subitem }}</div> | ||||
|            </ng2> | ||||
|          `);
 | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )'); | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should instantiate ng1 in ng2 template and project content', async(() => { | ||||
| 
 | ||||
| @ -140,38 +145,39 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should support multi-slot projection', async(() => { | ||||
|     fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|         it('should support multi-slot projection', async(() => { | ||||
| 
 | ||||
|          @Component({ | ||||
|            selector: 'ng2', | ||||
|            template: '2a(<ng-content select=".ng1a"></ng-content>)' + | ||||
|                '2b(<ng-content select=".ng1b"></ng-content>)' | ||||
|          }) | ||||
|          class Ng2Component { | ||||
|            constructor() {} | ||||
|          } | ||||
|              @Component({ | ||||
|                selector: 'ng2', | ||||
|                template: '2a(<ng-content select=".ng1a"></ng-content>)' + | ||||
|                    '2b(<ng-content select=".ng1b"></ng-content>)' | ||||
|              }) | ||||
|              class Ng2Component { | ||||
|                constructor() {} | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = angular.module('ng1', []).directive( | ||||
|              'ng2', downgradeComponent({component: Ng2Component})); | ||||
|              const ng1Module = angular.module('ng1', []).directive( | ||||
|                  'ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          // The ng-if on one of the projected children is here to make sure
 | ||||
|          // the correct slot is targeted even with structural directives in play.
 | ||||
|          const element = html( | ||||
|              '<ng2><div ng-if="true" class="ng1a">1a</div><div' + | ||||
|              ' class="ng1b">1b</div></ng2>'); | ||||
|              // The ng-if on one of the projected children is here to make sure
 | ||||
|              // the correct slot is targeted even with structural directives in play.
 | ||||
|              const element = html( | ||||
|                  '<ng2><div ng-if="true" class="ng1a">1a</div><div' + | ||||
|                  ' class="ng1b">1b</div></ng2>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|            expect(document.body.textContent).toEqual('2a(1a)2b(1b)'); | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|                expect(document.body.textContent).toEqual('2a(1a)2b(1b)'); | ||||
|              }); | ||||
|            })); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -10,6 +10,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Compiler, Component, Compone | ||||
| import {async, fakeAsync, tick} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| 
 | ||||
| @ -21,104 +22,106 @@ withEachNg1Version(() => { | ||||
|     beforeEach(() => destroyPlatform()); | ||||
|     afterEach(() => destroyPlatform()); | ||||
| 
 | ||||
|     it('should bind properties, events', async(() => { | ||||
|          const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => { | ||||
|            $rootScope['name'] = 'world'; | ||||
|            $rootScope['dataA'] = 'A'; | ||||
|            $rootScope['dataB'] = 'B'; | ||||
|            $rootScope['modelA'] = 'initModelA'; | ||||
|            $rootScope['modelB'] = 'initModelB'; | ||||
|            $rootScope['eventA'] = '?'; | ||||
|            $rootScope['eventB'] = '?'; | ||||
|          }); | ||||
|     fixmeIvy('FW-716: Error: [$rootScope:inprog] $digest already in progress') && | ||||
|         it('should bind properties, events', async(() => { | ||||
|              const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => { | ||||
|                $rootScope['name'] = 'world'; | ||||
|                $rootScope['dataA'] = 'A'; | ||||
|                $rootScope['dataB'] = 'B'; | ||||
|                $rootScope['modelA'] = 'initModelA'; | ||||
|                $rootScope['modelB'] = 'initModelB'; | ||||
|                $rootScope['eventA'] = '?'; | ||||
|                $rootScope['eventB'] = '?'; | ||||
|              }); | ||||
| 
 | ||||
|          @Component({ | ||||
|            selector: 'ng2', | ||||
|            inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'], | ||||
|            outputs: [ | ||||
|              'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange' | ||||
|            ], | ||||
|            template: 'ignore: {{ignore}}; ' + | ||||
|                'literal: {{literal}}; interpolate: {{interpolate}}; ' + | ||||
|                'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' + | ||||
|                'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})' | ||||
|          }) | ||||
|          class Ng2Component implements OnChanges { | ||||
|            ngOnChangesCount = 0; | ||||
|            ignore = '-'; | ||||
|            literal = '?'; | ||||
|            interpolate = '?'; | ||||
|            oneWayA = '?'; | ||||
|            oneWayB = '?'; | ||||
|            twoWayA = '?'; | ||||
|            twoWayB = '?'; | ||||
|            eventA = new EventEmitter(); | ||||
|            eventB = new EventEmitter(); | ||||
|            twoWayAEmitter = new EventEmitter(); | ||||
|            twoWayBEmitter = new EventEmitter(); | ||||
|              @Component({ | ||||
|                selector: 'ng2', | ||||
|                inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'], | ||||
|                outputs: [ | ||||
|                  'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', | ||||
|                  'twoWayBEmitter: twoWayBChange' | ||||
|                ], | ||||
|                template: 'ignore: {{ignore}}; ' + | ||||
|                    'literal: {{literal}}; interpolate: {{interpolate}}; ' + | ||||
|                    'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' + | ||||
|                    'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})' | ||||
|              }) | ||||
|              class Ng2Component implements OnChanges { | ||||
|                ngOnChangesCount = 0; | ||||
|                ignore = '-'; | ||||
|                literal = '?'; | ||||
|                interpolate = '?'; | ||||
|                oneWayA = '?'; | ||||
|                oneWayB = '?'; | ||||
|                twoWayA = '?'; | ||||
|                twoWayB = '?'; | ||||
|                eventA = new EventEmitter(); | ||||
|                eventB = new EventEmitter(); | ||||
|                twoWayAEmitter = new EventEmitter(); | ||||
|                twoWayBEmitter = new EventEmitter(); | ||||
| 
 | ||||
|            ngOnChanges(changes: SimpleChanges) { | ||||
|              const assert = (prop: string, value: any) => { | ||||
|                const propVal = (this as any)[prop]; | ||||
|                if (propVal != value) { | ||||
|                  throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`); | ||||
|                ngOnChanges(changes: SimpleChanges) { | ||||
|                  const assert = (prop: string, value: any) => { | ||||
|                    const propVal = (this as any)[prop]; | ||||
|                    if (propVal != value) { | ||||
|                      throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`); | ||||
|                    } | ||||
|                  }; | ||||
| 
 | ||||
|                  const assertChange = (prop: string, value: any) => { | ||||
|                    assert(prop, value); | ||||
|                    if (!changes[prop]) { | ||||
|                      throw new Error(`Changes record for '${prop}' not found.`); | ||||
|                    } | ||||
|                    const actualValue = changes[prop].currentValue; | ||||
|                    if (actualValue != value) { | ||||
|                      throw new Error( | ||||
|                          `Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`); | ||||
|                    } | ||||
|                  }; | ||||
| 
 | ||||
|                  switch (this.ngOnChangesCount++) { | ||||
|                    case 0: | ||||
|                      assert('ignore', '-'); | ||||
|                      assertChange('literal', 'Text'); | ||||
|                      assertChange('interpolate', 'Hello world'); | ||||
|                      assertChange('oneWayA', 'A'); | ||||
|                      assertChange('oneWayB', 'B'); | ||||
|                      assertChange('twoWayA', 'initModelA'); | ||||
|                      assertChange('twoWayB', 'initModelB'); | ||||
| 
 | ||||
|                      this.twoWayAEmitter.emit('newA'); | ||||
|                      this.twoWayBEmitter.emit('newB'); | ||||
|                      this.eventA.emit('aFired'); | ||||
|                      this.eventB.emit('bFired'); | ||||
|                      break; | ||||
|                    case 1: | ||||
|                      assertChange('twoWayA', 'newA'); | ||||
|                      assertChange('twoWayB', 'newB'); | ||||
|                      break; | ||||
|                    case 2: | ||||
|                      assertChange('interpolate', 'Hello everyone'); | ||||
|                      break; | ||||
|                    default: | ||||
|                      throw new Error('Called too many times! ' + JSON.stringify(changes)); | ||||
|                  } | ||||
|                } | ||||
|              }; | ||||
| 
 | ||||
|              const assertChange = (prop: string, value: any) => { | ||||
|                assert(prop, value); | ||||
|                if (!changes[prop]) { | ||||
|                  throw new Error(`Changes record for '${prop}' not found.`); | ||||
|                } | ||||
|                const actualValue = changes[prop].currentValue; | ||||
|                if (actualValue != value) { | ||||
|                  throw new Error( | ||||
|                      `Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`); | ||||
|                } | ||||
|              }; | ||||
| 
 | ||||
|              switch (this.ngOnChangesCount++) { | ||||
|                case 0: | ||||
|                  assert('ignore', '-'); | ||||
|                  assertChange('literal', 'Text'); | ||||
|                  assertChange('interpolate', 'Hello world'); | ||||
|                  assertChange('oneWayA', 'A'); | ||||
|                  assertChange('oneWayB', 'B'); | ||||
|                  assertChange('twoWayA', 'initModelA'); | ||||
|                  assertChange('twoWayB', 'initModelB'); | ||||
| 
 | ||||
|                  this.twoWayAEmitter.emit('newA'); | ||||
|                  this.twoWayBEmitter.emit('newB'); | ||||
|                  this.eventA.emit('aFired'); | ||||
|                  this.eventB.emit('bFired'); | ||||
|                  break; | ||||
|                case 1: | ||||
|                  assertChange('twoWayA', 'newA'); | ||||
|                  assertChange('twoWayB', 'newB'); | ||||
|                  break; | ||||
|                case 2: | ||||
|                  assertChange('interpolate', 'Hello everyone'); | ||||
|                  break; | ||||
|                default: | ||||
|                  throw new Error('Called too many times! ' + JSON.stringify(changes)); | ||||
|              } | ||||
|            } | ||||
|          } | ||||
| 
 | ||||
|          ng1Module.directive('ng2', downgradeComponent({ | ||||
|                                component: Ng2Component, | ||||
|                              })); | ||||
|              ng1Module.directive('ng2', downgradeComponent({ | ||||
|                                    component: Ng2Component, | ||||
|                                  })); | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const element = html(` | ||||
|              const element = html(` | ||||
|            <div> | ||||
|              <ng2 literal="Text" interpolate="Hello {{name}}" | ||||
|                  bind-one-way-a="dataA" [one-way-b]="dataB" | ||||
| @ -127,23 +130,23 @@ withEachNg1Version(() => { | ||||
|              | modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}}; | ||||
|            </div>`);
 | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toEqual( | ||||
|                    'ignore: -; ' + | ||||
|                    'literal: Text; interpolate: Hello world; ' + | ||||
|                    'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' + | ||||
|                    'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toEqual( | ||||
|                        'ignore: -; ' + | ||||
|                        'literal: Text; interpolate: Hello world; ' + | ||||
|                        'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' + | ||||
|                        'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); | ||||
| 
 | ||||
|            $apply(upgrade, 'name = "everyone"'); | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toEqual( | ||||
|                    'ignore: -; ' + | ||||
|                    'literal: Text; interpolate: Hello everyone; ' + | ||||
|                    'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' + | ||||
|                    'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); | ||||
|          }); | ||||
|        })); | ||||
|                $apply(upgrade, 'name = "everyone"'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toEqual( | ||||
|                        'ignore: -; ' + | ||||
|                        'literal: Text; interpolate: Hello everyone; ' + | ||||
|                        'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' + | ||||
|                        'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should bind properties to onpush components', async(() => { | ||||
|          const ng1Module = angular.module('ng1', []).run( | ||||
| @ -186,57 +189,58 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should support two-way binding and event listener', async(() => { | ||||
|          const listenerSpy = jasmine.createSpy('$rootScope.listener'); | ||||
|          const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => { | ||||
|            $rootScope['value'] = 'world'; | ||||
|            $rootScope['listener'] = listenerSpy; | ||||
|          }); | ||||
|     fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') && | ||||
|         it('should support two-way binding and event listener', async(() => { | ||||
|              const listenerSpy = jasmine.createSpy('$rootScope.listener'); | ||||
|              const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => { | ||||
|                $rootScope['value'] = 'world'; | ||||
|                $rootScope['listener'] = listenerSpy; | ||||
|              }); | ||||
| 
 | ||||
|          @Component({selector: 'ng2', template: `model: {{model}};`}) | ||||
|          class Ng2Component implements OnChanges { | ||||
|            ngOnChangesCount = 0; | ||||
|            @Input() model = '?'; | ||||
|            @Output() modelChange = new EventEmitter(); | ||||
|              @Component({selector: 'ng2', template: `model: {{model}};`}) | ||||
|              class Ng2Component implements OnChanges { | ||||
|                ngOnChangesCount = 0; | ||||
|                @Input() model = '?'; | ||||
|                @Output() modelChange = new EventEmitter(); | ||||
| 
 | ||||
|            ngOnChanges(changes: SimpleChanges) { | ||||
|              switch (this.ngOnChangesCount++) { | ||||
|                case 0: | ||||
|                  expect(changes.model.currentValue).toBe('world'); | ||||
|                  this.modelChange.emit('newC'); | ||||
|                  break; | ||||
|                case 1: | ||||
|                  expect(changes.model.currentValue).toBe('newC'); | ||||
|                  break; | ||||
|                default: | ||||
|                  throw new Error('Called too many times! ' + JSON.stringify(changes)); | ||||
|                ngOnChanges(changes: SimpleChanges) { | ||||
|                  switch (this.ngOnChangesCount++) { | ||||
|                    case 0: | ||||
|                      expect(changes.model.currentValue).toBe('world'); | ||||
|                      this.modelChange.emit('newC'); | ||||
|                      break; | ||||
|                    case 1: | ||||
|                      expect(changes.model.currentValue).toBe('newC'); | ||||
|                      break; | ||||
|                    default: | ||||
|                      throw new Error('Called too many times! ' + JSON.stringify(changes)); | ||||
|                  } | ||||
|                } | ||||
|              } | ||||
|            } | ||||
|          } | ||||
| 
 | ||||
|          ng1Module.directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|              ng1Module.directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const element = html(` | ||||
|              const element = html(` | ||||
|           <div> | ||||
|             <ng2 [(model)]="value" (model-change)="listener($event)"></ng2> | ||||
|             | value: {{value}} | ||||
|           </div> | ||||
|         `);
 | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|            expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC'); | ||||
|            expect(listenerSpy).toHaveBeenCalledWith('newC'); | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { | ||||
|                expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC'); | ||||
|                expect(listenerSpy).toHaveBeenCalledWith('newC'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should run change-detection on every digest (by default)', async(() => { | ||||
|          let ng2Component: Ng2Component; | ||||
| @ -400,65 +404,66 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should initialize inputs in time for `ngOnChanges`', async(() => { | ||||
|          @Component({ | ||||
|            selector: 'ng2', | ||||
|            template: ` | ||||
|     fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') && | ||||
|         it('should initialize inputs in time for `ngOnChanges`', async(() => { | ||||
|              @Component({ | ||||
|                selector: 'ng2', | ||||
|                template: ` | ||||
|              ngOnChangesCount: {{ ngOnChangesCount }} | | ||||
|              firstChangesCount: {{ firstChangesCount }} | | ||||
|              initialValue: {{ initialValue }}` | ||||
|          }) | ||||
|          class Ng2Component implements OnChanges { | ||||
|            ngOnChangesCount = 0; | ||||
|            firstChangesCount = 0; | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            initialValue !: string; | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Input() foo !: string; | ||||
|              }) | ||||
|              class Ng2Component implements OnChanges { | ||||
|                ngOnChangesCount = 0; | ||||
|                firstChangesCount = 0; | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                initialValue !: string; | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Input() foo !: string; | ||||
| 
 | ||||
|            ngOnChanges(changes: SimpleChanges) { | ||||
|              this.ngOnChangesCount++; | ||||
|                ngOnChanges(changes: SimpleChanges) { | ||||
|                  this.ngOnChangesCount++; | ||||
| 
 | ||||
|              if (this.ngOnChangesCount === 1) { | ||||
|                this.initialValue = this.foo; | ||||
|                  if (this.ngOnChangesCount === 1) { | ||||
|                    this.initialValue = this.foo; | ||||
|                  } | ||||
| 
 | ||||
|                  if (changes['foo'] && changes['foo'].isFirstChange()) { | ||||
|                    this.firstChangesCount++; | ||||
|                  } | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|              if (changes['foo'] && changes['foo'].isFirstChange()) { | ||||
|                this.firstChangesCount++; | ||||
|              @NgModule({ | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
|            } | ||||
|          } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              const ng1Module = angular.module('ng1', []).directive( | ||||
|                  'ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          const ng1Module = angular.module('ng1', []).directive( | ||||
|              'ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          const element = html(` | ||||
|              const element = html(` | ||||
|            <ng2 [foo]="'foo'"></ng2> | ||||
|            <ng2 foo="bar"></ng2> | ||||
|            <ng2 [foo]="'baz'" ng-if="true"></ng2> | ||||
|            <ng2 foo="qux" ng-if="true"></ng2> | ||||
|          `);
 | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            const nodes = element.querySelectorAll('ng2'); | ||||
|            const expectedTextWith = (value: string) => | ||||
|                `ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`; | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                const nodes = element.querySelectorAll('ng2'); | ||||
|                const expectedTextWith = (value: string) => | ||||
|                    `ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`; | ||||
| 
 | ||||
|            expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo')); | ||||
|            expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar')); | ||||
|            expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz')); | ||||
|            expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux')); | ||||
|          }); | ||||
|        })); | ||||
|                expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo')); | ||||
|                expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar')); | ||||
|                expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz')); | ||||
|                expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux')); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should bind to ng-model', async(() => { | ||||
|          const ng1Module = angular.module('ng1', []).run( | ||||
| @ -515,94 +520,96 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should properly run cleanup when ng1 directive is destroyed', async(() => { | ||||
|     fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') && | ||||
|         it('should properly run cleanup when ng1 directive is destroyed', async(() => { | ||||
| 
 | ||||
|          let destroyed = false; | ||||
|          @Component({selector: 'ng2', template: 'test'}) | ||||
|          class Ng2Component implements OnDestroy { | ||||
|            ngOnDestroy() { destroyed = true; } | ||||
|          } | ||||
|              let destroyed = false; | ||||
|              @Component({selector: 'ng2', template: 'test'}) | ||||
|              class Ng2Component implements OnDestroy { | ||||
|                ngOnDestroy() { destroyed = true; } | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = | ||||
|              angular.module('ng1', []) | ||||
|                  .directive( | ||||
|                      'ng1', | ||||
|                      () => { return {template: '<div ng-if="!destroyIt"><ng2></ng2></div>'}; }) | ||||
|                  .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|          const element = html('<ng1></ng1>'); | ||||
|          platformBrowserDynamic().bootstrapModule(Ng2Module).then((ref) => { | ||||
|            const adapter = ref.injector.get(UpgradeModule) as UpgradeModule; | ||||
|            adapter.bootstrap(element, [ng1Module.name]); | ||||
|            expect(element.textContent).toContain('test'); | ||||
|            expect(destroyed).toBe(false); | ||||
|              const ng1Module = | ||||
|                  angular.module('ng1', []) | ||||
|                      .directive( | ||||
|                          'ng1', | ||||
|                          () => { return {template: '<div ng-if="!destroyIt"><ng2></ng2></div>'}; }) | ||||
|                      .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|              const element = html('<ng1></ng1>'); | ||||
|              platformBrowserDynamic().bootstrapModule(Ng2Module).then((ref) => { | ||||
|                const adapter = ref.injector.get(UpgradeModule) as UpgradeModule; | ||||
|                adapter.bootstrap(element, [ng1Module.name]); | ||||
|                expect(element.textContent).toContain('test'); | ||||
|                expect(destroyed).toBe(false); | ||||
| 
 | ||||
|            const $rootScope = adapter.$injector.get('$rootScope'); | ||||
|            $rootScope.$apply('destroyIt = true'); | ||||
|                const $rootScope = adapter.$injector.get('$rootScope'); | ||||
|                $rootScope.$apply('destroyIt = true'); | ||||
| 
 | ||||
|            expect(element.textContent).not.toContain('test'); | ||||
|            expect(destroyed).toBe(true); | ||||
|          }); | ||||
|        })); | ||||
|                expect(element.textContent).not.toContain('test'); | ||||
|                expect(destroyed).toBe(true); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should properly run cleanup with multiple levels of nesting', async(() => { | ||||
|          let destroyed = false; | ||||
|     fixmeIvy('FW-642: ASSERTION ERROR: Slot should have been initialized to NO_CHANGE') && | ||||
|         it('should properly run cleanup with multiple levels of nesting', async(() => { | ||||
|              let destroyed = false; | ||||
| 
 | ||||
|          @Component({ | ||||
|            selector: 'ng2-outer', | ||||
|            template: '<div *ngIf="!destroyIt"><ng1></ng1></div>', | ||||
|          }) | ||||
|          class Ng2OuterComponent { | ||||
|            @Input() destroyIt = false; | ||||
|          } | ||||
|              @Component({ | ||||
|                selector: 'ng2-outer', | ||||
|                template: '<div *ngIf="!destroyIt"><ng1></ng1></div>', | ||||
|              }) | ||||
|              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; } | ||||
|              } | ||||
| 
 | ||||
|          @Directive({selector: 'ng1'}) | ||||
|          class Ng1ComponentFacade extends UpgradeComponent { | ||||
|            constructor(elementRef: ElementRef, injector: Injector) { | ||||
|              super('ng1', elementRef, injector); | ||||
|            } | ||||
|          } | ||||
|              @Directive({selector: 'ng1'}) | ||||
|              class Ng1ComponentFacade extends UpgradeComponent { | ||||
|                constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                  super('ng1', elementRef, injector); | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|            declarations: [Ng1ComponentFacade, Ng2InnerComponent, Ng2OuterComponent], | ||||
|            entryComponents: [Ng2InnerComponent, Ng2OuterComponent], | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|                declarations: [Ng1ComponentFacade, Ng2InnerComponent, Ng2OuterComponent], | ||||
|                entryComponents: [Ng2InnerComponent, Ng2OuterComponent], | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = | ||||
|              angular.module('ng1', []) | ||||
|                  .directive('ng1', () => ({template: '<ng2-inner></ng2-inner>'})) | ||||
|                  .directive('ng2Inner', downgradeComponent({component: Ng2InnerComponent})) | ||||
|                  .directive('ng2Outer', downgradeComponent({component: Ng2OuterComponent})); | ||||
|              const ng1Module = | ||||
|                  angular.module('ng1', []) | ||||
|                      .directive('ng1', () => ({template: '<ng2-inner></ng2-inner>'})) | ||||
|                      .directive('ng2Inner', downgradeComponent({component: Ng2InnerComponent})) | ||||
|                      .directive('ng2Outer', downgradeComponent({component: Ng2OuterComponent})); | ||||
| 
 | ||||
|          const element = html('<ng2-outer [destroy-it]="destroyIt"></ng2-outer>'); | ||||
|              const element = html('<ng2-outer [destroy-it]="destroyIt"></ng2-outer>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            expect(element.textContent).toBe('test'); | ||||
|            expect(destroyed).toBe(false); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                expect(element.textContent).toBe('test'); | ||||
|                expect(destroyed).toBe(false); | ||||
| 
 | ||||
|            $apply(upgrade, 'destroyIt = true'); | ||||
|                $apply(upgrade, 'destroyIt = true'); | ||||
| 
 | ||||
|            expect(element.textContent).toBe(''); | ||||
|            expect(destroyed).toBe(true); | ||||
|          }); | ||||
|        })); | ||||
|                expect(element.textContent).toBe(''); | ||||
|                expect(destroyed).toBe(true); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should work when compiled outside the dom (by fallback to the root ng2.injector)', | ||||
|        async(() => { | ||||
| @ -704,86 +711,88 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should respect hierarchical dependency injection for ng2', async(() => { | ||||
|          @Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'}) | ||||
|          class ParentComponent { | ||||
|          } | ||||
|     fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|         it('should respect hierarchical dependency injection for ng2', async(() => { | ||||
|              @Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'}) | ||||
|              class ParentComponent { | ||||
|              } | ||||
| 
 | ||||
|          @Component({selector: 'child', template: 'child'}) | ||||
|          class ChildComponent { | ||||
|            constructor(parent: ParentComponent) {} | ||||
|          } | ||||
|              @Component({selector: 'child', template: 'child'}) | ||||
|              class ChildComponent { | ||||
|                constructor(parent: ParentComponent) {} | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [ParentComponent, ChildComponent], | ||||
|            entryComponents: [ParentComponent, ChildComponent], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [ParentComponent, ChildComponent], | ||||
|                entryComponents: [ParentComponent, ChildComponent], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = | ||||
|              angular.module('ng1', []) | ||||
|                  .directive('parent', downgradeComponent({component: ParentComponent})) | ||||
|                  .directive('child', downgradeComponent({component: ChildComponent})); | ||||
|              const ng1Module = | ||||
|                  angular.module('ng1', []) | ||||
|                      .directive('parent', downgradeComponent({component: ParentComponent})) | ||||
|                      .directive('child', downgradeComponent({component: ChildComponent})); | ||||
| 
 | ||||
|          const element = html('<parent><child></child></parent>'); | ||||
|              const element = html('<parent><child></child></parent>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            expect(multiTrim(document.body.textContent)).toBe('parent(child)'); | ||||
|          }); | ||||
|        })); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                expect(multiTrim(document.body.textContent)).toBe('parent(child)'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should work with ng2 lazy loaded components', async(() => { | ||||
|     fixmeIvy('FW-561: Runtime compiler is not loaded') && | ||||
|         it('should work with ng2 lazy loaded components', async(() => { | ||||
| 
 | ||||
|          let componentInjector: Injector; | ||||
|              let componentInjector: Injector; | ||||
| 
 | ||||
|          @Component({selector: 'ng2', template: ''}) | ||||
|          class Ng2Component { | ||||
|            constructor(injector: Injector) { componentInjector = injector; } | ||||
|          } | ||||
|              @Component({selector: 'ng2', template: ''}) | ||||
|              class Ng2Component { | ||||
|                constructor(injector: Injector) { componentInjector = injector; } | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          @Component({template: ''}) | ||||
|          class LazyLoadedComponent { | ||||
|            constructor(public module: NgModuleRef<any>) {} | ||||
|          } | ||||
|              @Component({template: ''}) | ||||
|              class LazyLoadedComponent { | ||||
|                constructor(public module: NgModuleRef<any>) {} | ||||
|              } | ||||
| 
 | ||||
|          @NgModule({ | ||||
|            declarations: [LazyLoadedComponent], | ||||
|            entryComponents: [LazyLoadedComponent], | ||||
|          }) | ||||
|          class LazyLoadedModule { | ||||
|          } | ||||
|              @NgModule({ | ||||
|                declarations: [LazyLoadedComponent], | ||||
|                entryComponents: [LazyLoadedComponent], | ||||
|              }) | ||||
|              class LazyLoadedModule { | ||||
|              } | ||||
| 
 | ||||
|          const ng1Module = angular.module('ng1', []).directive( | ||||
|              'ng2', downgradeComponent({component: Ng2Component})); | ||||
|              const ng1Module = angular.module('ng1', []).directive( | ||||
|                  'ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          const element = html('<ng2></ng2>'); | ||||
|              const element = html('<ng2></ng2>'); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            const modInjector = upgrade.injector; | ||||
|            // Emulate the router lazy loading a module and creating a component
 | ||||
|            const compiler = modInjector.get(Compiler); | ||||
|            const modFactory = compiler.compileModuleSync(LazyLoadedModule); | ||||
|            const childMod = modFactory.create(modInjector); | ||||
|            const cmpFactory = | ||||
|                childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent) !; | ||||
|            const lazyCmp = cmpFactory.create(componentInjector); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                const modInjector = upgrade.injector; | ||||
|                // Emulate the router lazy loading a module and creating a component
 | ||||
|                const compiler = modInjector.get(Compiler); | ||||
|                const modFactory = compiler.compileModuleSync(LazyLoadedModule); | ||||
|                const childMod = modFactory.create(modInjector); | ||||
|                const cmpFactory = | ||||
|                    childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent) !; | ||||
|                const lazyCmp = cmpFactory.create(componentInjector); | ||||
| 
 | ||||
|            expect(lazyCmp.instance.module.injector).toBe(childMod.injector); | ||||
|          }); | ||||
|                expect(lazyCmp.instance.module.injector).toBe(childMod.injector); | ||||
|              }); | ||||
| 
 | ||||
|        })); | ||||
|            })); | ||||
| 
 | ||||
|     it('should throw if `downgradedModule` is specified', async(() => { | ||||
|          @Component({selector: 'ng2', template: ''}) | ||||
|  | ||||
| @ -11,6 +11,7 @@ import {async, fakeAsync, tick} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {downgradeComponent, downgradeModule} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| import {$EXCEPTION_HANDLER, $ROOT_SCOPE, INJECTOR_KEY, LAZY_MODULE_REF} from '@angular/upgrade/static/src/common/constants'; | ||||
| @ -131,62 +132,64 @@ withEachNg1Version(() => { | ||||
|            }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should support using an upgraded service', async(() => { | ||||
|            class Ng2Service { | ||||
|              constructor(@Inject('ng1Value') private ng1Value: string) {} | ||||
|              getValue = () => `${this.ng1Value}-bar`; | ||||
|            } | ||||
|       fixmeIvy('FW-718: upgraded service not being initialized correctly on the injector') && | ||||
|           it('should support using an upgraded service', async(() => { | ||||
|                class Ng2Service { | ||||
|                  constructor(@Inject('ng1Value') private ng1Value: string) {} | ||||
|                  getValue = () => `${this.ng1Value}-bar`; | ||||
|                } | ||||
| 
 | ||||
|            @Component({selector: 'ng2', template: '{{ value }}'}) | ||||
|            class Ng2Component { | ||||
|              value: string; | ||||
|              constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); } | ||||
|            } | ||||
|                @Component({selector: 'ng2', template: '{{ value }}'}) | ||||
|                class Ng2Component { | ||||
|                  value: string; | ||||
|                  constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); } | ||||
|                } | ||||
| 
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule], | ||||
|              providers: [ | ||||
|                Ng2Service, | ||||
|                { | ||||
|                  provide: 'ng1Value', | ||||
|                  useFactory: (i: angular.IInjectorService) => i.get('ng1Value'), | ||||
|                  deps: ['$injector'], | ||||
|                }, | ||||
|              ], | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule], | ||||
|                  providers: [ | ||||
|                    Ng2Service, | ||||
|                    { | ||||
|                      provide: 'ng1Value', | ||||
|                      useFactory: (i: angular.IInjectorService) => i.get('ng1Value'), | ||||
|                      deps: ['$injector'], | ||||
|                    }, | ||||
|                  ], | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|            const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|            const ng1Module = | ||||
|                angular.module('ng1', [lazyModuleName]) | ||||
|                    .directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                    .value('ng1Value', 'foo'); | ||||
|                const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                    platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|                const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1', [lazyModuleName]) | ||||
|                        .directive( | ||||
|                            'ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                        .value('ng1Value', 'foo'); | ||||
| 
 | ||||
|            const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>'); | ||||
|            const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|            const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
|                const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>'); | ||||
|                const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|                const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
| 
 | ||||
|            expect(element.textContent).toBe(''); | ||||
|            expect(() => $injector.get(INJECTOR_KEY)).toThrowError(); | ||||
|                expect(element.textContent).toBe(''); | ||||
|                expect(() => $injector.get(INJECTOR_KEY)).toThrowError(); | ||||
| 
 | ||||
|            $rootScope.$apply('loadNg2 = true'); | ||||
|            expect(element.textContent).toBe(''); | ||||
|            expect(() => $injector.get(INJECTOR_KEY)).toThrowError(); | ||||
|                $rootScope.$apply('loadNg2 = true'); | ||||
|                expect(element.textContent).toBe(''); | ||||
|                expect(() => $injector.get(INJECTOR_KEY)).toThrowError(); | ||||
| 
 | ||||
|            // Wait for the module to be bootstrapped.
 | ||||
|            setTimeout(() => { | ||||
|              expect(() => $injector.get(INJECTOR_KEY)).not.toThrow(); | ||||
|                // Wait for the module to be bootstrapped.
 | ||||
|                setTimeout(() => { | ||||
|                  expect(() => $injector.get(INJECTOR_KEY)).not.toThrow(); | ||||
| 
 | ||||
|              // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|              setTimeout(() => expect(element.textContent).toBe('foo-bar')); | ||||
|            }); | ||||
|          })); | ||||
|                  // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|                  setTimeout(() => expect(element.textContent).toBe('foo-bar')); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should create components inside the Angular zone', async(() => { | ||||
|            @Component({selector: 'ng2', template: 'In the zone: {{ inTheZone }}'}) | ||||
| @ -222,99 +225,103 @@ withEachNg1Version(() => { | ||||
|            }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should destroy components inside the Angular zone', async(() => { | ||||
|            let destroyedInTheZone = false; | ||||
|       fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') && | ||||
|           it('should destroy components inside the Angular zone', async(() => { | ||||
|                let destroyedInTheZone = false; | ||||
| 
 | ||||
|            @Component({selector: 'ng2', template: ''}) | ||||
|            class Ng2Component implements OnDestroy { | ||||
|              ngOnDestroy() { destroyedInTheZone = NgZone.isInAngularZone(); } | ||||
|            } | ||||
|                @Component({selector: 'ng2', template: ''}) | ||||
|                class Ng2Component implements OnDestroy { | ||||
|                  ngOnDestroy() { destroyedInTheZone = NgZone.isInAngularZone(); } | ||||
|                } | ||||
| 
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule], | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule], | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|            const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|            const ng1Module = | ||||
|                angular.module('ng1', [lazyModuleName]) | ||||
|                    .directive( | ||||
|                        'ng2', downgradeComponent({component: Ng2Component, propagateDigest})); | ||||
|                const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                    platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|                const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1', [lazyModuleName]) | ||||
|                        .directive( | ||||
|                            'ng2', downgradeComponent({component: Ng2Component, propagateDigest})); | ||||
| 
 | ||||
|            const element = html('<ng2 ng-if="!hideNg2"></ng2>'); | ||||
|            const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|            const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
|                const element = html('<ng2 ng-if="!hideNg2"></ng2>'); | ||||
|                const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|                const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
| 
 | ||||
|            // Wait for the module to be bootstrapped.
 | ||||
|            setTimeout(() => { | ||||
|              $rootScope.$apply('hideNg2 = true'); | ||||
|              expect(destroyedInTheZone).toBe(true); | ||||
|            }); | ||||
|          })); | ||||
|                // Wait for the module to be bootstrapped.
 | ||||
|                setTimeout(() => { | ||||
|                  $rootScope.$apply('hideNg2 = true'); | ||||
|                  expect(destroyedInTheZone).toBe(true); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should propagate input changes inside the Angular zone', async(() => { | ||||
|            let ng2Component: Ng2Component; | ||||
|       fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') && | ||||
|           it('should propagate input changes inside the Angular zone', async(() => { | ||||
|                let ng2Component: Ng2Component; | ||||
| 
 | ||||
|            @Component({selector: 'ng2', template: ''}) | ||||
|            class Ng2Component implements OnChanges { | ||||
|              @Input() attrInput = 'foo'; | ||||
|              @Input() propInput = 'foo'; | ||||
|                @Component({selector: 'ng2', template: ''}) | ||||
|                class Ng2Component implements OnChanges { | ||||
|                  @Input() attrInput = 'foo'; | ||||
|                  @Input() propInput = 'foo'; | ||||
| 
 | ||||
|              constructor() { ng2Component = this; } | ||||
|              ngOnChanges() {} | ||||
|            } | ||||
|                  constructor() { ng2Component = this; } | ||||
|                  ngOnChanges() {} | ||||
|                } | ||||
| 
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule], | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule], | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|            const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|            const ng1Module = | ||||
|                angular.module('ng1', [lazyModuleName]) | ||||
|                    .directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                    .run(($rootScope: angular.IRootScopeService) => { | ||||
|                      $rootScope.attrVal = 'bar'; | ||||
|                      $rootScope.propVal = 'bar'; | ||||
|                    }); | ||||
|                const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                    platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|                const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1', [lazyModuleName]) | ||||
|                        .directive( | ||||
|                            'ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                        .run(($rootScope: angular.IRootScopeService) => { | ||||
|                          $rootScope.attrVal = 'bar'; | ||||
|                          $rootScope.propVal = 'bar'; | ||||
|                        }); | ||||
| 
 | ||||
|            const element = html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>'); | ||||
|            const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|            const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
|                const element = | ||||
|                    html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>'); | ||||
|                const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|                const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
| 
 | ||||
|            setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|              setTimeout(() => {  // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|                const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true); | ||||
|                const changesSpy = | ||||
|                    spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone); | ||||
|                setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|                  setTimeout(() => {  // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|                    const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true); | ||||
|                    const changesSpy = | ||||
|                        spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone); | ||||
| 
 | ||||
|                expect(ng2Component.attrInput).toBe('bar'); | ||||
|                expect(ng2Component.propInput).toBe('bar'); | ||||
|                    expect(ng2Component.attrInput).toBe('bar'); | ||||
|                    expect(ng2Component.propInput).toBe('bar'); | ||||
| 
 | ||||
|                $rootScope.$apply('attrVal = "baz"'); | ||||
|                expect(ng2Component.attrInput).toBe('baz'); | ||||
|                expect(ng2Component.propInput).toBe('bar'); | ||||
|                expect(changesSpy).toHaveBeenCalledTimes(1); | ||||
|                    $rootScope.$apply('attrVal = "baz"'); | ||||
|                    expect(ng2Component.attrInput).toBe('baz'); | ||||
|                    expect(ng2Component.propInput).toBe('bar'); | ||||
|                    expect(changesSpy).toHaveBeenCalledTimes(1); | ||||
| 
 | ||||
|                $rootScope.$apply('propVal = "qux"'); | ||||
|                expect(ng2Component.attrInput).toBe('baz'); | ||||
|                expect(ng2Component.propInput).toBe('qux'); | ||||
|                expect(changesSpy).toHaveBeenCalledTimes(2); | ||||
|              }); | ||||
|            }); | ||||
|          })); | ||||
|                    $rootScope.$apply('propVal = "qux"'); | ||||
|                    expect(ng2Component.attrInput).toBe('baz'); | ||||
|                    expect(ng2Component.propInput).toBe('qux'); | ||||
|                    expect(changesSpy).toHaveBeenCalledTimes(2); | ||||
|                  }); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should wire up the component for change detection', async(() => { | ||||
|            @Component( | ||||
| @ -358,209 +365,215 @@ withEachNg1Version(() => { | ||||
|            }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should run the lifecycle hooks in the correct order', async(() => { | ||||
|            const logs: string[] = []; | ||||
|            let rootScope: angular.IRootScopeService; | ||||
|       fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') && | ||||
|           fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|           it('should run the lifecycle hooks in the correct order', async(() => { | ||||
|                const logs: string[] = []; | ||||
|                let rootScope: angular.IRootScopeService; | ||||
| 
 | ||||
|            @Component({ | ||||
|              selector: 'ng2', | ||||
|              template: ` | ||||
|                @Component({ | ||||
|                  selector: 'ng2', | ||||
|                  template: ` | ||||
|                {{ value }} | ||||
|                <button (click)="value = 'qux'"></button> | ||||
|                <ng-content></ng-content> | ||||
|              ` | ||||
|            }) | ||||
|            class Ng2Component implements AfterContentChecked, | ||||
|                AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, | ||||
|                OnInit { | ||||
|              @Input() value = 'foo'; | ||||
|                }) | ||||
|                class Ng2Component implements AfterContentChecked, | ||||
|                    AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, | ||||
|                    OnInit { | ||||
|                  @Input() value = 'foo'; | ||||
| 
 | ||||
|              ngAfterContentChecked() { this.log('AfterContentChecked'); } | ||||
|              ngAfterContentInit() { this.log('AfterContentInit'); } | ||||
|              ngAfterViewChecked() { this.log('AfterViewChecked'); } | ||||
|              ngAfterViewInit() { this.log('AfterViewInit'); } | ||||
|              ngDoCheck() { this.log('DoCheck'); } | ||||
|              ngOnChanges() { this.log('OnChanges'); } | ||||
|              ngOnDestroy() { this.log('OnDestroy'); } | ||||
|              ngOnInit() { this.log('OnInit'); } | ||||
|                  ngAfterContentChecked() { this.log('AfterContentChecked'); } | ||||
|                  ngAfterContentInit() { this.log('AfterContentInit'); } | ||||
|                  ngAfterViewChecked() { this.log('AfterViewChecked'); } | ||||
|                  ngAfterViewInit() { this.log('AfterViewInit'); } | ||||
|                  ngDoCheck() { this.log('DoCheck'); } | ||||
|                  ngOnChanges() { this.log('OnChanges'); } | ||||
|                  ngOnDestroy() { this.log('OnDestroy'); } | ||||
|                  ngOnInit() { this.log('OnInit'); } | ||||
| 
 | ||||
|              private log(hook: string) { logs.push(`${hook}(${this.value})`); } | ||||
|            } | ||||
|                  private log(hook: string) { logs.push(`${hook}(${this.value})`); } | ||||
|                } | ||||
| 
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule], | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule], | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|            const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|            const ng1Module = | ||||
|                angular.module('ng1', [lazyModuleName]) | ||||
|                    .directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                    .run(($rootScope: angular.IRootScopeService) => { | ||||
|                      rootScope = $rootScope; | ||||
|                      rootScope.value = 'bar'; | ||||
|                    }); | ||||
|                const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                    platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|                const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1', [lazyModuleName]) | ||||
|                        .directive( | ||||
|                            'ng2', downgradeComponent({component: Ng2Component, propagateDigest})) | ||||
|                        .run(($rootScope: angular.IRootScopeService) => { | ||||
|                          rootScope = $rootScope; | ||||
|                          rootScope.value = 'bar'; | ||||
|                        }); | ||||
| 
 | ||||
|            const element = | ||||
|                html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>'); | ||||
|            angular.bootstrap(element, [ng1Module.name]); | ||||
|                const element = | ||||
|                    html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>'); | ||||
|                angular.bootstrap(element, [ng1Module.name]); | ||||
| 
 | ||||
|            setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|              setTimeout(() => {  // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|                const button = element.querySelector('button') !; | ||||
|                setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|                  setTimeout(() => {  // Wait for `$evalAsync()` to propagate inputs.
 | ||||
|                    const button = element.querySelector('button') !; | ||||
| 
 | ||||
|                // Once initialized.
 | ||||
|                expect(multiTrim(element.textContent)).toBe('bar Content'); | ||||
|                expect(logs).toEqual([ | ||||
|                  // `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
 | ||||
|                  'OnChanges(bar)', | ||||
|                  // Initial CD triggered directly through the `detectChanges()` or `inputChanges`
 | ||||
|                  // $watcher (for `propagateDigest` true/false respectively).
 | ||||
|                  'OnInit(bar)', | ||||
|                  'DoCheck(bar)', | ||||
|                  'AfterContentInit(bar)', | ||||
|                  'AfterContentChecked(bar)', | ||||
|                  'AfterViewInit(bar)', | ||||
|                  'AfterViewChecked(bar)', | ||||
|                  ...(propagateDigest ? | ||||
|                          [ | ||||
|                            // CD triggered directly through the `detectChanges()` $watcher (2nd
 | ||||
|                            // $digest).
 | ||||
|                            'DoCheck(bar)', | ||||
|                            'AfterContentChecked(bar)', | ||||
|                            'AfterViewChecked(bar)', | ||||
|                          ] : | ||||
|                          []), | ||||
|                  // CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
 | ||||
|                  'DoCheck(bar)', | ||||
|                  'AfterContentChecked(bar)', | ||||
|                  'AfterViewChecked(bar)', | ||||
|                ]); | ||||
|                logs.length = 0; | ||||
|                    // Once initialized.
 | ||||
|                    expect(multiTrim(element.textContent)).toBe('bar Content'); | ||||
|                    expect(logs).toEqual([ | ||||
|                      // `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
 | ||||
|                      'OnChanges(bar)', | ||||
|                      // Initial CD triggered directly through the `detectChanges()` or
 | ||||
|                      // `inputChanges`
 | ||||
|                      // $watcher (for `propagateDigest` true/false respectively).
 | ||||
|                      'OnInit(bar)', | ||||
|                      'DoCheck(bar)', | ||||
|                      'AfterContentInit(bar)', | ||||
|                      'AfterContentChecked(bar)', | ||||
|                      'AfterViewInit(bar)', | ||||
|                      'AfterViewChecked(bar)', | ||||
|                      ...(propagateDigest ? | ||||
|                              [ | ||||
|                                // CD triggered directly through the `detectChanges()` $watcher (2nd
 | ||||
|                                // $digest).
 | ||||
|                                'DoCheck(bar)', | ||||
|                                'AfterContentChecked(bar)', | ||||
|                                'AfterViewChecked(bar)', | ||||
|                              ] : | ||||
|                              []), | ||||
|                      // CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
 | ||||
|                      'DoCheck(bar)', | ||||
|                      'AfterContentChecked(bar)', | ||||
|                      'AfterViewChecked(bar)', | ||||
|                    ]); | ||||
|                    logs.length = 0; | ||||
| 
 | ||||
|                // Change inputs and run `$digest`.
 | ||||
|                rootScope.$apply('value = "baz"'); | ||||
|                expect(multiTrim(element.textContent)).toBe('baz Content'); | ||||
|                expect(logs).toEqual([ | ||||
|                  // `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
 | ||||
|                  'OnChanges(baz)', | ||||
|                  // `propagateDigest: true` (3 CD runs):
 | ||||
|                  //   - CD triggered due to entering/leaving the NgZone (in `inputChanges`
 | ||||
|                  //   $watcher).
 | ||||
|                  //   - CD triggered directly through the `detectChanges()` $watcher.
 | ||||
|                  //   - CD triggered due to entering/leaving the NgZone (in `detectChanges`
 | ||||
|                  //   $watcher).
 | ||||
|                  // `propagateDigest: false` (2 CD runs):
 | ||||
|                  //   - CD triggered directly through the `inputChanges` $watcher.
 | ||||
|                  //   - CD triggered due to entering/leaving the NgZone (in `inputChanges`
 | ||||
|                  //   $watcher).
 | ||||
|                  'DoCheck(baz)', | ||||
|                  'AfterContentChecked(baz)', | ||||
|                  'AfterViewChecked(baz)', | ||||
|                  'DoCheck(baz)', | ||||
|                  'AfterContentChecked(baz)', | ||||
|                  'AfterViewChecked(baz)', | ||||
|                  ...(propagateDigest ? | ||||
|                          [ | ||||
|                            'DoCheck(baz)', | ||||
|                            'AfterContentChecked(baz)', | ||||
|                            'AfterViewChecked(baz)', | ||||
|                          ] : | ||||
|                          []), | ||||
|                ]); | ||||
|                logs.length = 0; | ||||
|                    // Change inputs and run `$digest`.
 | ||||
|                    rootScope.$apply('value = "baz"'); | ||||
|                    expect(multiTrim(element.textContent)).toBe('baz Content'); | ||||
|                    expect(logs).toEqual([ | ||||
|                      // `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
 | ||||
|                      'OnChanges(baz)', | ||||
|                      // `propagateDigest: true` (3 CD runs):
 | ||||
|                      //   - CD triggered due to entering/leaving the NgZone (in `inputChanges`
 | ||||
|                      //   $watcher).
 | ||||
|                      //   - CD triggered directly through the `detectChanges()` $watcher.
 | ||||
|                      //   - CD triggered due to entering/leaving the NgZone (in `detectChanges`
 | ||||
|                      //   $watcher).
 | ||||
|                      // `propagateDigest: false` (2 CD runs):
 | ||||
|                      //   - CD triggered directly through the `inputChanges` $watcher.
 | ||||
|                      //   - CD triggered due to entering/leaving the NgZone (in `inputChanges`
 | ||||
|                      //   $watcher).
 | ||||
|                      'DoCheck(baz)', | ||||
|                      'AfterContentChecked(baz)', | ||||
|                      'AfterViewChecked(baz)', | ||||
|                      'DoCheck(baz)', | ||||
|                      'AfterContentChecked(baz)', | ||||
|                      'AfterViewChecked(baz)', | ||||
|                      ...(propagateDigest ? | ||||
|                              [ | ||||
|                                'DoCheck(baz)', | ||||
|                                'AfterContentChecked(baz)', | ||||
|                                'AfterViewChecked(baz)', | ||||
|                              ] : | ||||
|                              []), | ||||
|                    ]); | ||||
|                    logs.length = 0; | ||||
| 
 | ||||
|                // Run `$digest` (without changing inputs).
 | ||||
|                rootScope.$digest(); | ||||
|                expect(multiTrim(element.textContent)).toBe('baz Content'); | ||||
|                expect(logs).toEqual( | ||||
|                    propagateDigest ? | ||||
|                        [ | ||||
|                          // CD triggered directly through the `detectChanges()` $watcher.
 | ||||
|                          'DoCheck(baz)', | ||||
|                          'AfterContentChecked(baz)', | ||||
|                          'AfterViewChecked(baz)', | ||||
|                          // CD triggered due to entering/leaving the NgZone (in the above $watcher).
 | ||||
|                          'DoCheck(baz)', | ||||
|                          'AfterContentChecked(baz)', | ||||
|                          'AfterViewChecked(baz)', | ||||
|                        ] : | ||||
|                        []); | ||||
|                logs.length = 0; | ||||
|                    // Run `$digest` (without changing inputs).
 | ||||
|                    rootScope.$digest(); | ||||
|                    expect(multiTrim(element.textContent)).toBe('baz Content'); | ||||
|                    expect(logs).toEqual( | ||||
|                        propagateDigest ? | ||||
|                            [ | ||||
|                              // CD triggered directly through the `detectChanges()` $watcher.
 | ||||
|                              'DoCheck(baz)', | ||||
|                              'AfterContentChecked(baz)', | ||||
|                              'AfterViewChecked(baz)', | ||||
|                              // CD triggered due to entering/leaving the NgZone (in the above
 | ||||
|                              // $watcher).
 | ||||
|                              'DoCheck(baz)', | ||||
|                              'AfterContentChecked(baz)', | ||||
|                              'AfterViewChecked(baz)', | ||||
|                            ] : | ||||
|                            []); | ||||
|                    logs.length = 0; | ||||
| 
 | ||||
|                // Trigger change detection (without changing inputs).
 | ||||
|                button.click(); | ||||
|                expect(multiTrim(element.textContent)).toBe('qux Content'); | ||||
|                expect(logs).toEqual([ | ||||
|                  'DoCheck(qux)', | ||||
|                  'AfterContentChecked(qux)', | ||||
|                  'AfterViewChecked(qux)', | ||||
|                ]); | ||||
|                logs.length = 0; | ||||
|                    // Trigger change detection (without changing inputs).
 | ||||
|                    button.click(); | ||||
|                    expect(multiTrim(element.textContent)).toBe('qux Content'); | ||||
|                    expect(logs).toEqual([ | ||||
|                      'DoCheck(qux)', | ||||
|                      'AfterContentChecked(qux)', | ||||
|                      'AfterViewChecked(qux)', | ||||
|                    ]); | ||||
|                    logs.length = 0; | ||||
| 
 | ||||
|                // Destroy the component.
 | ||||
|                rootScope.$apply('hideNg2 = true'); | ||||
|                expect(logs).toEqual([ | ||||
|                  'OnDestroy(qux)', | ||||
|                ]); | ||||
|                logs.length = 0; | ||||
|              }); | ||||
|            }); | ||||
|          })); | ||||
|                    // Destroy the component.
 | ||||
|                    rootScope.$apply('hideNg2 = true'); | ||||
|                    expect(logs).toEqual([ | ||||
|                      'OnDestroy(qux)', | ||||
|                    ]); | ||||
|                    logs.length = 0; | ||||
|                  }); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should detach hostViews from the ApplicationRef once destroyed', async(() => { | ||||
|            let ng2Component: Ng2Component; | ||||
|       fixmeIvy('FW-717: Browser locks up and disconnects') && | ||||
|           it('should detach hostViews from the ApplicationRef once destroyed', async(() => { | ||||
|                let ng2Component: Ng2Component; | ||||
| 
 | ||||
|            @Component({selector: 'ng2', template: ''}) | ||||
|            class Ng2Component { | ||||
|              constructor(public appRef: ApplicationRef) { | ||||
|                ng2Component = this; | ||||
|                spyOn(appRef, 'attachView').and.callThrough(); | ||||
|                spyOn(appRef, 'detachView').and.callThrough(); | ||||
|              } | ||||
|            } | ||||
|                @Component({selector: 'ng2', template: ''}) | ||||
|                class Ng2Component { | ||||
|                  constructor(public appRef: ApplicationRef) { | ||||
|                    ng2Component = this; | ||||
|                    spyOn(appRef, 'attachView').and.callThrough(); | ||||
|                    spyOn(appRef, 'detachView').and.callThrough(); | ||||
|                  } | ||||
|                } | ||||
| 
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule], | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule], | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|            const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|            const ng1Module = | ||||
|                angular.module('ng1', [lazyModuleName]) | ||||
|                    .directive( | ||||
|                        'ng2', downgradeComponent({component: Ng2Component, propagateDigest})); | ||||
|                const bootstrapFn = (extraProviders: StaticProvider[]) => | ||||
|                    platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module); | ||||
|                const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn); | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1', [lazyModuleName]) | ||||
|                        .directive( | ||||
|                            'ng2', downgradeComponent({component: Ng2Component, propagateDigest})); | ||||
| 
 | ||||
|            const element = html('<ng2 ng-if="!hideNg2"></ng2>'); | ||||
|            const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|            const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
|                const element = html('<ng2 ng-if="!hideNg2"></ng2>'); | ||||
|                const $injector = angular.bootstrap(element, [ng1Module.name]); | ||||
|                const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService; | ||||
| 
 | ||||
|            setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|              setTimeout(() => {  // Wait for the hostView to be attached (during the `$digest`).
 | ||||
|                const hostView: ViewRef = | ||||
|                    (ng2Component.appRef.attachView as jasmine.Spy).calls.mostRecent().args[0]; | ||||
|                setTimeout(() => {    // Wait for the module to be bootstrapped.
 | ||||
|                  setTimeout(() => {  // Wait for the hostView to be attached (during the `$digest`).
 | ||||
|                    const hostView: ViewRef = | ||||
|                        (ng2Component.appRef.attachView as jasmine.Spy).calls.mostRecent().args[0]; | ||||
| 
 | ||||
|                expect(hostView.destroyed).toBe(false); | ||||
|                    expect(hostView.destroyed).toBe(false); | ||||
| 
 | ||||
|                $rootScope.$apply('hideNg2 = true'); | ||||
|                    $rootScope.$apply('hideNg2 = true'); | ||||
| 
 | ||||
|                expect(hostView.destroyed).toBe(true); | ||||
|                expect(ng2Component.appRef.detachView).toHaveBeenCalledWith(hostView); | ||||
|              }); | ||||
|            }); | ||||
|          })); | ||||
|                    expect(hostView.destroyed).toBe(true); | ||||
|                    expect(ng2Component.appRef.detachView).toHaveBeenCalledWith(hostView); | ||||
|                  }); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should only retrieve the Angular zone once (and cache it for later use)', | ||||
|          fakeAsync(() => { | ||||
|  | ||||
| @ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, destroyPlat | ||||
| import {async} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| 
 | ||||
| @ -23,67 +24,70 @@ withEachNg1Version(() => { | ||||
| 
 | ||||
|     it('should have AngularJS loaded', () => expect(angular.version.major).toBe(1)); | ||||
| 
 | ||||
|     it('should verify UpgradeAdapter example', async(() => { | ||||
|     fixmeIvy('FW-714: ng1 projected content is not being rendered') && | ||||
|         it('should verify UpgradeAdapter example', async(() => { | ||||
| 
 | ||||
|          // This is wrapping (upgrading) an AngularJS component to be used in an Angular
 | ||||
|          // component
 | ||||
|          @Directive({selector: 'ng1'}) | ||||
|          class Ng1Component extends UpgradeComponent { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Input() title !: string; | ||||
|              // This is wrapping (upgrading) an AngularJS component to be used in an Angular
 | ||||
|              // component
 | ||||
|              @Directive({selector: 'ng1'}) | ||||
|              class Ng1Component extends UpgradeComponent { | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Input() title !: string; | ||||
| 
 | ||||
|            constructor(elementRef: ElementRef, injector: Injector) { | ||||
|              super('ng1', elementRef, injector); | ||||
|            } | ||||
|          } | ||||
|                constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                  super('ng1', elementRef, injector); | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|          // This is an Angular component that will be downgraded
 | ||||
|          @Component({ | ||||
|            selector: 'ng2', | ||||
|            template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)' | ||||
|          }) | ||||
|          class Ng2Component { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Input('name') nameProp !: string; | ||||
|          } | ||||
|              // This is an Angular component that will be downgraded
 | ||||
|              @Component({ | ||||
|                selector: 'ng2', | ||||
|                template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)' | ||||
|              }) | ||||
|              class Ng2Component { | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Input('name') nameProp !: string; | ||||
|              } | ||||
| 
 | ||||
|          // This module represents the Angular pieces of the application
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng1Component, Ng2Component], | ||||
|            entryComponents: [Ng2Component], | ||||
|            imports: [BrowserModule, UpgradeModule] | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from complaining */ | ||||
|            } | ||||
|          } | ||||
|              // This module represents the Angular pieces of the application
 | ||||
|              @NgModule({ | ||||
|                declarations: [Ng1Component, Ng2Component], | ||||
|                entryComponents: [Ng2Component], | ||||
|                imports: [BrowserModule, UpgradeModule] | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from | ||||
|                                     complaining */ | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|          // This module represents the AngularJS pieces of the application
 | ||||
|          const ng1Module = | ||||
|              angular | ||||
|                  .module('myExample', []) | ||||
|                  // This is an AngularJS component that will be upgraded
 | ||||
|                  .directive( | ||||
|                      'ng1', | ||||
|                      () => { | ||||
|                        return { | ||||
|                          scope: {title: '='}, | ||||
|                          transclude: true, | ||||
|                          template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)' | ||||
|                        }; | ||||
|                      }) | ||||
|                  // This is wrapping (downgrading) an Angular component to be used in AngularJS
 | ||||
|                  .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|              // This module represents the AngularJS pieces of the application
 | ||||
|              const ng1Module = | ||||
|                  angular | ||||
|                      .module('myExample', []) | ||||
|                      // This is an AngularJS component that will be upgraded
 | ||||
|                      .directive( | ||||
|                          'ng1', | ||||
|                          () => { | ||||
|                            return { | ||||
|                              scope: {title: '='}, | ||||
|                              transclude: true, | ||||
|                              template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)' | ||||
|                            }; | ||||
|                          }) | ||||
|                      // This is wrapping (downgrading) an Angular component to be used in
 | ||||
|                      // AngularJS
 | ||||
|                      .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|          // This is the (AngularJS) application bootstrap element
 | ||||
|          // Notice that it is actually a downgraded Angular component
 | ||||
|          const element = html('<ng2 name="World">project</ng2>'); | ||||
|              // This is the (AngularJS) application bootstrap element
 | ||||
|              // Notice that it is actually a downgraded Angular component
 | ||||
|              const element = html('<ng2 name="World">project</ng2>'); | ||||
| 
 | ||||
|          // Let's use a helper function to make this simpler
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|            expect(multiTrim(element.textContent)) | ||||
|                .toBe('ng2[ng1[Hello World!](transclude)](project)'); | ||||
|          }); | ||||
|        })); | ||||
|              // Let's use a helper function to make this simpler
 | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => { | ||||
|                expect(multiTrim(element.textContent)) | ||||
|                    .toBe('ng2[ng1[Hello World!](transclude)](project)'); | ||||
|              }); | ||||
|            })); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -10,6 +10,7 @@ import {Component, Directive, ElementRef, ErrorHandler, EventEmitter, Inject, In | ||||
| import {async, fakeAsync, tick} from '@angular/core/testing'; | ||||
| import {BrowserModule} from '@angular/platform-browser'; | ||||
| import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | ||||
| import {fixmeIvy} from '@angular/private/testing'; | ||||
| import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; | ||||
| import * as angular from '@angular/upgrade/static/src/common/angular1'; | ||||
| import {$EXCEPTION_HANDLER, $SCOPE} from '@angular/upgrade/static/src/common/constants'; | ||||
| @ -2133,72 +2134,77 @@ withEachNg1Version(() => { | ||||
|     }); | ||||
| 
 | ||||
|     describe('transclusion', () => { | ||||
|       it('should support single-slot transclusion', async(() => { | ||||
|            let ng2ComponentAInstance: Ng2ComponentA; | ||||
|            let ng2ComponentBInstance: Ng2ComponentB; | ||||
|       fixmeIvy( | ||||
|           `Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) && | ||||
|           it('should support single-slot transclusion', async(() => { | ||||
|                let ng2ComponentAInstance: Ng2ComponentA; | ||||
|                let ng2ComponentBInstance: Ng2ComponentB; | ||||
| 
 | ||||
|            // Define `ng1Component`
 | ||||
|            const ng1Component: | ||||
|                angular.IComponent = {template: 'ng1(<div ng-transclude></div>)', transclude: true}; | ||||
|                // Define `ng1Component`
 | ||||
|                const ng1Component: angular.IComponent = { | ||||
|                  template: 'ng1(<div ng-transclude></div>)', | ||||
|                  transclude: true | ||||
|                }; | ||||
| 
 | ||||
|            // Define `Ng1ComponentFacade`
 | ||||
|            @Directive({selector: 'ng1'}) | ||||
|            class Ng1ComponentFacade extends UpgradeComponent { | ||||
|              constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                super('ng1', elementRef, injector); | ||||
|              } | ||||
|            } | ||||
|                // Define `Ng1ComponentFacade`
 | ||||
|                @Directive({selector: 'ng1'}) | ||||
|                class Ng1ComponentFacade extends UpgradeComponent { | ||||
|                  constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                    super('ng1', elementRef, injector); | ||||
|                  } | ||||
|                } | ||||
| 
 | ||||
|            // Define `Ng2Component`
 | ||||
|            @Component({ | ||||
|              selector: 'ng2A', | ||||
|              template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)' | ||||
|            }) | ||||
|            class Ng2ComponentA { | ||||
|              value = 'foo'; | ||||
|              showB = false; | ||||
|              constructor() { ng2ComponentAInstance = this; } | ||||
|            } | ||||
|                // Define `Ng2Component`
 | ||||
|                @Component({ | ||||
|                  selector: 'ng2A', | ||||
|                  template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)' | ||||
|                }) | ||||
|                class Ng2ComponentA { | ||||
|                  value = 'foo'; | ||||
|                  showB = false; | ||||
|                  constructor() { ng2ComponentAInstance = this; } | ||||
|                } | ||||
| 
 | ||||
|            @Component({selector: 'ng2B', template: 'ng2B({{ value }})'}) | ||||
|            class Ng2ComponentB { | ||||
|              value = 'bar'; | ||||
|              constructor() { ng2ComponentBInstance = this; } | ||||
|            } | ||||
|                @Component({selector: 'ng2B', template: 'ng2B({{ value }})'}) | ||||
|                class Ng2ComponentB { | ||||
|                  value = 'bar'; | ||||
|                  constructor() { ng2ComponentBInstance = this; } | ||||
|                } | ||||
| 
 | ||||
|            // Define `ng1Module`
 | ||||
|            const ng1Module = angular.module('ng1Module', []) | ||||
|                                  .component('ng1', ng1Component) | ||||
|                                  .directive('ng2A', downgradeComponent({component: Ng2ComponentA})); | ||||
|                // Define `ng1Module`
 | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1Module', []) | ||||
|                        .component('ng1', ng1Component) | ||||
|                        .directive('ng2A', downgradeComponent({component: Ng2ComponentA})); | ||||
| 
 | ||||
|            // Define `Ng2Module`
 | ||||
|            @NgModule({ | ||||
|              imports: [BrowserModule, UpgradeModule], | ||||
|              declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB], | ||||
|              entryComponents: [Ng2ComponentA] | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                // Define `Ng2Module`
 | ||||
|                @NgModule({ | ||||
|                  imports: [BrowserModule, UpgradeModule], | ||||
|                  declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB], | ||||
|                  entryComponents: [Ng2ComponentA] | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            // Bootstrap
 | ||||
|            const element = html(`<ng2-a></ng2-a>`); | ||||
|                // Bootstrap
 | ||||
|                const element = html(`<ng2-a></ng2-a>`); | ||||
| 
 | ||||
|            bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|              expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))'); | ||||
|                bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|                  expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))'); | ||||
| 
 | ||||
|              ng2ComponentAInstance.value = 'baz'; | ||||
|              ng2ComponentAInstance.showB = true; | ||||
|              $digest(adapter); | ||||
|                  ng2ComponentAInstance.value = 'baz'; | ||||
|                  ng2ComponentAInstance.showB = true; | ||||
|                  $digest(adapter); | ||||
| 
 | ||||
|              expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))'); | ||||
|                  expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))'); | ||||
| 
 | ||||
|              ng2ComponentBInstance.value = 'qux'; | ||||
|              $digest(adapter); | ||||
|                  ng2ComponentBInstance.value = 'qux'; | ||||
|                  $digest(adapter); | ||||
| 
 | ||||
|              expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))'); | ||||
|            }); | ||||
|          })); | ||||
|                  expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))'); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should support single-slot transclusion with fallback content', async(() => { | ||||
|            let ng1ControllerInstances: any[] = []; | ||||
| @ -2525,28 +2531,30 @@ withEachNg1Version(() => { | ||||
|            }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should support structural directives in transcluded content', async(() => { | ||||
|            let ng2ComponentInstance: Ng2Component; | ||||
|       fixmeIvy( | ||||
|           `Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) && | ||||
|           it('should support structural directives in transcluded content', async(() => { | ||||
|                let ng2ComponentInstance: Ng2Component; | ||||
| 
 | ||||
|            // Define `ng1Component`
 | ||||
|            const ng1Component: angular.IComponent = { | ||||
|              template: | ||||
|                  'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))', | ||||
|              transclude: {slotX: 'contentX'} | ||||
|            }; | ||||
|                // Define `ng1Component`
 | ||||
|                const ng1Component: angular.IComponent = { | ||||
|                  template: | ||||
|                      'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))', | ||||
|                  transclude: {slotX: 'contentX'} | ||||
|                }; | ||||
| 
 | ||||
|            // Define `Ng1ComponentFacade`
 | ||||
|            @Directive({selector: 'ng1'}) | ||||
|            class Ng1ComponentFacade extends UpgradeComponent { | ||||
|              constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                super('ng1', elementRef, injector); | ||||
|              } | ||||
|            } | ||||
|                // Define `Ng1ComponentFacade`
 | ||||
|                @Directive({selector: 'ng1'}) | ||||
|                class Ng1ComponentFacade extends UpgradeComponent { | ||||
|                  constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                    super('ng1', elementRef, injector); | ||||
|                  } | ||||
|                } | ||||
| 
 | ||||
|            // Define `Ng2Component`
 | ||||
|            @Component({ | ||||
|              selector: 'ng2', | ||||
|              template: ` | ||||
|                // Define `Ng2Component`
 | ||||
|                @Component({ | ||||
|                  selector: 'ng2', | ||||
|                  template: ` | ||||
|                ng2( | ||||
|                  <ng1> | ||||
|                    <content-x><div *ngIf="show">{{ x }}1</div></content-x> | ||||
| @ -2555,49 +2563,53 @@ withEachNg1Version(() => { | ||||
|                    <div *ngIf="show">{{ y }}2</div> | ||||
|                  </ng1> | ||||
|                )` | ||||
|            }) | ||||
|            class Ng2Component { | ||||
|              x = 'foo'; | ||||
|              y = 'bar'; | ||||
|              show = true; | ||||
|              constructor() { ng2ComponentInstance = this; } | ||||
|            } | ||||
|                }) | ||||
|                class Ng2Component { | ||||
|                  x = 'foo'; | ||||
|                  y = 'bar'; | ||||
|                  show = true; | ||||
|                  constructor() { ng2ComponentInstance = this; } | ||||
|                } | ||||
| 
 | ||||
|            // Define `ng1Module`
 | ||||
|            const ng1Module = angular.module('ng1Module', []) | ||||
|                                  .component('ng1', ng1Component) | ||||
|                                  .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|                // Define `ng1Module`
 | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1Module', []) | ||||
|                        .component('ng1', ng1Component) | ||||
|                        .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|            // Define `Ng2Module`
 | ||||
|            @NgModule({ | ||||
|              imports: [BrowserModule, UpgradeModule], | ||||
|              declarations: [Ng1ComponentFacade, Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              schemas: [NO_ERRORS_SCHEMA] | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                // Define `Ng2Module`
 | ||||
|                @NgModule({ | ||||
|                  imports: [BrowserModule, UpgradeModule], | ||||
|                  declarations: [Ng1ComponentFacade, Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  schemas: [NO_ERRORS_SCHEMA] | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            // Bootstrap
 | ||||
|            const element = html(`<ng2></ng2>`); | ||||
|                // Bootstrap
 | ||||
|                const element = html(`<ng2></ng2>`); | ||||
| 
 | ||||
|            bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|              expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(foo1)|default(bar2)))'); | ||||
|                bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|                  expect(multiTrim(element.textContent, true)) | ||||
|                      .toBe('ng2(ng1(x(foo1)|default(bar2)))'); | ||||
| 
 | ||||
|              ng2ComponentInstance.x = 'baz'; | ||||
|              ng2ComponentInstance.y = 'qux'; | ||||
|              ng2ComponentInstance.show = false; | ||||
|              $digest(adapter); | ||||
|                  ng2ComponentInstance.x = 'baz'; | ||||
|                  ng2ComponentInstance.y = 'qux'; | ||||
|                  ng2ComponentInstance.show = false; | ||||
|                  $digest(adapter); | ||||
| 
 | ||||
|              expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz2)|default(qux1)))'); | ||||
|                  expect(multiTrim(element.textContent, true)) | ||||
|                      .toBe('ng2(ng1(x(baz2)|default(qux1)))'); | ||||
| 
 | ||||
|              ng2ComponentInstance.show = true; | ||||
|              $digest(adapter); | ||||
|                  ng2ComponentInstance.show = true; | ||||
|                  $digest(adapter); | ||||
| 
 | ||||
|              expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1)|default(qux2)))'); | ||||
|            }); | ||||
|          })); | ||||
|                  expect(multiTrim(element.textContent, true)) | ||||
|                      .toBe('ng2(ng1(x(baz1)|default(qux2)))'); | ||||
|                }); | ||||
|              })); | ||||
|     }); | ||||
| 
 | ||||
|     describe('lifecycle hooks', () => { | ||||
| @ -3324,98 +3336,103 @@ withEachNg1Version(() => { | ||||
|          })); | ||||
| 
 | ||||
| 
 | ||||
|       it('should call `$onDestroy()` on controller', async(() => { | ||||
|            const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA'); | ||||
|            const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB'); | ||||
|       fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') && | ||||
|           it('should call `$onDestroy()` on controller', async(() => { | ||||
|                const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA'); | ||||
|                const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB'); | ||||
| 
 | ||||
|            // Define `ng1Directive`
 | ||||
|            const ng1DirectiveA: angular.IDirective = { | ||||
|              template: 'ng1A', | ||||
|              scope: {}, | ||||
|              bindToController: false, | ||||
|              controllerAs: '$ctrl', | ||||
|              controller: class {$onDestroy() { controllerOnDestroyA(); }} | ||||
|            }; | ||||
|                // Define `ng1Directive`
 | ||||
|                const ng1DirectiveA: angular.IDirective = { | ||||
|                  template: 'ng1A', | ||||
|                  scope: {}, | ||||
|                  bindToController: false, | ||||
|                  controllerAs: '$ctrl', | ||||
|                  controller: class {$onDestroy() { controllerOnDestroyA(); }} | ||||
|                }; | ||||
| 
 | ||||
|            const ng1DirectiveB: angular.IDirective = { | ||||
|              template: 'ng1B', | ||||
|              scope: {}, | ||||
|              bindToController: true, | ||||
|              controllerAs: '$ctrl', | ||||
|              controller: | ||||
|                  class {constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }} | ||||
|            }; | ||||
|                const ng1DirectiveB: angular.IDirective = { | ||||
|                  template: 'ng1B', | ||||
|                  scope: {}, | ||||
|                  bindToController: true, | ||||
|                  controllerAs: '$ctrl', | ||||
|                  controller: | ||||
|                      class {constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }} | ||||
|                }; | ||||
| 
 | ||||
|            // Define `Ng1ComponentFacade`
 | ||||
|            @Directive({selector: 'ng1A'}) | ||||
|            class Ng1ComponentAFacade extends UpgradeComponent { | ||||
|              constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                super('ng1A', elementRef, injector); | ||||
|              } | ||||
|            } | ||||
|                // Define `Ng1ComponentFacade`
 | ||||
|                @Directive({selector: 'ng1A'}) | ||||
|                class Ng1ComponentAFacade extends UpgradeComponent { | ||||
|                  constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                    super('ng1A', elementRef, injector); | ||||
|                  } | ||||
|                } | ||||
| 
 | ||||
|            @Directive({selector: 'ng1B'}) | ||||
|            class Ng1ComponentBFacade extends UpgradeComponent { | ||||
|              constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                super('ng1B', elementRef, injector); | ||||
|              } | ||||
|            } | ||||
|                @Directive({selector: 'ng1B'}) | ||||
|                class Ng1ComponentBFacade extends UpgradeComponent { | ||||
|                  constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                    super('ng1B', elementRef, injector); | ||||
|                  } | ||||
|                } | ||||
| 
 | ||||
|            // Define `Ng2Component`
 | ||||
|            @Component( | ||||
|                {selector: 'ng2', template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>'}) | ||||
|            class Ng2Component { | ||||
|              // TODO(issue/24571): remove '!'.
 | ||||
|              @Input() show !: boolean; | ||||
|            } | ||||
|                // Define `Ng2Component`
 | ||||
|                @Component({ | ||||
|                  selector: 'ng2', | ||||
|                  template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>' | ||||
|                }) | ||||
|                class Ng2Component { | ||||
|                  // TODO(issue/24571): remove '!'.
 | ||||
|                  @Input() show !: boolean; | ||||
|                } | ||||
| 
 | ||||
|            // Define `ng1Module`
 | ||||
|            const ng1Module = angular.module('ng1Module', []) | ||||
|                                  .directive('ng1A', () => ng1DirectiveA) | ||||
|                                  .directive('ng1B', () => ng1DirectiveB) | ||||
|                                  .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
|                // Define `ng1Module`
 | ||||
|                const ng1Module = | ||||
|                    angular.module('ng1Module', []) | ||||
|                        .directive('ng1A', () => ng1DirectiveA) | ||||
|                        .directive('ng1B', () => ng1DirectiveB) | ||||
|                        .directive('ng2', downgradeComponent({component: Ng2Component})); | ||||
| 
 | ||||
|            // Define `Ng2Module`
 | ||||
|            @NgModule({ | ||||
|              declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component], | ||||
|              entryComponents: [Ng2Component], | ||||
|              imports: [BrowserModule, UpgradeModule] | ||||
|            }) | ||||
|            class Ng2Module { | ||||
|              ngDoBootstrap() {} | ||||
|            } | ||||
|                // Define `Ng2Module`
 | ||||
|                @NgModule({ | ||||
|                  declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component], | ||||
|                  entryComponents: [Ng2Component], | ||||
|                  imports: [BrowserModule, UpgradeModule] | ||||
|                }) | ||||
|                class Ng2Module { | ||||
|                  ngDoBootstrap() {} | ||||
|                } | ||||
| 
 | ||||
|            // Bootstrap
 | ||||
|            const element = html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>'); | ||||
|                // Bootstrap
 | ||||
|                const element = html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>'); | ||||
| 
 | ||||
|            bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|              const $rootScope = adapter.$injector.get('$rootScope') as angular.IRootScopeService; | ||||
|                bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|                  const $rootScope = | ||||
|                      adapter.$injector.get('$rootScope') as angular.IRootScopeService; | ||||
| 
 | ||||
|              expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B'); | ||||
|              expect(controllerOnDestroyA).not.toHaveBeenCalled(); | ||||
|              expect(controllerOnDestroyB).not.toHaveBeenCalled(); | ||||
|                  expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B'); | ||||
|                  expect(controllerOnDestroyA).not.toHaveBeenCalled(); | ||||
|                  expect(controllerOnDestroyB).not.toHaveBeenCalled(); | ||||
| 
 | ||||
|              $rootScope.$apply('destroyFromNg1 = true'); | ||||
|                  $rootScope.$apply('destroyFromNg1 = true'); | ||||
| 
 | ||||
|              expect(multiTrim(document.body.textContent)).toBe(''); | ||||
|              expect(controllerOnDestroyA).toHaveBeenCalled(); | ||||
|              expect(controllerOnDestroyB).toHaveBeenCalled(); | ||||
|                  expect(multiTrim(document.body.textContent)).toBe(''); | ||||
|                  expect(controllerOnDestroyA).toHaveBeenCalled(); | ||||
|                  expect(controllerOnDestroyB).toHaveBeenCalled(); | ||||
| 
 | ||||
|              controllerOnDestroyA.calls.reset(); | ||||
|              controllerOnDestroyB.calls.reset(); | ||||
|              $rootScope.$apply('destroyFromNg1 = false'); | ||||
|                  controllerOnDestroyA.calls.reset(); | ||||
|                  controllerOnDestroyB.calls.reset(); | ||||
|                  $rootScope.$apply('destroyFromNg1 = false'); | ||||
| 
 | ||||
|              expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B'); | ||||
|              expect(controllerOnDestroyA).not.toHaveBeenCalled(); | ||||
|              expect(controllerOnDestroyB).not.toHaveBeenCalled(); | ||||
|                  expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B'); | ||||
|                  expect(controllerOnDestroyA).not.toHaveBeenCalled(); | ||||
|                  expect(controllerOnDestroyB).not.toHaveBeenCalled(); | ||||
| 
 | ||||
|              $rootScope.$apply('destroyFromNg2 = true'); | ||||
|                  $rootScope.$apply('destroyFromNg2 = true'); | ||||
| 
 | ||||
|              expect(multiTrim(document.body.textContent)).toBe(''); | ||||
|              expect(controllerOnDestroyA).toHaveBeenCalled(); | ||||
|              expect(controllerOnDestroyB).toHaveBeenCalled(); | ||||
|            }); | ||||
|          })); | ||||
|                  expect(multiTrim(document.body.textContent)).toBe(''); | ||||
|                  expect(controllerOnDestroyA).toHaveBeenCalled(); | ||||
|                  expect(controllerOnDestroyB).toHaveBeenCalled(); | ||||
|                }); | ||||
|              })); | ||||
| 
 | ||||
|       it('should not call `$onDestroy()` on scope', async(() => { | ||||
|            const scopeOnDestroy = jasmine.createSpy('scopeOnDestroy'); | ||||
| @ -3948,22 +3965,23 @@ withEachNg1Version(() => { | ||||
|          }); | ||||
|        })); | ||||
| 
 | ||||
|     it('should support ng2 > ng1 > ng2 (with inputs/outputs)', fakeAsync(() => { | ||||
|          let ng2ComponentAInstance: Ng2ComponentA; | ||||
|          let ng2ComponentBInstance: Ng2ComponentB; | ||||
|          let ng1ControllerXInstance: Ng1ControllerX; | ||||
|     fixmeIvy('FW-642: ASSERTION ERROR: Slot should have been initialized to NO_CHANGE') && | ||||
|         it('should support ng2 > ng1 > ng2 (with inputs/outputs)', fakeAsync(() => { | ||||
|              let ng2ComponentAInstance: Ng2ComponentA; | ||||
|              let ng2ComponentBInstance: Ng2ComponentB; | ||||
|              let ng1ControllerXInstance: Ng1ControllerX; | ||||
| 
 | ||||
|          // Define `ng1Component`
 | ||||
|          class Ng1ControllerX { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            ng1XInputA !: string; | ||||
|            ng1XInputB: any; | ||||
|            ng1XInputC: any; | ||||
|              // Define `ng1Component`
 | ||||
|              class Ng1ControllerX { | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                ng1XInputA !: string; | ||||
|                ng1XInputB: any; | ||||
|                ng1XInputC: any; | ||||
| 
 | ||||
|            constructor() { ng1ControllerXInstance = this; } | ||||
|          } | ||||
|          const ng1Component: angular.IComponent = { | ||||
|            template: ` | ||||
|                constructor() { ng1ControllerXInstance = this; } | ||||
|              } | ||||
|              const ng1Component: angular.IComponent = { | ||||
|                template: ` | ||||
|               ng1X({{ $ctrl.ng1XInputA }}, {{ $ctrl.ng1XInputB.value }}, {{ $ctrl.ng1XInputC.value }}) | | ||||
|               <ng2-b | ||||
|                 [ng2-b-input1]="$ctrl.ng1XInputA" | ||||
| @ -3971,39 +3989,39 @@ withEachNg1Version(() => { | ||||
|                 (ng2-b-output-c)="$ctrl.ng1XInputC = {value: $event}"> | ||||
|               </ng2-b> | ||||
|             `,
 | ||||
|            bindings: { | ||||
|              ng1XInputA: '@', | ||||
|              ng1XInputB: '<', | ||||
|              ng1XInputC: '=', | ||||
|              ng1XOutputA: '&', | ||||
|              ng1XOutputB: '&' | ||||
|            }, | ||||
|            controller: Ng1ControllerX | ||||
|          }; | ||||
|                bindings: { | ||||
|                  ng1XInputA: '@', | ||||
|                  ng1XInputB: '<', | ||||
|                  ng1XInputC: '=', | ||||
|                  ng1XOutputA: '&', | ||||
|                  ng1XOutputB: '&' | ||||
|                }, | ||||
|                controller: Ng1ControllerX | ||||
|              }; | ||||
| 
 | ||||
|          // Define `Ng1ComponentFacade`
 | ||||
|          @Directive({selector: 'ng1X'}) | ||||
|          class Ng1ComponentXFacade extends UpgradeComponent { | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Input() ng1XInputA !: string; | ||||
|            @Input() ng1XInputB: any; | ||||
|            @Input() ng1XInputC: any; | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Output() ng1XInputCChange !: EventEmitter<any>; | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Output() ng1XOutputA !: EventEmitter<any>; | ||||
|            // TODO(issue/24571): remove '!'.
 | ||||
|            @Output() ng1XOutputB !: EventEmitter<any>; | ||||
|              // Define `Ng1ComponentFacade`
 | ||||
|              @Directive({selector: 'ng1X'}) | ||||
|              class Ng1ComponentXFacade extends UpgradeComponent { | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Input() ng1XInputA !: string; | ||||
|                @Input() ng1XInputB: any; | ||||
|                @Input() ng1XInputC: any; | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Output() ng1XInputCChange !: EventEmitter<any>; | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Output() ng1XOutputA !: EventEmitter<any>; | ||||
|                // TODO(issue/24571): remove '!'.
 | ||||
|                @Output() ng1XOutputB !: EventEmitter<any>; | ||||
| 
 | ||||
|            constructor(elementRef: ElementRef, injector: Injector) { | ||||
|              super('ng1X', elementRef, injector); | ||||
|            } | ||||
|          } | ||||
|                constructor(elementRef: ElementRef, injector: Injector) { | ||||
|                  super('ng1X', elementRef, injector); | ||||
|                } | ||||
|              } | ||||
| 
 | ||||
|          // Define `Ng2Component`
 | ||||
|          @Component({ | ||||
|            selector: 'ng2-a', | ||||
|            template: ` | ||||
|              // Define `Ng2Component`
 | ||||
|              @Component({ | ||||
|                selector: 'ng2-a', | ||||
|                template: ` | ||||
|               ng2A({{ ng2ADataA.value }}, {{ ng2ADataB.value }}, {{ ng2ADataC.value }}) | | ||||
|               <ng1X | ||||
|                   ng1XInputA="{{ ng2ADataA.value }}" | ||||
| @ -4013,129 +4031,131 @@ withEachNg1Version(() => { | ||||
|                   on-ng1XOutputB="ng2ADataB.value = $event"> | ||||
|               </ng1X> | ||||
|             ` | ||||
|          }) | ||||
|          class Ng2ComponentA { | ||||
|            ng2ADataA = {value: 'foo'}; | ||||
|            ng2ADataB = {value: 'bar'}; | ||||
|            ng2ADataC = {value: 'baz'}; | ||||
|              }) | ||||
|              class Ng2ComponentA { | ||||
|                ng2ADataA = {value: 'foo'}; | ||||
|                ng2ADataB = {value: 'bar'}; | ||||
|                ng2ADataC = {value: 'baz'}; | ||||
| 
 | ||||
|            constructor() { ng2ComponentAInstance = this; } | ||||
|          } | ||||
|                constructor() { ng2ComponentAInstance = this; } | ||||
|              } | ||||
| 
 | ||||
|          @Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'}) | ||||
|          class Ng2ComponentB { | ||||
|            @Input('ng2BInput1') ng2BInputA: any; | ||||
|            @Input() ng2BInputC: any; | ||||
|            @Output() ng2BOutputC = new EventEmitter(); | ||||
|              @Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'}) | ||||
|              class Ng2ComponentB { | ||||
|                @Input('ng2BInput1') ng2BInputA: any; | ||||
|                @Input() ng2BInputC: any; | ||||
|                @Output() ng2BOutputC = new EventEmitter(); | ||||
| 
 | ||||
|            constructor() { ng2ComponentBInstance = this; } | ||||
|          } | ||||
|                constructor() { ng2ComponentBInstance = this; } | ||||
|              } | ||||
| 
 | ||||
|          // Define `ng1Module`
 | ||||
|          const ng1Module = angular.module('ng1', []) | ||||
|                                .component('ng1X', ng1Component) | ||||
|                                .directive('ng2A', downgradeComponent({component: Ng2ComponentA})) | ||||
|                                .directive('ng2B', downgradeComponent({component: Ng2ComponentB})); | ||||
|              // Define `ng1Module`
 | ||||
|              const ng1Module = | ||||
|                  angular.module('ng1', []) | ||||
|                      .component('ng1X', ng1Component) | ||||
|                      .directive('ng2A', downgradeComponent({component: Ng2ComponentA})) | ||||
|                      .directive('ng2B', downgradeComponent({component: Ng2ComponentB})); | ||||
| 
 | ||||
|          // Define `Ng2Module`
 | ||||
|          @NgModule({ | ||||
|            declarations: [Ng1ComponentXFacade, Ng2ComponentA, Ng2ComponentB], | ||||
|            entryComponents: [Ng2ComponentA, Ng2ComponentB], | ||||
|            imports: [BrowserModule, UpgradeModule], | ||||
|            schemas: [NO_ERRORS_SCHEMA], | ||||
|          }) | ||||
|          class Ng2Module { | ||||
|            ngDoBootstrap() {} | ||||
|          } | ||||
|              // Define `Ng2Module`
 | ||||
|              @NgModule({ | ||||
|                declarations: [Ng1ComponentXFacade, Ng2ComponentA, Ng2ComponentB], | ||||
|                entryComponents: [Ng2ComponentA, Ng2ComponentB], | ||||
|                imports: [BrowserModule, UpgradeModule], | ||||
|                schemas: [NO_ERRORS_SCHEMA], | ||||
|              }) | ||||
|              class Ng2Module { | ||||
|                ngDoBootstrap() {} | ||||
|              } | ||||
| 
 | ||||
|          // Bootstrap
 | ||||
|          const element = html(`<ng2-a></ng2-a>`); | ||||
|              // Bootstrap
 | ||||
|              const element = html(`<ng2-a></ng2-a>`); | ||||
| 
 | ||||
|          bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|            // Initial value propagation.
 | ||||
|            // (ng2A > ng1X > ng2B)
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo, baz)'); | ||||
|              bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { | ||||
|                // Initial value propagation.
 | ||||
|                // (ng2A > ng1X > ng2B)
 | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo, baz)'); | ||||
| 
 | ||||
|            // Update `ng2BInputA`/`ng2BInputC`.
 | ||||
|            // (Should not propagate upwards.)
 | ||||
|            ng2ComponentBInstance.ng2BInputA = 'foo2'; | ||||
|            ng2ComponentBInstance.ng2BInputC = 'baz2'; | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Update `ng2BInputA`/`ng2BInputC`.
 | ||||
|                // (Should not propagate upwards.)
 | ||||
|                ng2ComponentBInstance.ng2BInputA = 'foo2'; | ||||
|                ng2ComponentBInstance.ng2BInputC = 'baz2'; | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo2, baz2)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo2, baz2)'); | ||||
| 
 | ||||
|            // Emit from `ng2BOutputC`.
 | ||||
|            // (Should propagate all the way up to `ng1ADataC` and back all the way down to
 | ||||
|            // `ng2BInputC`.)
 | ||||
|            ng2ComponentBInstance.ng2BOutputC.emit('baz3'); | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Emit from `ng2BOutputC`.
 | ||||
|                // (Should propagate all the way up to `ng1ADataC` and back all the way down to
 | ||||
|                // `ng2BInputC`.)
 | ||||
|                ng2ComponentBInstance.ng2BOutputC.emit('baz3'); | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz3) | ng1X(foo, bar, baz3) | ng2B(foo2, baz3)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz3) | ng1X(foo, bar, baz3) | ng2B(foo2, baz3)'); | ||||
| 
 | ||||
|            // Update `ng1XInputA`/`ng1XInputB`.
 | ||||
|            // (Should not propagate upwards, only downwards.)
 | ||||
|            ng1ControllerXInstance.ng1XInputA = 'foo4'; | ||||
|            ng1ControllerXInstance.ng1XInputB = {value: 'bar4'}; | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Update `ng1XInputA`/`ng1XInputB`.
 | ||||
|                // (Should not propagate upwards, only downwards.)
 | ||||
|                ng1ControllerXInstance.ng1XInputA = 'foo4'; | ||||
|                ng1ControllerXInstance.ng1XInputB = {value: 'bar4'}; | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz3) | ng1X(foo4, bar4, baz3) | ng2B(foo4, baz3)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz3) | ng1X(foo4, bar4, baz3) | ng2B(foo4, baz3)'); | ||||
| 
 | ||||
|            // Update `ng1XInputC`.
 | ||||
|            // (Should propagate upwards and downwards.)
 | ||||
|            ng1ControllerXInstance.ng1XInputC = {value: 'baz5'}; | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Update `ng1XInputC`.
 | ||||
|                // (Should propagate upwards and downwards.)
 | ||||
|                ng1ControllerXInstance.ng1XInputC = {value: 'baz5'}; | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz5) | ng1X(foo4, bar4, baz5) | ng2B(foo4, baz5)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz5) | ng1X(foo4, bar4, baz5) | ng2B(foo4, baz5)'); | ||||
| 
 | ||||
|            // Update a property on `ng1XInputC`.
 | ||||
|            // (Should propagate upwards and downwards.)
 | ||||
|            ng1ControllerXInstance.ng1XInputC.value = 'baz6'; | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Update a property on `ng1XInputC`.
 | ||||
|                // (Should propagate upwards and downwards.)
 | ||||
|                ng1ControllerXInstance.ng1XInputC.value = 'baz6'; | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo, bar, baz6) | ng1X(foo4, bar4, baz6) | ng2B(foo4, baz6)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo, bar, baz6) | ng1X(foo4, bar4, baz6) | ng2B(foo4, baz6)'); | ||||
| 
 | ||||
|            // Emit from `ng1XOutputA`.
 | ||||
|            // (Should propagate upwards to `ng1ADataA` and back all the way down to `ng2BInputA`.)
 | ||||
|            (ng1ControllerXInstance as any).ng1XOutputA({value: 'foo7'}); | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Emit from `ng1XOutputA`.
 | ||||
|                // (Should propagate upwards to `ng1ADataA` and back all the way down to
 | ||||
|                // `ng2BInputA`.)
 | ||||
|                (ng1ControllerXInstance as any).ng1XOutputA({value: 'foo7'}); | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo7, bar, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo7, bar, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)'); | ||||
| 
 | ||||
|            // Emit from `ng1XOutputB`.
 | ||||
|            // (Should propagate upwards to `ng1ADataB`, but not downwards,
 | ||||
|            //  since `ng1XInputB` has been re-assigned (i.e. `ng2ADataB !== ng1XInputB`).)
 | ||||
|            (ng1ControllerXInstance as any).ng1XOutputB('bar8'); | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Emit from `ng1XOutputB`.
 | ||||
|                // (Should propagate upwards to `ng1ADataB`, but not downwards,
 | ||||
|                //  since `ng1XInputB` has been re-assigned (i.e. `ng2ADataB !== ng1XInputB`).)
 | ||||
|                (ng1ControllerXInstance as any).ng1XOutputB('bar8'); | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo7, bar8, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)'); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo7, bar8, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)'); | ||||
| 
 | ||||
|            // Update `ng2ADataA`/`ng2ADataB`/`ng2ADataC`.
 | ||||
|            // (Should propagate everywhere.)
 | ||||
|            ng2ComponentAInstance.ng2ADataA = {value: 'foo9'}; | ||||
|            ng2ComponentAInstance.ng2ADataB = {value: 'bar9'}; | ||||
|            ng2ComponentAInstance.ng2ADataC = {value: 'baz9'}; | ||||
|            $digest(adapter); | ||||
|            tick(); | ||||
|                // Update `ng2ADataA`/`ng2ADataB`/`ng2ADataC`.
 | ||||
|                // (Should propagate everywhere.)
 | ||||
|                ng2ComponentAInstance.ng2ADataA = {value: 'foo9'}; | ||||
|                ng2ComponentAInstance.ng2ADataB = {value: 'bar9'}; | ||||
|                ng2ComponentAInstance.ng2ADataC = {value: 'baz9'}; | ||||
|                $digest(adapter); | ||||
|                tick(); | ||||
| 
 | ||||
|            expect(multiTrim(document.body.textContent)) | ||||
|                .toBe('ng2A(foo9, bar9, baz9) | ng1X(foo9, bar9, baz9) | ng2B(foo9, baz9)'); | ||||
|          }); | ||||
|        })); | ||||
|                expect(multiTrim(document.body.textContent)) | ||||
|                    .toBe('ng2A(foo9, bar9, baz9) | ng1X(foo9, bar9, baz9) | ng2B(foo9, baz9)'); | ||||
|              }); | ||||
|            })); | ||||
| 
 | ||||
|     it('should support ng2 > ng1 > ng2 > ng1 (with `require`)', async(() => { | ||||
|          // Define `ng1Component`
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user