;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
                 |
                {{ someText }} - Data: {{ dataA }} - Length: {{ dataA.length }}
              `
           })
           class Ng2Component {
             someText = 'ng2';
             dataA = [1, 2, 3];
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             const ng1 = element.querySelector('[ng1]') !;
             const ng1Controller = angular.element(ng1).controller !('ng1');
             expect(multiTrim(element.textContent))
                 .toBe('ng1 - Data: [1,2,3] - Length: 3 | ng2 - Data: 1,2,3 - Length: 3');
             ng1Controller.$scope.inputA = [4, 5];
             tick();
             expect(multiTrim(element.textContent))
                 .toBe('ng1 - Data: [4,5] - Length: 2 | ng2 - Data: 4,5 - Length: 2');
             ng1Controller.$scope.outputA(6);
             $digest(adapter);
             tick();
             expect(ng1Controller.$scope.inputA).toEqual([4, 5, 6]);
             expect(multiTrim(element.textContent))
                 .toBe('ng1 - Data: [4,5,6] - Length: 3 | ng2 - Data: 4,5,6 - Length: 3');
           });
         }));
    });
    describe('compiling', () => {
      it('should compile the ng1 template in the correct DOM context', async(() => {
           let grandParentNodeName: string;
           // Define `ng1Component`
           const ng1ComponentA: angular.IComponent = {template: 'ng1A()'};
           const ng1DirectiveB: angular.IDirective = {
             compile: tElem => {
               grandParentNodeName = tElem.parent !().parent !()[0].nodeName;
               return {};
             }
           };
           // Define `Ng1ComponentAFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           // Define `Ng2ComponentX`
           @Component({selector: 'ng2-x', template: 'ng2X()'})
           class Ng2ComponentX {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .component('ng1A', ng1ComponentA)
                                 .directive('ng1B', () => ng1DirectiveB)
                                 .directive('ng2X', downgradeComponent({component: Ng2ComponentX}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentAFacade, Ng2ComponentX],
             entryComponents: [Ng2ComponentX],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(grandParentNodeName).toBe('NG2-X');
           });
         }));
    });
    describe('linking', () => {
      it('should run the pre-linking after instantiating the controller', async(() => {
           const log: string[] = [];
           // Define `ng1Directive`
           const ng1Directive: angular.IDirective = {
             template: '',
             link: {pre: () => log.push('ng1-pre')},
             controller: class {constructor() { log.push('ng1-ctrl'); }}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(log).toEqual(['ng1-ctrl', 'ng1-pre']);
           });
         }));
      it('should run the pre-linking function before linking', async(() => {
           const log: string[] = [];
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: '',
             link: {pre: () => log.push('ng1A-pre')}
           };
           const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
           // Define `Ng1ComponentAFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .directive('ng1A', () => ng1DirectiveA)
                                 .directive('ng1B', () => ng1DirectiveB)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentAFacade, Ng2Component],
             entryComponents: [Ng2Component],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(log).toEqual(['ng1A-pre', 'ng1B-post']);
           });
         }));
      it('should run the post-linking function after linking (link: object)', async(() => {
           const log: string[] = [];
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: '',
             link: {post: () => log.push('ng1A-post')}
           };
           const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
           // Define `Ng1ComponentAFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .directive('ng1A', () => ng1DirectiveA)
                                 .directive('ng1B', () => ng1DirectiveB)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentAFacade, Ng2Component],
             entryComponents: [Ng2Component],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(log).toEqual(['ng1B-post', 'ng1A-post']);
           });
         }));
      it('should run the post-linking function after linking (link: function)', async(() => {
           const log: string[] = [];
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: '',
             link: () => log.push('ng1A-post')
           };
           const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
           // Define `Ng1ComponentAFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .directive('ng1A', () => ng1DirectiveA)
                                 .directive('ng1B', () => ng1DirectiveB)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentAFacade, Ng2Component],
             entryComponents: [Ng2Component],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(log).toEqual(['ng1B-post', 'ng1A-post']);
           });
         }));
      it('should run the post-linking function before `$postLink`', async(() => {
           const log: string[] = [];
           // Define `ng1Directive`
           const ng1Directive: angular.IDirective = {
             template: '',
             link: () => log.push('ng1-post'),
             controller: class {$postLink() { log.push('ng1-$post'); }}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1', [])
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(log).toEqual(['ng1-post', 'ng1-$post']);
           });
         }));
    });
    describe('controller', () => {
      it('should support `controllerAs`', async(() => {
           // Define `ng1Directive`
           const ng1Directive: angular.IDirective = {
             template:
                 '{{ vm.scope }}; {{ vm.isClass }}; {{ vm.hasElement }}; {{ vm.isPublished() }}',
             scope: true,
             controllerAs: 'vm',
             controller: class {
               hasElement: string;  // TODO(issue/24571): remove '!'.
               isClass !: string;
               scope: string;
               constructor(public $element: angular.IAugmentedJQuery, $scope: angular.IScope) {
                 this.hasElement = $element[0].nodeName;
                 this.scope = $scope.$parent.$parent === $scope.$root ? 'scope' : 'wrong-scope';
                 this.verifyIAmAClass();
               }
               isPublished() {
                 return this.$element.controller !('ng1') === this ? 'published' : 'not-published';
               }
               verifyIAmAClass() { this.isClass = 'isClass'; }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('scope; isClass; NG1; published');
           });
         }));
      it('should support `bindToController` (boolean)', async(() => {
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'Scope: {{ title }}; Controller: {{ $ctrl.title }}',
             scope: {title: '@'},
             bindToController: false,
             controllerAs: '$ctrl',
             controller: class {}
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'Scope: {{ title }}; Controller: {{ $ctrl.title }}',
             scope: {title: '@'},
             bindToController: true,
             controllerAs: '$ctrl',
             controller: class {}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             // TODO(issue/24571): remove '!'.
             @Input() title !: string;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           @Directive({selector: 'ng1B'})
           class Ng1ComponentBFacade extends UpgradeComponent {
             // TODO(issue/24571): remove '!'.
             @Input() title !: string;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1B', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
             |
            
          `
           })
           class 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],
             schemas: [NO_ERRORS_SCHEMA]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent))
                 .toBe('Scope: WORKS; Controller: | Scope: ; Controller: WORKS');
           });
         }));
      it('should support `bindToController` (object)', async(() => {
           // Define `ng1Directive`
           const ng1Directive: angular.IDirective = {
             template: '{{ $ctrl.title }}',
             scope: {},
             bindToController: {title: '@'},
             controllerAs: '$ctrl',
             controller: class {}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             // TODO(issue/24571): remove '!'.
             @Input() title !: string;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
             dataA = 'foo';
             dataB = 'bar';
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('WORKS');
           });
         }));
      it('should support `controller` as string', async(() => {
           // Define `ng1Directive`
           const ng1Directive: angular.IDirective = {
             template: '{{ $ctrl.title }} {{ $ctrl.text }}',
             scope: {title: '@'},
             bindToController: true,
             controller: 'Ng1Controller as $ctrl'
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             // TODO(issue/24571): remove '!'.
             @Input() title !: string;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .controller('Ng1Controller', class { text = 'GREAT'; })
                                 .directive('ng1', () => ng1Directive)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('WORKS GREAT');
           });
         }));
      it('should insert the compiled content before instantiating the controller', async(() => {
           let compiledContent: string;
           let getCurrentContent: () => string;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template: 'Hello, {{ $ctrl.name }}!',
             controller: class {
               name = 'world';
               constructor($element: angular.IAugmentedJQuery) {
                 getCurrentContent = () => $element.text !();
                 compiledContent = getCurrentContent();
               }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class 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]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(compiledContent)).toBe('Hello, {{ $ctrl.name }}!');
             expect(multiTrim(getCurrentContent())).toBe('Hello, world!');
           });
         }));
    });
    describe('require', () => {
      // NOT YET SUPPORTED
      xdescribe('in pre-/post-link', () => {
        it('should resolve to its own controller if falsy', async(() => {
             // Define `ng1Directive`
             const ng1Directive: angular.IDirective = {
               template: 'Pre: {{ pre }} | Post: {{ post }}',
               controller: class {value = 'foo';},
               link: {
                 pre: function(scope: any, elem: any, attrs: any, ctrl: any) {
                   scope['pre'] = ctrl.value;
                 },
                 post: function(scope: any, elem: any, attrs: any, ctrl: any) {
                   scope['post'] = ctrl.value;
                 }
               }
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1'})
             class Ng1ComponentFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: ''})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .directive('ng1', () => ng1Directive)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(document.body.textContent)).toBe('Pre: foo | Post: foo');
             });
           }));
        // TODO: Add more tests
      });
      describe('in controller', () => {
        it('should be available to children', async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {
               template: '',
               controller: class {value = 'ng1A';}
             };
             const ng1ComponentB: angular.IComponent = {
               template: 'Required: {{ $ctrl.required.value }}',
               require: {required: '^^ng1A'}
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1A'})
             class Ng1ComponentAFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1A', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: ''})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1A', ng1ComponentA)
                                   .component('ng1B', ng1ComponentB)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentAFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(element.textContent)).toBe('Required: ng1A');
             });
           }));
        it('should throw if required controller cannot be found', async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {require: {foo: 'iDoNotExist'}};
             const ng1ComponentB: angular.IComponent = {require: {foo: '^iDoNotExist'}};
             const ng1ComponentC: angular.IComponent = {require: {foo: '^^iDoNotExist'}};
             // 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: 'ng1C'})
             class Ng1ComponentCFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1C', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2-a', template: ''})
             class Ng2ComponentA {
             }
             @Component({selector: 'ng2-b', template: ''})
             class Ng2ComponentB {
             }
             @Component({selector: 'ng2-c', template: ''})
             class Ng2ComponentC {
             }
             // Define `ng1Module`
             const mockExceptionHandler = jasmine.createSpy('$exceptionHandler');
             const ng1Module =
                 angular.module('ng1Module', [])
                     .component('ng1A', ng1ComponentA)
                     .component('ng1B', ng1ComponentB)
                     .component('ng1C', ng1ComponentC)
                     .directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
                     .directive('ng2B', downgradeComponent({component: Ng2ComponentB}))
                     .directive('ng2C', downgradeComponent({component: Ng2ComponentC}))
                     .value('$exceptionHandler', mockExceptionHandler);
             // Define `Ng2Module`
             @NgModule({
               declarations: [
                 Ng1ComponentAFacade, Ng1ComponentBFacade, Ng1ComponentCFacade, Ng2ComponentA,
                 Ng2ComponentB, Ng2ComponentC
               ],
               entryComponents: [Ng2ComponentA, Ng2ComponentB, Ng2ComponentC],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const elementA = html(``);
             const elementB = html(``);
             const elementC = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, elementA, ng1Module).then(() => {
               expect(mockExceptionHandler)
                   .toHaveBeenCalledWith(new Error(
                       'Unable to find required \'iDoNotExist\' in upgraded directive \'ng1A\'.'));
             });
             bootstrap(platformBrowserDynamic(), Ng2Module, elementB, ng1Module).then(() => {
               expect(mockExceptionHandler)
                   .toHaveBeenCalledWith(new Error(
                       'Unable to find required \'^iDoNotExist\' in upgraded directive \'ng1B\'.'));
             });
             bootstrap(platformBrowserDynamic(), Ng2Module, elementC, ng1Module).then(() => {
               expect(mockExceptionHandler)
                   .toHaveBeenCalledWith(new Error(
                       'Unable to find required \'^^iDoNotExist\' in upgraded directive \'ng1C\'.'));
             });
           }));
        it('should not throw if missing required controller is optional', async(() => {
             // Define `ng1Component`
             const ng1Component: angular.IComponent = {
               require: {
                 foo: '?iDoNotExist',
                 bar: '^?iDoNotExist',
                 baz: '?^^iDoNotExist',
               }
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1'})
             class Ng1ComponentFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: ''})
             class Ng2Component {
             }
             // Define `ng1Module`
             const mockExceptionHandler = jasmine.createSpy('$exceptionHandler');
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1', ng1Component)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}))
                                   .value('$exceptionHandler', mockExceptionHandler);
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(mockExceptionHandler).not.toHaveBeenCalled();
             });
           }));
        it('should assign resolved values to the controller instance (if `require` is not object)',
           async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {
               template: 'ng1A(
)',
               controller: class {value = 'A';}
             };
             const ng1ComponentB: angular.IComponent = {
               template: `ng1B({{ $ctrl.getProps() }})`,
               require: '^ng1A',
               controller: class {
                 getProps() {
                   // If all goes well, there should be no keys on `this`
                   return Object.keys(this).join(', ');
                 }
               }
             };
             const ng1ComponentC: angular.IComponent = {
               template: `ng1C({{ $ctrl.getProps() }})`,
               require: ['?ng1A', '^ng1A', '^^ng1A', 'ng1C', '^ng1C', '?^^ng1C'],
               controller: class {
                 getProps() {
                   // If all goes well, there should be no keys on `this`
                   return Object.keys(this).join(', ');
                 }
               }
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1B'})
             class Ng1ComponentBFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1B', elementRef, injector);
               }
             }
             @Directive({selector: 'ng1C'})
             class Ng1ComponentCFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1C', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component(
                 {selector: 'ng2', template: 'ng2( | 
)'})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1A', ng1ComponentA)
                                   .component('ng1B', ng1ComponentB)
                                   .component('ng1C', ng1ComponentC)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentBFacade, Ng1ComponentCFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(element.textContent)).toBe('ng1A(ng2(ng1B() | ng1C()))');
             });
           }));
        it('should assign resolved values to the controller instance (if `require` is object)',
           async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {
               template: 'ng1A(
)',
               controller: class {value = 'A';}
             };
             const ng1ComponentB: angular.IComponent = {
               template: `ng1B(
                 ng1A: {{ $ctrl.ng1ASelf.value }} |
                 ^ng1A: {{ $ctrl.ng1ASelfUp.value }} |
                 ^^ng1A: {{ $ctrl.ng1AParentUp.value }} |
                 ng1B: {{ $ctrl.ng1BSelf.value }} |
                 ^ng1B: {{ $ctrl.ng1BSelfUp.value }} |
                 ^^ng1B: {{ $ctrl.ng1BParentUp.value }}
               )`,
               require: {
                 ng1ASelf: '?ng1A',
                 ng1ASelfUp: '^ng1A',
                 ng1AParentUp: '^^ng1A',
                 ng1BSelf: 'ng1B',
                 ng1BSelfUp: '^ng1B',
                 ng1BParentUp: '?^^ng1B',
               },
               controller: class {value = 'B';}
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1B'})
             class Ng1ComponentBFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1B', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: 'ng2(
)'})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1A', ng1ComponentA)
                                   .component('ng1B', ng1ComponentB)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentBFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(element.textContent))
                   .toBe(
                       'ng1A(ng2(ng1B( ng1A: | ^ng1A: A | ^^ng1A: A | ng1B: B | ^ng1B: B | ^^ng1B: )))');
             });
           }));
        it('should assign to controller before calling `$onInit()`', async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {
               template: '',
               controller: class {value = 'ng1A';}
             };
             const ng1ComponentB: angular.IComponent = {
               template: '$onInit: {{ $ctrl.onInitValue }}',
               require: {required: '^^ng1A'},
               controller: class {
                 $onInit() {
                   const self = this as any;
                   self.onInitValue = self.required.value;
                 }
               }
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1B'})
             class Ng1ComponentBFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1B', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: ''})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1A', ng1ComponentA)
                                   .component('ng1B', ng1ComponentB)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentBFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html(``);
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(element.textContent)).toBe('$onInit: ng1A');
             });
           }));
        it('should use the key as name if the required controller name is omitted', async(() => {
             // Define `ng1Component`
             const ng1ComponentA: angular.IComponent = {
               template: '',
               controller: class {value = 'A';}
             };
             const ng1ComponentB:
                 angular.IComponent = {template: '', controller: class {value = 'B';}};
             const ng1ComponentC: angular.IComponent = {
               template:
                   'ng1A: {{ $ctrl.ng1A.value }} | ng1B: {{ $ctrl.ng1B.value }} | ng1C: {{ $ctrl.ng1C.value }}',
               require: {
                 ng1A: '^^',
                 ng1B: '?^',
                 ng1C: '',
               },
               controller: class {value = 'C';}
             };
             // Define `Ng1ComponentFacade`
             @Directive({selector: 'ng1C'})
             class Ng1ComponentCFacade extends UpgradeComponent {
               constructor(elementRef: ElementRef, injector: Injector) {
                 super('ng1C', elementRef, injector);
               }
             }
             // Define `Ng2Component`
             @Component({selector: 'ng2', template: ''})
             class Ng2Component {
             }
             // Define `ng1Module`
             const ng1Module = angular.module('ng1Module', [])
                                   .component('ng1A', ng1ComponentA)
                                   .component('ng1B', ng1ComponentB)
                                   .component('ng1C', ng1ComponentC)
                                   .directive('ng2', downgradeComponent({component: Ng2Component}));
             // Define `Ng2Module`
             @NgModule({
               declarations: [Ng1ComponentCFacade, Ng2Component],
               entryComponents: [Ng2Component],
               imports: [BrowserModule, UpgradeModule]
             })
             class Ng2Module {
               ngDoBootstrap() {}
             }
             // Bootstrap
             const element = html('');
             bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
               expect(multiTrim(element.textContent)).toBe('ng1A: A | ng1B: B | ng1C: C');
             });
           }));
      });
    });
    describe('transclusion', () => {
      it('should support single-slot transclusion', async(() => {
           let ng2ComponentAInstance: Ng2ComponentA;
           let ng2ComponentBInstance: Ng2ComponentB;
           // Define `ng1Component`
           const ng1Component:
               angular.IComponent = {template: 'ng1()', transclude: true};
           // 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({{ value }} | )'
           })
           class Ng2ComponentA {
             value = 'foo';
             showB = false;
             constructor() { ng2ComponentAInstance = 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 `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
             entryComponents: [Ng2ComponentA]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
             ng2ComponentAInstance.value = 'baz';
             ng2ComponentAInstance.showB = true;
             $digest(adapter);
             expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
             ng2ComponentBInstance.value = 'qux';
             $digest(adapter);
             expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
           });
         }));
      it('should support single-slot transclusion with fallback content', async(() => {
           let ng1ControllerInstances: any[] = [];
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template: 'ng1({{ $ctrl.value }}
)',
             transclude: true,
             controller:
                 class {value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); }}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: 'ng2({{ value }} | )'})
           class Ng2Component {
             value = 'from-ng2';
             constructor() { ng2ComponentInstance = this; }
           }
           // 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]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(multiTrim(element.textContent)).toBe('ng2(ng1(from-ng2) | ng1(from-ng1))');
             ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-foo');
             ng2ComponentInstance.value = 'ng2-bar';
             $digest(adapter);
             expect(multiTrim(element.textContent)).toBe('ng2(ng1(ng2-bar) | ng1(ng1-foo))');
           });
         }));
      it('should support multi-slot transclusion', async(() => {
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template:
                 'ng1(x() | y())',
             transclude: {slotX: 'contentX', slotY: 'contentY'}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
               ng2(
                 
                   {{ x }}1
                   {{ y }}1
                   {{ x }}2
                   {{ y }}2
                 
               )`
           })
           class Ng2Component {
             x = 'foo';
             y = 'bar';
             constructor() { ng2ComponentInstance = this; }
           }
           // 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(foo1foo2)|y(bar1bar2)))');
             ng2ComponentInstance.x = 'baz';
             ng2ComponentInstance.y = 'qux';
             $digest(adapter);
             expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1baz2)|y(qux1qux2)))');
           });
         }));
      it('should support default slot (with fallback content)', async(() => {
           let ng1ControllerInstances: any[] = [];
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template: 'ng1(default(fallback-{{ $ctrl.value }}
))',
             transclude: {slotX: 'contentX', slotY: 'contentY'},
             controller:
                 class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
               ng2(
                 
                   ({{ x }})
                   ignored x
                   {{ x }}-{{ y }}
                   ignored y
                   ({{ y }})
                  |
                 
                 ignored xignored y
               )`
           })
           class Ng2Component {
             x = 'foo';
             y = 'bar';
             constructor() { ng2ComponentInstance = this; }
           }
           // 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(multiTrim(element.textContent, true))
                 .toBe('ng2(ng1(default((foo)foo-bar(bar)))|ng1(default(fallback-ng1)))');
             ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-plus');
             ng2ComponentInstance.x = 'baz';
             ng2ComponentInstance.y = 'qux';
             $digest(adapter);
             expect(multiTrim(element.textContent, true))
                 .toBe('ng2(ng1(default((baz)baz-qux(qux)))|ng1(default(fallback-ng1-plus)))');
           });
         }));
      it('should support optional transclusion slots (with fallback content)', async(() => {
           let ng1ControllerInstances: any[] = [];
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template: `
               ng1(
                x({{ $ctrl.x }}
) |
                y({{ $ctrl.y }}
)
               )`,
             transclude: {slotX: '?contentX', slotY: '?contentY'},
             controller: class {
               x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
               ng2(
                 {{ x }} |
                 {{ y }}
               )`
           })
           class Ng2Component {
             x = 'ng2X';
             y = 'ng2Y';
             constructor() { ng2ComponentInstance = this; }
           }
           // 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(multiTrim(element.textContent, true))
                 .toBe('ng2(ng1(x(ng2X)|y(ng1Y))|ng1(x(ng1X)|y(ng2Y)))');
             ng1ControllerInstances.forEach(ctrl => {
               ctrl.x = 'ng1X-foo';
               ctrl.y = 'ng1Y-bar';
             });
             ng2ComponentInstance.x = 'ng2X-baz';
             ng2ComponentInstance.y = 'ng2Y-qux';
             $digest(adapter);
             expect(multiTrim(element.textContent, true))
                 .toBe('ng2(ng1(x(ng2X-baz)|y(ng1Y-bar))|ng1(x(ng1X-foo)|y(ng2Y-qux)))');
           });
         }));
      it('should throw if a non-optional slot is not filled', async(() => {
           let errorMessage: string;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template: '',
             transclude: {slotX: '?contentX', slotY: 'contentY'}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module =
               angular.module('ng1Module', [])
                   .value('$exceptionHandler', (error: Error) => errorMessage = error.message)
                   .component('ng1', ng1Component)
                   .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(errorMessage)
                 .toContain('Required transclusion slot \'slotY\' on directive: ng1');
           });
         }));
      it('should support structural directives in transcluded content', async(() => {
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             template:
                 'ng1(x() | default())',
             transclude: {slotX: 'contentX'}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: `
               ng2(
                 
                   {{ x }}1
                   {{ y }}1
                   {{ x }}2
                   {{ y }}2
                 
               )`
           })
           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 `Ng2Module`
           @NgModule({
             imports: [BrowserModule, UpgradeModule],
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             schemas: [NO_ERRORS_SCHEMA]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           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);
             expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz2)|default(qux1)))');
             ng2ComponentInstance.show = true;
             $digest(adapter);
             expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1)|default(qux2)))');
           });
         }));
    });
    describe('lifecycle hooks', () => {
      it('should call `$onChanges()` on binding destination (prototype)', fakeAsync(() => {
           const scopeOnChanges = jasmine.createSpy('scopeOnChanges');
           const controllerOnChangesA = jasmine.createSpy('controllerOnChangesA');
           const controllerOnChangesB = jasmine.createSpy('controllerOnChangesB');
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: '',
             scope: {inputA: '<'},
             bindToController: false,
             controllerAs: '$ctrl',
             controller:
                 class {$onChanges(changes: SimpleChanges) { controllerOnChangesA(changes); }}
           };
           const ng1DirectiveB: angular.IDirective = {
             template: '',
             scope: {inputB: '<'},
             bindToController: true,
             controllerAs: '$ctrl',
             controller:
                 class {$onChanges(changes: SimpleChanges) { controllerOnChangesB(changes); }}
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             @Input() inputA: any;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           @Directive({selector: 'ng1B'})
           class Ng1ComponentBFacade extends UpgradeComponent {
             @Input() inputB: any;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1B', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: ' | '
           })
           class Ng2Component {
             data = {foo: 'bar'};
             constructor() { ng2ComponentInstance = this; }
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .directive('ng1A', () => ng1DirectiveA)
                                 .directive('ng1B', () => ng1DirectiveB)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}))
                                 .run(($rootScope: angular.IRootScopeService) => {
                                   Object.getPrototypeOf($rootScope)['$onChanges'] = scopeOnChanges;
                                 });
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             // Initial change
             expect(scopeOnChanges.calls.count()).toBe(1);
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(1);
             expect(scopeOnChanges.calls.argsFor(0)[0]).toEqual({inputA: jasmine.any(Object)});
             expect(scopeOnChanges.calls.argsFor(0)[0].inputA.currentValue).toEqual({foo: 'bar'});
             expect(scopeOnChanges.calls.argsFor(0)[0].inputA.isFirstChange()).toBe(true);
             expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.currentValue).toEqual({
               foo: 'bar'
             });
             expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.isFirstChange()).toBe(true);
             // Change: Re-assign `data`
             ng2ComponentInstance.data = {foo: 'baz'};
             $digest(adapter);
             tick();
             expect(scopeOnChanges.calls.count()).toBe(2);
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(2);
             expect(scopeOnChanges.calls.argsFor(1)[0]).toEqual({inputA: jasmine.any(Object)});
             expect(scopeOnChanges.calls.argsFor(1)[0].inputA.previousValue).toEqual({foo: 'bar'});
             expect(scopeOnChanges.calls.argsFor(1)[0].inputA.currentValue).toEqual({foo: 'baz'});
             expect(scopeOnChanges.calls.argsFor(1)[0].inputA.isFirstChange()).toBe(false);
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.previousValue).toEqual({
               foo: 'bar'
             });
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.currentValue).toEqual({
               foo: 'baz'
             });
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.isFirstChange()).toBe(false);
             // No change: Update internal property
             ng2ComponentInstance.data.foo = 'qux';
             $digest(adapter);
             tick();
             expect(scopeOnChanges.calls.count()).toBe(2);
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(2);
             // Change: Re-assign `data` (even if it looks the same)
             ng2ComponentInstance.data = {foo: 'qux'};
             $digest(adapter);
             tick();
             expect(scopeOnChanges.calls.count()).toBe(3);
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(3);
             expect(scopeOnChanges.calls.argsFor(2)[0]).toEqual({inputA: jasmine.any(Object)});
             expect(scopeOnChanges.calls.argsFor(2)[0].inputA.previousValue).toEqual({foo: 'qux'});
             expect(scopeOnChanges.calls.argsFor(2)[0].inputA.currentValue).toEqual({foo: 'qux'});
             expect(scopeOnChanges.calls.argsFor(2)[0].inputA.isFirstChange()).toBe(false);
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.previousValue).toEqual({
               foo: 'qux'
             });
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.currentValue).toEqual({
               foo: 'qux'
             });
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.isFirstChange()).toBe(false);
           });
         }));
      it('should call `$onChanges()` on binding destination (instance)', fakeAsync(() => {
           const scopeOnChangesA = jasmine.createSpy('scopeOnChangesA');
           const scopeOnChangesB = jasmine.createSpy('scopeOnChangesB');
           const controllerOnChangesA = jasmine.createSpy('controllerOnChangesA');
           const controllerOnChangesB = jasmine.createSpy('controllerOnChangesB');
           let ng2ComponentInstance: Ng2Component;
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: '',
             scope: {inputA: '<'},
             bindToController: false,
             controllerAs: '$ctrl',
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['$onChanges'] = scopeOnChangesA;
                 (this as any).$onChanges = controllerOnChangesA;
               }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: '',
             scope: {inputB: '<'},
             bindToController: true,
             controllerAs: '$ctrl',
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['$onChanges'] = scopeOnChangesB;
                 (this as any).$onChanges = controllerOnChangesB;
               }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1A'})
           class Ng1ComponentAFacade extends UpgradeComponent {
             @Input() inputA: any;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1A', elementRef, injector);
             }
           }
           @Directive({selector: 'ng1B'})
           class Ng1ComponentBFacade extends UpgradeComponent {
             @Input() inputB: any;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1B', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({
             selector: 'ng2',
             template: ' | '
           })
           class Ng2Component {
             data = {foo: 'bar'};
             constructor() { ng2ComponentInstance = this; }
           }
           // 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             // Initial change
             expect(scopeOnChangesA.calls.count()).toBe(1);
             expect(scopeOnChangesB).not.toHaveBeenCalled();
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(1);
             expect(scopeOnChangesA.calls.argsFor(0)[0].inputA.currentValue).toEqual({foo: 'bar'});
             expect(scopeOnChangesA.calls.argsFor(0)[0].inputA.isFirstChange()).toBe(true);
             expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.currentValue).toEqual({
               foo: 'bar'
             });
             expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.isFirstChange()).toBe(true);
             // Change: Re-assign `data`
             ng2ComponentInstance.data = {foo: 'baz'};
             $digest(adapter);
             tick();
             expect(scopeOnChangesA.calls.count()).toBe(2);
             expect(scopeOnChangesB).not.toHaveBeenCalled();
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(2);
             expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.previousValue).toEqual({foo: 'bar'});
             expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.currentValue).toEqual({foo: 'baz'});
             expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.isFirstChange()).toBe(false);
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.previousValue).toEqual({
               foo: 'bar'
             });
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.currentValue).toEqual({
               foo: 'baz'
             });
             expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.isFirstChange()).toBe(false);
             // No change: Update internal property
             ng2ComponentInstance.data.foo = 'qux';
             $digest(adapter);
             tick();
             expect(scopeOnChangesA.calls.count()).toBe(2);
             expect(scopeOnChangesB).not.toHaveBeenCalled();
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(2);
             // Change: Re-assign `data` (even if it looks the same)
             ng2ComponentInstance.data = {foo: 'qux'};
             $digest(adapter);
             tick();
             expect(scopeOnChangesA.calls.count()).toBe(3);
             expect(scopeOnChangesB).not.toHaveBeenCalled();
             expect(controllerOnChangesA).not.toHaveBeenCalled();
             expect(controllerOnChangesB.calls.count()).toBe(3);
             expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.previousValue).toEqual({foo: 'qux'});
             expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.currentValue).toEqual({foo: 'qux'});
             expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.isFirstChange()).toBe(false);
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.previousValue).toEqual({
               foo: 'qux'
             });
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.currentValue).toEqual({
               foo: 'qux'
             });
             expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.isFirstChange()).toBe(false);
           });
         }));
      it('should call `$onInit()` on controller', async(() => {
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: false,
             controller: class {
               constructor(private $scope: angular.IScope) { $scope['called'] = 'no'; }
               $onInit() { this.$scope['called'] = 'yes'; }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: true,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 (this as any)['$onInit'] = () => $scope['called'] = 'yes';
               }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('Called: yes | Called: yes');
           });
         }));
      it('should not call `$onInit()` on scope', async(() => {
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: false,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 $scope['$onInit'] = () => $scope['called'] = 'yes';
                 Object.getPrototypeOf($scope)['$onInit'] = () => $scope['called'] = 'yes';
               }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: true,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 $scope['$onInit'] = () => $scope['called'] = 'yes';
                 Object.getPrototypeOf($scope)['$onInit'] = () => $scope['called'] = 'yes';
               }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('Called: no | Called: no');
           });
         }));
      it('should call `$postLink()` on controller', async(() => {
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: false,
             controller: class {
               constructor(private $scope: angular.IScope) { $scope['called'] = 'no'; }
               $postLink() { this.$scope['called'] = 'yes'; }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: true,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 (this as any)['$postLink'] = () => $scope['called'] = 'yes';
               }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('Called: yes | Called: yes');
           });
         }));
      it('should not call `$postLink()` on scope', async(() => {
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: false,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 $scope['$postLink'] = () => $scope['called'] = 'yes';
                 Object.getPrototypeOf($scope)['$postLink'] = () => $scope['called'] = 'yes';
               }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'Called: {{ called }}',
             bindToController: true,
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['called'] = 'no';
                 $scope['$postLink'] = () => $scope['called'] = 'yes';
                 Object.getPrototypeOf($scope)['$postLink'] = () => $scope['called'] = 'yes';
               }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent)).toBe('Called: no | Called: no');
           });
         }));
      it('should call `$doCheck()` on controller', async(() => {
           const controllerDoCheckA = jasmine.createSpy('controllerDoCheckA');
           const controllerDoCheckB = jasmine.createSpy('controllerDoCheckB');
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'ng1A',
             bindToController: false,
             controller: class {$doCheck() { controllerDoCheckA(); }}
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'ng1B',
             bindToController: true,
             controller: class {constructor() { (this as any)['$doCheck'] = controllerDoCheckB; }}
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             // Get to a stable `$digest` state.
             $digest(adapter);
             // Initial change.
             // (Do not use a specific number due to differences between AngularJS 1.5/1.6.)
             expect(controllerDoCheckA.calls.count()).toBeGreaterThan(0);
             expect(controllerDoCheckB.calls.count()).toBeGreaterThan(0);
             controllerDoCheckA.calls.reset();
             controllerDoCheckB.calls.reset();
             // Run a `$digest`
             $digest(adapter);
             expect(controllerDoCheckA.calls.count()).toBe(1);
             expect(controllerDoCheckB.calls.count()).toBe(1);
             // Run another `$digest`
             $digest(adapter);
             expect(controllerDoCheckA.calls.count()).toBe(2);
             expect(controllerDoCheckB.calls.count()).toBe(2);
           });
         }));
      it('should not call `$doCheck()` on scope', async(() => {
           const scopeDoCheck = jasmine.createSpy('scopeDoCheck');
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'ng1A',
             bindToController: false,
             controller: class {
               constructor(private $scope: angular.IScope) { $scope['$doCheck'] = scopeDoCheck; }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'ng1B',
             bindToController: true,
             controller: class {
               constructor(private $scope: angular.IScope) { $scope['$doCheck'] = scopeDoCheck; }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ' | '})
           class 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() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             // Initial change
             expect(scopeDoCheck).not.toHaveBeenCalled();
             // Run a `$digest`
             $digest(adapter);
             expect(scopeDoCheck).not.toHaveBeenCalled();
             // Run another `$digest`
             $digest(adapter);
             expect(scopeDoCheck).not.toHaveBeenCalled();
           });
         }));
      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(); }}
           };
           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);
             }
           }
           @Directive({selector: 'ng1B'})
           class Ng1ComponentBFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1B', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component(
               {selector: 'ng2', template: ' | 
'})
           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 `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html('');
           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();
             $rootScope.$apply('destroyFromNg1 = true');
             expect(multiTrim(document.body.textContent)).toBe('');
             expect(controllerOnDestroyA).toHaveBeenCalled();
             expect(controllerOnDestroyB).toHaveBeenCalled();
             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();
             $rootScope.$apply('destroyFromNg2 = true');
             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');
           // Define `ng1Directive`
           const ng1DirectiveA: angular.IDirective = {
             template: 'ng1A',
             scope: {},
             bindToController: false,
             controllerAs: '$ctrl',
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['$onDestroy'] = scopeOnDestroy;
                 Object.getPrototypeOf($scope)['$onDestroy'] = scopeOnDestroy;
               }
             }
           };
           const ng1DirectiveB: angular.IDirective = {
             template: 'ng1B',
             scope: {},
             bindToController: true,
             controllerAs: '$ctrl',
             controller: class {
               constructor($scope: angular.IScope) {
                 $scope['$onDestroy'] = scopeOnDestroy;
                 Object.getPrototypeOf($scope)['$onDestroy'] = scopeOnDestroy;
               }
             }
           };
           // 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);
             }
           }
           // Define `Ng2Component`
           @Component(
               {selector: 'ng2', template: ' | 
'})
           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 `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html('');
           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(scopeOnDestroy).not.toHaveBeenCalled();
             $rootScope.$apply('destroyFromNg1 = true');
             expect(multiTrim(document.body.textContent)).toBe('');
             expect(scopeOnDestroy).not.toHaveBeenCalled();
             $rootScope.$apply('destroyFromNg1 = false');
             expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
             expect(scopeOnDestroy).not.toHaveBeenCalled();
             $rootScope.$apply('destroyFromNg2 = true');
             expect(multiTrim(document.body.textContent)).toBe('');
             expect(scopeOnDestroy).not.toHaveBeenCalled();
           });
         }));
      it('should be called in order `$onChanges()` > `$onInit()` > `$doCheck()` > `$postLink()`',
         async(() => {
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             // `$doCheck()` will keep getting called as long as the interpolated value keeps
             // changing (by appending `> $doCheck`). Only care about the first 4 values.
             template: '{{ $ctrl.calls.slice(0, 4).join(" > ") }}',
             bindings: {value: '<'},
             controller: class {
               calls: string[] = [];
               $onChanges() { this.calls.push('$onChanges'); }
               $onInit() { this.calls.push('$onInit'); }
               $doCheck() { this.calls.push('$doCheck'); }
               $postLink() { this.calls.push('$postLink'); }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             @Input() value: any;
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .component('ng1', ng1Component)
                                 .directive('ng2', downgradeComponent({component: Ng2Component}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2Component],
             entryComponents: [Ng2Component],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
             expect(multiTrim(element.textContent))
                 .toBe('$onChanges > $onInit > $doCheck > $postLink');
           });
         }));
    });
    describe('destroying the upgraded component', () => {
      it('should destroy `$componentScope`', async(() => {
           const scopeDestroyListener = jasmine.createSpy('scopeDestroyListener');
           let ng2ComponentAInstance: Ng2ComponentA;
           // Define `ng1Component`
           const ng1Component: angular.IComponent = {
             controller: class {
               constructor($scope: angular.IScope) { $scope.$on('$destroy', scopeDestroyListener); }
             }
           };
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2A', template: ''})
           class Ng2ComponentA {
             destroyIt = false;
             constructor() { ng2ComponentAInstance = this; }
           }
           @Component({selector: 'ng2B', template: ''})
           class Ng2ComponentB {
           }
           // Define `ng1Module`
           const ng1Module = angular.module('ng1Module', [])
                                 .component('ng1', ng1Component)
                                 .directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
           // Define `Ng2Module`
           @NgModule({
             declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
             entryComponents: [Ng2ComponentA],
             imports: [BrowserModule, UpgradeModule]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             expect(scopeDestroyListener).not.toHaveBeenCalled();
             ng2ComponentAInstance.destroyIt = true;
             $digest(adapter);
             expect(scopeDestroyListener).toHaveBeenCalled();
           });
         }));
      it('should clean up `$doCheck()` watchers from the parent scope', async(() => {
           let ng2Component: Ng2Component;
           // Define `ng1Component`
           const ng1Component:
               angular.IComponent = {template: 'ng1', controller: class {$doCheck() {}}};
           // Define `Ng1ComponentFacade`
           @Directive({selector: 'ng1'})
           class Ng1ComponentFacade extends UpgradeComponent {
             constructor(elementRef: ElementRef, injector: Injector) {
               super('ng1', elementRef, injector);
             }
           }
           // Define `Ng2Component`
           @Component({selector: 'ng2', template: ''})
           class Ng2Component {
             doShow: boolean = false;
             constructor(@Inject($SCOPE) public $scope: angular.IScope) { ng2Component = this; }
           }
           // 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]
           })
           class Ng2Module {
             ngDoBootstrap() {}
           }
           // Bootstrap
           const element = html(``);
           bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
             const getWatcherCount: () => number = () =>
                 (ng2Component.$scope as any).$$watchers.length;
             const baseWatcherCount = getWatcherCount();
             expect(multiTrim(document.body.textContent)).toBe('');
             ng2Component.doShow = true;
             $digest(adapter);
             expect(multiTrim(document.body.textContent)).toBe('ng1');
             expect(getWatcherCount()).toBe(baseWatcherCount + 1);
             ng2Component.doShow = false;
             $digest(adapter);
             expect(multiTrim(document.body.textContent)).toBe('');
             expect(getWatcherCount()).toBe(baseWatcherCount);
             ng2Component.doShow = true;
             $digest(adapter);
             expect(multiTrim(document.body.textContent)).toBe('ng1');
             expect(getWatcherCount()).toBe(baseWatcherCount + 1);
           });
         }));
    });
    it('should support ng2 > ng1 > ng2 (no inputs/outputs)', async(() => {
         // Define `ng1Component`
         const ng1Component: angular.IComponent = {template: 'ng1X()'};
         // Define `Ng1ComponentFacade`
         @Directive({selector: 'ng1X'})
         class Ng1ComponentFacade extends UpgradeComponent {
           constructor(elementRef: ElementRef, injector: Injector) {
             super('ng1X', elementRef, injector);
           }
         }
         // Define `Ng2Component`
         @Component({selector: 'ng2-a', template: 'ng2A()'})
         class Ng2ComponentA {
         }
         @Component({selector: 'ng2-b', template: 'ng2B'})
         class 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: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
           entryComponents: [Ng2ComponentA, Ng2ComponentB],
           imports: [BrowserModule, UpgradeModule],
           schemas: [NO_ERRORS_SCHEMA],
         })
         class Ng2Module {
           ngDoBootstrap() {}
         }
         // Bootstrap
         const element = html(``);
         bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
           expect(multiTrim(document.body.textContent)).toBe('ng2A(ng1X(ng2B))');
         });
       }));
    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;
           constructor() { ng1ControllerXInstance = this; }
         }
         const ng1Component: angular.IComponent = {
           template: `
              ng1X({{ $ctrl.ng1XInputA }}, {{ $ctrl.ng1XInputB.value }}, {{ $ctrl.ng1XInputC.value }}) |
              
              
            `,
           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;
           // TODO(issue/24571): remove '!'.
           @Output() ng1XOutputA !: EventEmitter;
           // TODO(issue/24571): remove '!'.
           @Output() ng1XOutputB !: EventEmitter;
           constructor(elementRef: ElementRef, injector: Injector) {
             super('ng1X', elementRef, injector);
           }
         }
         // Define `Ng2Component`
         @Component({
           selector: 'ng2-a',
           template: `
              ng2A({{ ng2ADataA.value }}, {{ ng2ADataB.value }}, {{ ng2ADataC.value }}) |
              
              
            `
         })
         class Ng2ComponentA {
           ng2ADataA = {value: 'foo'};
           ng2ADataB = {value: 'bar'};
           ng2ADataC = {value: 'baz'};
           constructor() { ng2ComponentAInstance = this; }
         }
         @Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'})
         class Ng2ComponentB {
           @Input('ng2BInput1') ng2BInputA: any;
           @Input() ng2BInputC: any;
           @Output() ng2BOutputC = new EventEmitter();
           constructor() { ng2ComponentBInstance = this; }
         }
         // 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() {}
         }
         // Bootstrap
         const element = html(``);
         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();
           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();
           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();
           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();
           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();
           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();
           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();
           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();
           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`
         const ng1ComponentA: angular.IComponent = {
           template: 'ng1A()',
           controller: class {value = 'ng1A';}
         };
         const ng1ComponentB: angular.IComponent = {
           template:
               'ng1B(^^ng1A: {{ $ctrl.ng1A.value }} | ?^^ng1B: {{ $ctrl.ng1B.value }} | ^ng1B: {{ $ctrl.ng1BSelf.value }})',
           require: {ng1A: '^^', ng1B: '?^^', ng1BSelf: '^ng1B'},
           controller: class {value = 'ng1B';}
         };
         // 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);
           }
         }
         // Define `Ng2Component`
         @Component({selector: 'ng2-a', template: 'ng2A()'})
         class Ng2ComponentA {
         }
         @Component({selector: 'ng2-b', template: 'ng2B()'})
         class Ng2ComponentB {
         }
         // Define `ng1Module`
         const ng1Module = angular.module('ng1', [])
                               .component('ng1A', ng1ComponentA)
                               .component('ng1B', ng1ComponentB)
                               .directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
                               .directive('ng2B', downgradeComponent({component: Ng2ComponentB}));
         // Define `Ng2Module`
         @NgModule({
           declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2ComponentA, Ng2ComponentB],
           entryComponents: [Ng2ComponentA, Ng2ComponentB],
           imports: [BrowserModule, UpgradeModule],
           schemas: [NO_ERRORS_SCHEMA],
         })
         class Ng2Module {
           ngDoBootstrap() {}
         }
         // Bootstrap
         const element = html(``);
         bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
           expect(multiTrim(document.body.textContent))
               .toBe('ng2A(ng1A(ng2B(ng1B(^^ng1A: ng1A | ?^^ng1B: | ^ng1B: ng1B))))');
         });
       }));
  });
});