describe('compiler compliance: dependency injection', () => {
  const angularFiles = setup({
    compileAngular: true,
    compileAnimations: false,
    compileCommon: true,
  });

  it('should create factory methods', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule, Injectable, Attribute, Host, SkipSelf, Self, Optional} from '@angular/core';
              import {CommonModule} from '@angular/common';

              @Injectable()
              export class MyService {}

              @Component({
                selector: 'my-component',
                template: \`\`
              })
              export class MyComponent {
                constructor(
                  @Attribute('name') name:string,
                  s1: MyService, 
                  @Host() s2: MyService,
                  @Self() s4: MyService,
                  @SkipSelf() s3: MyService,
                  @Optional() s5: MyService,
                  @Self() @Optional() s6: MyService,
                ) {}
              }

              @NgModule({declarations: [MyComponent], imports: [CommonModule], providers: [MyService]})
              export class MyModule {}
          `
      }
    };

    const factory = `
      factory: function MyComponent_Factory() {
        return new MyComponent(
          $r3$.ɵinjectAttribute('name'),
          $r3$.ɵdirectiveInject(MyService), 
          $r3$.ɵdirectiveInject(MyService, 1),
          $r3$.ɵdirectiveInject(MyService, 2),
          $r3$.ɵdirectiveInject(MyService, 4),
          $r3$.ɵdirectiveInject(MyService, 8),
          $r3$.ɵdirectiveInject(MyService, 10)
        );
      }`;


    const result = compile(files, angularFiles);

    expectEmit(result.source, factory, 'Incorrect factory');
  });

});
describe('compiler compliance: listen()', () => {
  const angularFiles = setup({
    compileAngular: true,
    compileAnimations: false,
    compileCommon: true,
  });

  it('should create listener instruction on element', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`<div (click)="onClick($event); 1 == 2"></div>\`
              })
              export class MyComponent {
                onClick(event: any) {}
              }

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    // The template should look like this (where IDENT is a wild card for an identifier):
    const template = `
        template: function MyComponent_Template(rf, ctx) {
          if (rf & 1) {
            $r3$.ɵE(0, "div");
            $r3$.ɵL("click", function MyComponent_Template_div_click_listener($event) {
              ctx.onClick($event);
              return (1 == 2);
            });
            $r3$.ɵe();
          }
        }
        `;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

});
describe('compiler compliance: styling', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  describe('@Component.styles', () => {
    it('should pass in the component metadata styles into the component definition and shim them using style encapsulation',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  styles: ["div.foo { color: red; }", ":host p:nth-child(even) { --webkit-transition: 1s linear all; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template =
             'styles: ["div.foo[_ngcontent-%COMP%] { color: red; }", "[_nghost-%COMP%]   p[_ngcontent-%COMP%]:nth-child(even) { --webkit-transition: 1s linear all; }"]';
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should pass in styles, but skip shimming the styles if the view encapsulation signals not to',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  encapsulation: ${ViewEncapsulation.None},
                  styles: ["div.tall { height: 123px; }", ":host.small p { height:5px; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = 'div.tall { height: 123px; }", ":host.small p { height:5px; }';
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to native',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  encapsulation: ${ViewEncapsulation.Native},
                  selector: "my-component",
                  styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
         MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
           …
           styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"],
           encapsulation: 1
         })
         `;
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  describe('@Component.animations', () => {
    it('should pass in the component metadata animations into the component definition', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  animations: [{name: 'foo123'}, {name: 'trigger123'}],
                  template: ""
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          type: MyComponent,
          selectors:[["my-component"]],
          factory:function MyComponent_Factory(t){
            return new (t || MyComponent)();
          },
          consts: 0,
          vars: 0,
          template:  function MyComponent_Template(rf, $ctx$) {
          },
          encapsulation: 2,
          data: {
            animation: [{name: 'foo123'}, {name: 'trigger123'}]
          }
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should include animations even if the provided array is empty', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  animations: [],
                  template: ""
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          type: MyComponent,
          selectors:[["my-component"]],
          factory:function MyComponent_Factory(t){
            return new (t || MyComponent)();
          },
          consts: 0,
          vars: 0,
          template:  function MyComponent_Template(rf, $ctx$) {
          },
          encapsulation: 2,
          data: {
            animation: []
          }
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should generate any animation triggers into the component template', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  template: \`
                    <div [@foo]='exp'></div>
                    <div @bar></div>
                    <div [@baz]></div>\`,
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        const $e0_attrs$ = ["@foo", ""];
        const $e1_attrs$ = ["@bar", ""];
        const $e2_attrs$ = ["@baz", ""];
        …
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          …
          consts: 3,
          vars: 1,
          template:  function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelement(0, "div", $e0_attrs$);
              $r3$.ɵelement(1, "div", $e1_attrs$);
              $r3$.ɵelement(2, "div", $e2_attrs$);
            }
            if (rf & 2) {
              $r3$.ɵelementProperty(0, "@foo", $r3$.ɵbind(ctx.exp));
            }
          },
          encapsulation: 2
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });
  });

  describe('[style] and [style.prop]', () => {
    it('should create style instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute style instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div style="opacity:1"
                                   [attr.style]="'border-width: 10px'"
                                   [style.width]="myWidth"
                                   [style]="myStyleExp"
                                   [style.height]="myHeight"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myWidth = '100px';
                  myHeight = '100px';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
          const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              consts: 1,
              vars: 1,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
                  $r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
                  $r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
                  $r3$.ɵelementStylingApply(0);
                  $r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
                }
              },
              encapsulation: 2
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should assign a sanitizer instance to the element style allocation instruction if any url-based properties are detected',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style.background-image]="myImage">\`
                })
                export class MyComponent {
                  myImage = 'url(foo.jpg)';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["background-image"];
          export class MyComponent {
              constructor() {
                  this.myImage = 'url(foo.jpg)';
              }
          }

          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
            type: MyComponent,
            selectors: [["my-component"]],
            factory: function MyComponent_Factory(t) {
              return new (t || MyComponent)();
            },
            consts: 1,
            vars: 0,
            template:  function MyComponent_Template(rf, ctx) {
              if (rf & 1) {
                $r3$.ɵelementStart(0, "div");
                $r3$.ɵelementStyling(null, _c0, $r3$.ɵdefaultStyleSanitizer);
                $r3$.ɵelementEnd();
              }
              if (rf & 2) {
                $r3$.ɵelementStyleProp(0, 0, ctx.myImage);
                $r3$.ɵelementStylingApply(0);
              }
            },
            encapsulation: 2
          });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should support [style.foo.suffix] style bindings with a suffix', () => {

      const files = {
        app: {
          'spec.ts': `
             import {Component, NgModule} from '@angular/core';

             @Component({
               selector: 'my-component',
               template: \`<div [style.font-size.px]="12">\`
             })
             export class MyComponent {
             }

             @NgModule({declarations: [MyComponent]})
             export class MyModule {}
         `
        }
      };

      const template = `
          const $e0_styles$= ["font-size"];
          …
          template:  function MyComponent_Template(rf, ctx) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, _c0);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStyleProp(0, 0, 12, "px");
              $r3$.ɵelementStylingApply(0);
            }
          }
     `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');

    });
  });

  describe('[class]', () => {
    it('should create class styling instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [class]="myClassExp"></div>\`
                })
                export class MyComponent {
                  myClassExp = {'foo':true}
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling();
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0,$ctx$.myClassExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute class instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="grape"
                                   [attr.class]="'banana'"
                                   [class.apple]="yesToApple"
                                   [class]="myClassExp"
                                   [class.orange]="yesToOrange"></div>\`
                })
                export class MyComponent {
                  myClassExp = {a:true, b:true};
                  yesToApple = true;
                  yesToOrange = true;
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
          const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              consts: 1,
              vars: 1,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling($e0_cd$);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
                  $r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
                  $r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
                  $r3$.ɵelementStylingApply(0);
                  $r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
                }
              },
              encapsulation: 2
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should not generate the styling apply instruction if there are only static style/class attributes',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="foo"
                                   style="width:100px"
                                   [attr.class]="'round'"
                                   [attr.style]="'height:100px'"></div>\`
                })
                export class MyComponent {}

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class", "style"];
          const $e0_cd$ = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
          const $e0_sd$ = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              consts: 1,
              vars: 2,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling($e0_cd$, $e0_sd$);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("round"));
                  $r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("height:100px"), $r3$.ɵsanitizeStyle);
                }
              },
              encapsulation: 2
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  describe('[style] mixed with [class]', () => {
    it('should combine [style] and [class] bindings into a single instruction', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp" [class]="myClassExp"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myClassExp = 'foo bar apple';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, $ctx$.myClassExp, $ctx$.myStyleExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should stamp out pipe definitions in the creation block if used by styling bindings',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp | stylePipe" [class]="myClassExp | classPipe"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myClassExp = 'foo bar apple';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵpipe(1, "classPipe");
              $r3$.ɵpipe(2, "stylePipe");
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, $r3$.ɵpipeBind1(1, 0, $ctx$.myClassExp), $r3$.ɵpipeBind1(2, 2, $ctx$.myStyleExp));
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should properly offset multiple style pipe references for styling bindings', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`
                    <div [class]="{}"
                         [class.foo]="fooExp | pipe:2000"
                         [style]="myStyleExp | pipe:1000"
                         [style.bar]="barExp | pipe:3000"
                         [style.baz]="bazExp | pipe:4000">
                         {{ item }}</div>\`
                })
                export class MyComponent {
                  myStyleExp = {};
                  fooExp = 'foo';
                  barExp = 'bar';
                  bazExp = 'baz';
                  items = [1,2,3];
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling($e0_styling$, $e1_styling$, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵpipe(1, "pipe");
              $r3$.ɵpipe(2, "pipe");
              $r3$.ɵpipe(3, "pipe");
              $r3$.ɵpipe(4, "pipe");
              $r3$.ɵtext(5);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, $e2_styling$, $r3$.ɵpipeBind2(1, 1, $ctx$.myStyleExp, 1000));
              $r3$.ɵelementStyleProp(0, 0, $r3$.ɵpipeBind2(2, 4, $ctx$.barExp, 3000));
              $r3$.ɵelementStyleProp(0, 1, $r3$.ɵpipeBind2(3, 7, $ctx$.bazExp, 4000));
              $r3$.ɵelementClassProp(0, 0, $r3$.ɵpipeBind2(4, 10, $ctx$.fooExp, 2000));
              $r3$.ɵelementStylingApply(0);
              $r3$.ɵtextBinding(5, $r3$.ɵinterpolation1(" ", $ctx$.item, ""));
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });
  });

  describe('@Component host styles/classes', () => {
    it('should generate style/class instructions for a host component creation definition', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule, HostBinding} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: '',
                  host: {
                    'style': 'width:200px; height:500px',
                    'class': 'foo baz'
                  }
                })
                export class MyComponent {
                  @HostBinding('style')
                  myStyle = {width:'100px'};

                  @HostBinding('class')
                  myClass = {bar:false};

                  @HostBinding('style.color')
                  myColorProp = 'red';

                  @HostBinding('class.foo')
                  myFooClass = 'red';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
          const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
          …
          hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
            if (rf & 1) {
              $r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
              $r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
              $r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
              $r3$.ɵelementStylingApply(elIndex, ctx);
            }
          },
          consts: 0,
          vars: 0,
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should generate style/class instructions for multiple host binding definitions', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule, HostBinding} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: '',
                  host: {
                    '[style.height.pt]': 'myHeightProp',
                    '[class.bar]': 'myBarClass'
                  }
                })
                export class MyComponent {
                  myHeightProp = 20;
                  myBarClass = true;

                  @HostBinding('style')
                  myStyle = {};

                  @HostBinding('style.width')
                  myWidthProp = '500px';

                  @HostBinding('class.foo')
                  myFooClass = true;

                  @HostBinding('class')
                  myClasses = {a:true, b:true};
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          const _c0 = ["bar", "foo"];
          const _c1 = ["height", "width"];
          …
          hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
            if (rf & 1) {
              $r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(elIndex, ctx.myClasses, ctx.myStyle, ctx);
              $r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeightProp, "pt", ctx);
              $r3$.ɵelementStyleProp(elIndex, 1, ctx.myWidthProp, null, ctx);
              $r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
              $r3$.ɵelementClassProp(elIndex, 1, ctx.myFooClass, ctx);
              $r3$.ɵelementStylingApply(elIndex, ctx);
            }
          },
          consts: 0,
          vars: 0,
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should generate styling instructions for multiple directives that contain host binding definitions',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Directive, Component, NgModule, HostBinding} from '@angular/core';

                @Directive({selector: '[myClassDir]'})
                export class ClassDirective {
                  @HostBinding('class')
                  myClassMap = {red: true};
                }

                @Directive({selector: '[myWidthDir]'})
                export class WidthDirective {
                  @HostBinding('style.width')
                  myWidth = 200;

                  @HostBinding('class.foo')
                  myFooClass = true;
                }

                @Directive({selector: '[myHeightDir]'})
                export class HeightDirective {
                  @HostBinding('style.height')
                  myHeight = 200;

                  @HostBinding('class.bar')
                  myBarClass = true;
                }

                @Component({
                  selector: 'my-component',
                  template: '
                    <div myWidthDir myHeightDir myClassDir></div>
                  ',
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent, WidthDirective, HeightDirective, ClassDirective]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["foo"];
          const _c1 = ["width"];
          const _c2 = ["bar"];
          const _c3 = ["height"];
          …
          function ClassDirective_HostBindings(rf, ctx, elIndex) {
            if (rf & 1) {
              $r3$.ɵelementStyling(null, null, null, ctx);
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(elIndex, ctx.myClassMap, null, ctx);
              $r3$.ɵelementStylingApply(elIndex, ctx);
            }
          }
          …
          function WidthDirective_HostBindings(rf, ctx, elIndex) {
            if (rf & 1) {
              $r3$.ɵelementStyling(_c0, _c1, null, ctx);
            }
            if (rf & 2) {
              $r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
              $r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
              $r3$.ɵelementStylingApply(elIndex, ctx);
            }
          }
          …
          function HeightDirective_HostBindings(rf, ctx, elIndex) {
            if (rf & 1) {
              $r3$.ɵelementStyling(_c2, _c3, null, ctx);
            }
            if (rf & 2) {
              $r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
              $r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
              $r3$.ɵelementStylingApply(elIndex, ctx);
            }
          }
          …
          `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  it('should count only non-style and non-class host bindings on Components', () => {
    const files = {
      app: {
        'spec.ts': `
          import {Component, NgModule, HostBinding} from '@angular/core';

          @Component({
            selector: 'my-component',
            template: '',
            host: {
              'style': 'width:200px; height:500px',
              'class': 'foo baz'
            }
          })
          export class MyComponent {
            @HostBinding('style')
            myStyle = {width:'100px'};

            @HostBinding('class')
            myClass = {bar:false};

            @HostBinding('id')
            id = 'some id';

            @HostBinding('title')
            title = 'some title';
          }

          @NgModule({declarations: [MyComponent]})
          export class MyModule {}
        `
      }
    };

    const template = `
      const $_c0$ = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
      const $_c1$ = ["width", "height", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
      …
      hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
        if (rf & 1) {
          $r3$.ɵallocHostVars(2);
          $r3$.ɵelementStyling($_c0$, $_c1$, $r3$.ɵdefaultStyleSanitizer, ctx);
        }
        if (rf & 2) {
          $r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id));
          $r3$.ɵelementProperty(elIndex, "title", $r3$.ɵbind(ctx.title));
          $r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
          $r3$.ɵelementStylingApply(elIndex, ctx);
        }
      },
      consts: 0,
      vars: 0,
    `;

    const result = compile(files, angularFiles);
    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should count only non-style and non-class host bindings on Directives', () => {
    const files = {
      app: {
        'spec.ts': `
          import {Directive, Component, NgModule, HostBinding} from '@angular/core';

          @Directive({selector: '[myWidthDir]'})
          export class WidthDirective {
            @HostBinding('style.width')
            myWidth = 200;

            @HostBinding('class.foo')
            myFooClass = true;

            @HostBinding('id')
            id = 'some id';

            @HostBinding('title')
            title = 'some title';
          }
        `
      }
    };

    const template = `
      const $_c0$ = ["foo"];
      const $_c1$ = ["width"];
      …
      hostBindings: function WidthDirective_HostBindings(rf, ctx, elIndex) {
        if (rf & 1) {
          $r3$.ɵallocHostVars(2);
          $r3$.ɵelementStyling($_c0$, $_c1$, null, ctx);
        }
        if (rf & 2) {
          $r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.id));
          $r3$.ɵelementProperty(elIndex, "title", $r3$.ɵbind(ctx.title));
          $r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
          $r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
          $r3$.ɵelementStylingApply(elIndex, ctx);
        }
      }
    `;

    const result = compile(files, angularFiles);
    expectEmit(result.source, template, 'Incorrect template');
  });
});
describe('compiler compliance: styling', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  describe('@Component.styles', () => {
    it('should pass in the component metadata styles into the component definition and shim them using style encapsulation',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  styles: ["div.foo { color: red; }", ":host p:nth-child(even) { --webkit-transition: 1s linear all; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template =
             'styles: ["div.foo[_ngcontent-%COMP%] { color: red; }", "[_nghost-%COMP%]   p[_ngcontent-%COMP%]:nth-child(even) { --webkit-transition: 1s linear all; }"]';
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should pass in styles, but skip shimming the styles if the view encapsulation signals not to',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  encapsulation: ${ViewEncapsulation.None},
                  styles: ["div.tall { height: 123px; }", ":host.small p { height:5px; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = 'div.tall { height: 123px; }", ":host.small p { height:5px; }';
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should pass in the component metadata styles into the component definition but skip shimming when style encapsulation is set to native',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  encapsulation: ${ViewEncapsulation.Native},
                  selector: "my-component",
                  styles: ["div.cool { color: blue; }", ":host.nice p { color: gold; }"],
                  template: "..."
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = 'div.cool { color: blue; }", ":host.nice p { color: gold; }';
         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  describe('@Component.animations', () => {
    it('should pass in the component metadata animations into the component definition', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  animations: [{name: 'foo123'}, {name: 'trigger123'}],
                  template: ""
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          type: MyComponent,
          selectors:[["my-component"]],
          factory:function MyComponent_Factory(t){
            return new (t || MyComponent)();
          },
          features: [$r3$.ɵPublicFeature],
          consts: 0,
          vars: 0,
          template:  function MyComponent_Template(rf, $ctx$) {
          },
          animations: [{name: 'foo123'}, {name: 'trigger123'}]
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should include animations even if the provided array is empty', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  animations: [],
                  template: ""
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          type: MyComponent,
          selectors:[["my-component"]],
          factory:function MyComponent_Factory(t){
            return new (t || MyComponent)();
          },
          features: [$r3$.ɵPublicFeature],
          consts: 0,
          vars: 0,
          template:  function MyComponent_Template(rf, $ctx$) {
          },
          animations: []
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should generate any animation triggers into the component template', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: "my-component",
                  template: \`
                    <div [@foo]='exp'></div>
                    <div @bar></div>
                    <div [@baz]></div>\`,
                })
                export class MyComponent {
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
        const $e0_attrs$ = ["@foo", ""];
        const $e1_attrs$ = ["@bar", ""];
        const $e2_attrs$ = ["@baz", ""];
        …
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          …
          template:  function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelement(0, "div", $e0_attrs$);
              $r3$.ɵelement(1, "div", $e1_attrs$);
              $r3$.ɵelement(2, "div", $e2_attrs$);
            }
            if (rf & 2) {
              $r3$.ɵelementAttribute(0, "@foo", $r3$.ɵbind(ctx.exp));
            }
          }
        });
      `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });
  });

  describe('[style] and [style.prop]', () => {
    it('should create style instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute style instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div style="opacity:1"
                                   [attr.style]="'border-width: 10px'"
                                   [style.width]="myWidth"
                                   [style]="myStyleExp"
                                   [style.height]="myHeight"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myWidth = '100px';
                  myHeight = '100px';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
          const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              features: [$r3$.ɵPublicFeature],
              consts: 1,
              vars: 1,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵdefaultStyleSanitizer);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementStylingMap(0, null, $ctx$.myStyleExp);
                  $r3$.ɵelementStyleProp(0, 1, $ctx$.myWidth);
                  $r3$.ɵelementStyleProp(0, 2, $ctx$.myHeight);
                  $r3$.ɵelementStylingApply(0);
                  $r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("border-width: 10px"), $r3$.ɵsanitizeStyle);
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should assign a sanitizer instance to the element style allocation instruction if any url-based properties are detected',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style.background-image]="myImage">\`
                })
                export class MyComponent {
                  myImage = 'url(foo.jpg)';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["background-image"];
          export class MyComponent {
              constructor() {
                  this.myImage = 'url(foo.jpg)';
              }
          }

          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
            type: MyComponent,
            selectors: [["my-component"]],
            factory: function MyComponent_Factory(t) {
              return new (t || MyComponent)();
            },
            features: [$r3$.ɵPublicFeature],
            consts: 1,
            vars: 0,
            template:  function MyComponent_Template(rf, ctx) {
              if (rf & 1) {
                $r3$.ɵelementStart(0, "div");
                $r3$.ɵelementStyling(null, _c0, $r3$.ɵdefaultStyleSanitizer);
                $r3$.ɵelementEnd();
              }
              if (rf & 2) {
                $r3$.ɵelementStyleProp(0, 0, ctx.myImage);
                $r3$.ɵelementStylingApply(0);
              }
            }
          });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should support [style.foo.suffix] style bindings with a suffix', () => {

      const files = {
        app: {
          'spec.ts': `
             import {Component, NgModule} from '@angular/core';

             @Component({
               selector: 'my-component',
               template: \`<div [style.font-size.px]="12">\`
             })
             export class MyComponent {
             }

             @NgModule({declarations: [MyComponent]})
             export class MyModule {}
         `
        }
      };

      const template = `
          const $e0_styles$= ["font-size"];
          …
          template:  function MyComponent_Template(rf, ctx) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, _c0);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStyleProp(0, 0, 12, "px");
              $r3$.ɵelementStylingApply(0);
            }
          }
     `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');

    });
  });

  describe('[class]', () => {
    it('should create class styling instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [class]="myClassExp"></div>\`
                })
                export class MyComponent {
                  myClassExp = {'foo':true}
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling();
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0,$ctx$.myClassExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute class instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="grape"
                                   [attr.class]="'banana'"
                                   [class.apple]="yesToApple"
                                   [class]="myClassExp"
                                   [class.orange]="yesToOrange"></div>\`
                })
                export class MyComponent {
                  myClassExp = {a:true, b:true};
                  yesToApple = true;
                  yesToOrange = true;
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
          const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              features: [$r3$.ɵPublicFeature],
              consts: 1,
              vars: 1,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling($e0_cd$);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementStylingMap(0, $ctx$.myClassExp);
                  $r3$.ɵelementClassProp(0, 1, $ctx$.yesToApple);
                  $r3$.ɵelementClassProp(0, 2, $ctx$.yesToOrange);
                  $r3$.ɵelementStylingApply(0);
                  $r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("banana"));
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should not generate the styling apply instruction if there are only static style/class attributes',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="foo"
                                   style="width:100px"
                                   [attr.class]="'round'"
                                   [attr.style]="'height:100px'"></div>\`
                })
                export class MyComponent {}

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class", "style"];
          const $e0_cd$ = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
          const $e0_sd$ = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(t){
                return new (t || MyComponent)();
              },
              features: [$r3$.ɵPublicFeature],
              consts: 1,
              vars: 2,
              template:  function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵelementStart(0, "div", $e0_attrs$);
                  $r3$.ɵelementStyling($e0_cd$, $e0_sd$);
                  $r3$.ɵelementEnd();
                }
                if (rf & 2) {
                  $r3$.ɵelementAttribute(0, "class", $r3$.ɵbind("round"));
                  $r3$.ɵelementAttribute(0, "style", $r3$.ɵbind("height:100px"), $r3$.ɵsanitizeStyle);
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  describe('[style] mixed with [class]', () => {
    it('should combine [style] and [class] bindings into a single instruction', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp" [class]="myClassExp"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myClassExp = 'foo bar apple';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵelementStart(0, "div");
              $r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
              $r3$.ɵelementEnd();
            }
            if (rf & 2) {
              $r3$.ɵelementStylingMap(0, $ctx$.myClassExp, $ctx$.myStyleExp);
              $r3$.ɵelementStylingApply(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

  });
});
describe('compiler compliance: template', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  it('should correctly bind to context in nested template', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                  <ul *ngFor="let outer of items">
                    <li *ngFor="let middle of outer.items">
                      <div *ngFor="let inner of items"
                           (click)="onClick(outer, middle, inner)"
                           [title]="format(outer, middle, inner, component)"
                           >
                        {{format(outer, middle, inner, component)}}
                      </div>
                    </li>
                  </ul>\`
              })
              export class MyComponent {
                component = this;
                format(outer: any, middle: any, inner: any) { }
                onClick(outer: any, middle: any, inner: any) { }
              }

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    // The template should look like this (where IDENT is a wild card for an identifier):
    const template = `
      const $c0$ = ["ngFor","","ngForOf",""];
      function MyComponent_ul_li_div_Template_1(rf, ctx) {

        if (rf & 1) {
          const $s$ = $i0$.ɵgV();
          $i0$.ɵE(0, "div");
          $i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
            $i0$.ɵrV($s$);
            const $inner$ = ctx.$implicit;
            const $middle$ = $i0$.ɵx().$implicit;
            const $outer$ = $i0$.ɵx().$implicit;
            const $myComp$ = $i0$.ɵx();
            return $myComp$.onClick($outer$, $middle$, $inner$);
          });
          $i0$.ɵT(1);
          $i0$.ɵe();
        }

        if (rf & 2) {
          const $inner1$ = ctx.$implicit;
          const $middle1$ = $i0$.ɵx().$implicit;
          const $outer1$ = $i0$.ɵx().$implicit;
          const $myComp1$ = $i0$.ɵx();
          $i0$.ɵp(0, "title", $i0$.ɵb($myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component)));
          $i0$.ɵt(1, $i0$.ɵi1(" ", $myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component), " "));
        }
      }

      function MyComponent_ul_li_Template_1(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "li");
          $i0$.ɵC(1, MyComponent_ul_li_div_Template_1, null, _c0);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $myComp2$ = $i0$.ɵx(2);
          $i0$.ɵp(1, "ngForOf", $i0$.ɵb($myComp2$.items));
        }
      }

      function MyComponent_ul_Template_0(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "ul");
          $i0$.ɵC(1, MyComponent_ul_li_Template_1, null, _c0);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $outer2$ = ctx.$implicit;
          $i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer2$.items));
        }
      }
      // ...
      template:function MyComponent_Template(rf, ctx){
        if (rf & 1) {
          $i0$.ɵC(0, MyComponent_ul_Template_0, null, _c0);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should support ngFor context variables', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                    <span *ngFor="let item of items; index as i">
                      {{ i }} - {{ item }}
                    </span>\`
              })
              export class MyComponent {}

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    const template = `
      const $c0$ = ["ngFor", "", "ngForOf", ""];

      function MyComponent_span_Template_0(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "span");
          $i0$.ɵT(1);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $item$ = ctx.$implicit;
          const $i$ = ctx.index;
          $i0$.ɵt(1, $i0$.ɵi2(" ", $i$, " - ", $item$, " "));
        }
      }
      // ...
      template:function MyComponent_Template(rf, ctx){
        if (rf & 1) {
          $i0$.ɵC(0, MyComponent_span_Template_0, null, _c0);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should support ngFor context variables in parent views', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                  <div *ngFor="let item of items; index as i">
                      <span *ngIf="showing">
                        {{ i }} - {{ item }}
                      </span>
                  </div>\`
              })
              export class MyComponent {}

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    const template = `
      const $c0$ = ["ngFor", "", "ngForOf", ""];
      const $c1$ = ["ngIf", ""];

      function MyComponent_div_span_Template_1(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "span");
          $i0$.ɵT(1);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $div$ = $i0$.ɵx();
          const $i$ = $div$.index;
          const $item$ = $div$.$implicit;
          $i0$.ɵt(1, $i0$.ɵi2(" ", $i$, " - ", $item$, " "));
        }
      }

      function MyComponent_div_Template_0(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "div");
          $i0$.ɵC(1, MyComponent_div_span_Template_1, null, $c1$);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $app$ = $i0$.ɵx();
          $i0$.ɵp(1, "ngIf", $i0$.ɵb($app$.showing));
        }
      }

      // ...
      template:function MyComponent_Template(rf, ctx){
        if (rf & 1) {
          $i0$.ɵC(0, MyComponent_div_Template_0, null, $c0$);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should correctly skip contexts as needed', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                  <div *ngFor="let outer of items">
                    <div *ngFor="let middle of outer.items">
                      <div *ngFor="let inner of middle.items">
                        {{ middle.value }} - {{ name }}
                      </div>
                    </div>
                  </div>\`
              })
              export class MyComponent {}

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    // The template should look like this (where IDENT is a wild card for an identifier):
    const template = `
      const $c0$ = ["ngFor", "", "ngForOf", ""];
      function MyComponent_div_div_div_Template_1(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "div");
          $i0$.ɵT(1);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $middle$ = $i0$.ɵx().$implicit;
          const $myComp$ = $i0$.ɵx(2);
          $i0$.ɵt(1, $i0$.ɵi2(" ", $middle$.value, " - ", $myComp$.name, " "));
        }
      }

      function MyComponent_div_div_Template_1(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "div");
          $i0$.ɵC(1, MyComponent_div_div_div_Template_1, null, _c0);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $middle$ = ctx.$implicit;
          $i0$.ɵp(1, "ngForOf", $i0$.ɵb($middle$.items));
        }
      }

      function MyComponent_div_Template_0(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵE(0, "div");
          $i0$.ɵC(1, MyComponent_div_div_Template_1, null, _c0);
          $i0$.ɵe();
        }
        if (rf & 2) {
          const $outer$ = ctx.$implicit;
          $i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer$.items));
        }
      }
      // ...
      template:function MyComponent_Template(rf, ctx){
        if (rf & 1) {
          $i0$.ɵC(0, MyComponent_div_Template_0, null, _c0);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should support <ng-template>', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                  <ng-template [boundAttr]="b" attr="l">
                    some-content
                  </ng-template>\`
              })
              export class MyComponent {}

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    const template = `
      const $c0$ = ["attr", "", "boundAttr", ""];

      function Template_0(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵT(0, " some-content ");
        }
      }

      // ...

      template: function MyComponent_Template(rf, ctx) {
        if (rf & 1) {
          $i0$.ɵC(0, Template_0, null, $c0$);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "boundAttr", $i0$.ɵb(ctx.b));
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });
});
describe('compiler compliance: styling', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  describe('[style] and [style.prop]', () => {
    it('should create style instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style]="myStyleExp"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵE(0, "div");
              $r3$.ɵs(null, null, $r3$.ɵzss);
              $r3$.ɵe();
            }
            if (rf & 2) {
              $r3$.ɵsm(0, null, $ctx$.myStyleExp);
              $r3$.ɵsa(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute style instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div style="opacity:1"
                                   [attr.style]="'border-width: 10px'"
                                   [style.width]="myWidth"
                                   [style]="myStyleExp"
                                   [style.height]="myHeight"></div>\`
                })
                export class MyComponent {
                  myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
                  myWidth = '100px';
                  myHeight = '100px';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(){
                return new MyComponent();
              },
              features: [$r3$.ɵPublicFeature],
              template: function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵE(0, "div");
                  $r3$.ɵs(null, _c0, $r3$.ɵzss);
                  $r3$.ɵe();
                }
                if (rf & 2) {
                  $r3$.ɵsm(0, null, $ctx$.myStyleExp);
                  $r3$.ɵsp(0, 1, $ctx$.myWidth);
                  $r3$.ɵsp(0, 2, $ctx$.myHeight);
                  $r3$.ɵsa(0);
                  $r3$.ɵa(0, "style", $r3$.ɵb("border-width: 10px"), $r3$.ɵzs);
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should assign a sanitizer instance to the element style allocation instruction if any url-based properties are detected',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [style.background-image]="myImage">\`
                })
                export class MyComponent {
                  myImage = 'url(foo.jpg)';
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["background-image"];
          export class MyComponent {
              constructor() {
                  this.myImage = 'url(foo.jpg)';
              }
          }

          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
            type: MyComponent,
            selectors: [["my-component"]],
            factory: function MyComponent_Factory() {
              return new MyComponent();
            },
            features: [$r3$.ɵPublicFeature],
            template: function MyComponent_Template(rf, ctx) {
              if (rf & 1) {
                $r3$.ɵE(0, "div");
                $r3$.ɵs(null, _c0, $r3$.ɵzss);
                $r3$.ɵe();
              }
              if (rf & 2) {
                $r3$.ɵsp(0, 0, ctx.myImage);
                $r3$.ɵsa(0);
              }
            }
          });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });

  describe('[class]', () => {
    it('should create class styling instructions on the element', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div [class]="myClassExp"></div>\`
                })
                export class MyComponent {
                  myClassExp = {'foo':true}
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
        }
      };

      const template = `
          template: function MyComponent_Template(rf, $ctx$) {
            if (rf & 1) {
              $r3$.ɵE(0, "div");
              $r3$.ɵs();
              $r3$.ɵe();
            }
            if (rf & 2) {
              $r3$.ɵsm(0,$ctx$.myClassExp);
              $r3$.ɵsa(0);
            }
          }
          `;

      const result = compile(files, angularFiles);
      expectEmit(result.source, template, 'Incorrect template');
    });

    it('should place initial, multi, singular and application followed by attribute class instructions in the template code in that order',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="grape"
                                   [attr.class]="'banana'"
                                   [class.apple]="yesToApple"
                                   [class]="myClassExp"
                                   [class.orange]="yesToOrange"></div>\`
                })
                export class MyComponent {
                  myClassExp = {a:true, b:true};
                  yesToApple = true;
                  yesToOrange = true;
                }

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(){
                return new MyComponent();
              },
              features: [$r3$.ɵPublicFeature],
              template: function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵE(0, "div");
                  $r3$.ɵs(_c0);
                  $r3$.ɵe();
                }
                if (rf & 2) {
                  $r3$.ɵsm(0, $ctx$.myClassExp);
                  $r3$.ɵcp(0, 1, $ctx$.yesToApple);
                  $r3$.ɵcp(0, 2, $ctx$.yesToOrange);
                  $r3$.ɵsa(0);
                  $r3$.ɵa(0, "class", $r3$.ɵb("banana"));
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });

    it('should not generate the styling apply instruction if there are only static style/class attributes',
       () => {
         const files = {
           app: {
             'spec.ts': `
                import {Component, NgModule} from '@angular/core';

                @Component({
                  selector: 'my-component',
                  template: \`<div class="foo"
                                   style="width:100px"
                                   [attr.class]="'round'"
                                   [attr.style]="'height:100px'"></div>\`
                })
                export class MyComponent {}

                @NgModule({declarations: [MyComponent]})
                export class MyModule {}
            `
           }
         };

         const template = `
          const _c0 = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
          const _c1 = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
          …
          MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
              type: MyComponent,
              selectors:[["my-component"]],
              factory:function MyComponent_Factory(){
                return new MyComponent();
              },
              features: [$r3$.ɵPublicFeature],
              template: function MyComponent_Template(rf, $ctx$) {
                if (rf & 1) {
                  $r3$.ɵE(0, "div");
                  $r3$.ɵs(_c0, _c1);
                  $r3$.ɵe();
                }
                if (rf & 2) {
                  $r3$.ɵa(0, "class", $r3$.ɵb("round"));
                  $r3$.ɵa(0, "style", $r3$.ɵb("height:100px"), $r3$.ɵzs);
                }
              }
            });
        `;

         const result = compile(files, angularFiles);
         expectEmit(result.source, template, 'Incorrect template');
       });
  });
});
describe('compiler compliance: template', () => {
  const angularFiles = setup({
    compileAngular: true,
    compileAnimations: false,
    compileCommon: true,
  });

  it('should correctly bind to context in nested template', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';
              import {CommonModule} from '@angular/common';

              @Component({
                selector: 'my-component',
                template: \`
                  <ul *ngFor="let outer of items">
                    <li *ngFor="let middle of outer.items">
                      <div *ngFor="let inner of items"
                           (click)="onClick(outer, middle, inner)"
                           [title]="format(outer, middle, inner, component)"
                           >
                        {{format(outer, middle, inner, component)}}
                      </div>
                    </li>
                  </ul>\`
              })
              export class MyComponent {
                component = this;
                format(outer: any, middle: any, inner: any) { }
                onClick(outer: any, middle: any, inner: any) { }
              }

              @NgModule({declarations: [MyComponent], imports: [CommonModule]})
              export class MyModule {}
          `
      }
    };

    // The template should look like this (where IDENT is a wild card for an identifier):
    const template = `
      const $c0$ = ["ngFor","","ngForOf",""];
      // ...
      template:function MyComponent_Template(rf, $ctx$){
        if (rf & 1) {
          $i0$.ɵC(0, MyComponent_ul_Template_0, null, _c0);
        }
        if (rf & 2) {
          $i0$.ɵp(0, "ngForOf", $i0$.ɵb($ctx$.items));
        }

        function MyComponent_ul_Template_0(rf, $ctx0$) {
          if (rf & 1) {
            $i0$.ɵE(0, "ul");
            $i0$.ɵC(1, MyComponent_ul_li_Template_1, null, _c0);
            $i0$.ɵe();
          }
          if (rf & 2) {
            const $outer$ = $ctx0$.$implicit;
            $i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer$.items));
          }
          function MyComponent_ul_li_Template_1(rf, $ctx1$) {
            if (rf & 1) {
              $i0$.ɵE(0, "li");
              $i0$.ɵC(1, MyComponent_ul_li_div_Template_1, null, _c0);
              $i0$.ɵe();
            }
            if (rf & 2) {
              $i0$.ɵp(1, "ngForOf", $i0$.ɵb($ctx$.items));
            }
            function MyComponent_ul_li_div_Template_1(rf, $ctx2$) {
              if (rf & 1) {
                $i0$.ɵE(0, "div");
                $i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
                  const $outer$ = $ctx0$.$implicit;
                  const $middle$ = $ctx1$.$implicit;
                  const $inner$ = $ctx2$.$implicit;
                  return ctx.onClick($outer$, $middle$, $inner$);
                });
                $i0$.ɵT(1);
                $i0$.ɵe();
              }
              if (rf & 2) {
                const $outer$ = $ctx0$.$implicit;
                const $middle$ = $ctx1$.$implicit;
                const $inner$ = $ctx2$.$implicit;
                $i0$.ɵp(0, "title", $i0$.ɵb(ctx.format($outer$, $middle$, $inner$, $ctx$.component)));
                $i0$.ɵt(1, $i0$.ɵi1(" ", ctx.format($outer$, $middle$, $inner$, $ctx$.component), " "));
              }
            }
          }
        }
      }`;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

});
describe('compiler compliance: directives', () => {

  const angularFiles = setup({
    compileAngular: false,
    compileAnimations: false,
    compileFakeCore: true,
  });

  describe('matching', () => {

    it('should not match directives on i18n attribute', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, Directive, Input, NgModule} from '@angular/core';
    
                @Directive({selector: '[i18n]'})
                export class I18nDirective {}
    
                @Component({selector: 'my-component', template: '<div i18n></div>'})
                export class MyComponent {}
    
                @NgModule({declarations: [I18nDirective, MyComponent]})
                export class MyModule{}`
        }
      };

      // MyComponent definition should be:
      const MyComponentDefinition = `
            MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                type: MyComponent, 
                selectors: [["my-component"]], 
                factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }, 
                features: [$r3$.ɵPublicFeature], 
                consts: 1, 
                vars: 0, 
                template: function MyComponent_Template(rf, ctx) {
                    if (rf & 1) {
                        $r3$.ɵelement(0, "div");
                    }
                }
            });
        `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
    });

    it('should not match directives on i18n-prefixed attributes', () => {
      const files = {
        app: {
          'spec.ts': `
                import {Component, Directive, Input, NgModule} from '@angular/core';
    
                @Directive({selector: '[i18n]'})
                export class I18nDirective {}

                @Directive({selector: '[i18n-foo]'})
                export class I18nFooDirective {}

                @Directive({selector: '[foo]'})
                export class FooDirective {}
    
                @Component({selector: 'my-component', template: '<div i18n-foo></div>'})
                export class MyComponent {}
    
                @NgModule({declarations: [I18nDirective, I18nFooDirective, FooDirective, MyComponent]})
                export class MyModule{}`
        }
      };

      // MyComponent definition should be:
      const MyComponentDefinition = `
            MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                type: MyComponent, 
                selectors: [["my-component"]], 
                factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }, 
                features: [$r3$.ɵPublicFeature], 
                consts: 1, 
                vars: 0, 
                template: function MyComponent_Template(rf, ctx) {
                    if (rf & 1) {
                        $r3$.ɵelement(0, "div");
                    }
                }
            });
        `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
    });

    it('should match directives on element bindings', () => {

      const files = {
        app: {
          'spec.ts': `
                        import {Component, Directive, Input, NgModule} from '@angular/core';
            
                        @Directive({selector: '[someDirective]'})
                        export class SomeDirective {
                            @Input() someDirective;
                        }
            
                        @Component({selector: 'my-component', template: '<div [someDirective]="true"></div>'})
                        export class MyComponent {}
            
                        @NgModule({declarations: [SomeDirective, MyComponent]})
                        export class MyModule{}
                  `
        }
      };


      // MyComponent definition should be:
      const MyComponentDefinition = `
                …    
                const _c0 = [${AttributeMarker.SelectOnly}, "someDirective"];
                …
                MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                    …
                    template: function MyComponent_Template(rf, ctx) {
                        if (rf & 1) {
                            $r3$.ɵelement(0, "div", _c0);
                        }
                        if (rf & 2) {
                            $r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
                        }
                    },
                    …
                    directives: [SomeDirective]
                });
            `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
    });

    it('should match directives on ng-template bindings', () => {

      const files = {
        app: {
          'spec.ts': `
                        import {Component, Directive, Input, NgModule} from '@angular/core';
            
                        @Directive({selector: '[someDirective]'})
                        export class SomeDirective {
                            @Input() someDirective;
                        }
            
                        @Component({selector: 'my-component', template: '<ng-template [someDirective]="true"></ng-template>'})
                        export class MyComponent {}
            
                        @NgModule({declarations: [SomeDirective, MyComponent]})
                        export class MyModule{}
                  `
        }
      };


      // MyComponent definition should be:
      const MyComponentDefinition = `
                …    
                const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
                …
                MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                    …
                    template: function MyComponent_Template(rf, ctx) {
                        if (rf & 1) {
                            $r3$.ɵtemplate(0, Template_0, 0, 0, null, $c0_a0$);
                        }
                        if (rf & 2) {
                            $r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
                        }
                    },
                    …
                    directives: [SomeDirective]
                });
            `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
    });

    it('should match structural directives', () => {

      const files = {
        app: {
          'spec.ts': `
                        import {Component, Directive, Input, NgModule} from '@angular/core';
            
                        @Directive({selector: '[someDirective]'})
                        export class SomeDirective {
                            @Input() someDirective;
                        }
            
                        @Component({selector: 'my-component', template: '<div *someDirective></div>'})
                        export class MyComponent {}
            
                        @NgModule({declarations: [SomeDirective, MyComponent]})
                        export class MyModule{}
                  `
        }
      };

      // MyComponent definition should be:
      const MyComponentDefinition = `
                …    
                const $c0_a0$ = ["someDirective", ""];
                …
                MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                    …
                    template: function MyComponent_Template(rf, ctx) {
                        if (rf & 1) {
                            $r3$.ɵtemplate(0, MyComponent_div_Template_0, 1, 0, null, $c0_a0$);
                        }
                    },
                    …
                    directives: [SomeDirective]
                });
            `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');

    });

    it('should match directives on element outputs', () => {

      const files = {
        app: {
          'spec.ts': `
                        import {Component, Directive, Output, EventEmitter, NgModule} from '@angular/core';
            
                        @Directive({selector: '[someDirective]'})
                        export class SomeDirective {
                            @Output() someDirective = new EventEmitter();
                        }
            
                        @Component({selector: 'my-component', template: '<div (someDirective)="noop()"></div>'})
                        export class MyComponent {
                            noop() {}
                        }
            
                        @NgModule({declarations: [SomeDirective, MyComponent]})
                        export class MyModule{}
                  `
        }
      };


      // MyComponent definition should be:
      const MyComponentDefinition = `
                …    
                const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
                …
                MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
                    …
                    template: function MyComponent_Template(rf, ctx) {
                        if (rf & 1) {
                            $r3$.ɵelementStart(0, "div", $c0_a0$);
                            $r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_listener($event) { return ctx.noop(); });
                            $r3$.ɵelementEnd();
                        }
                    },
                    …
                    directives: [SomeDirective]
                });
            `;

      const result = compile(files, angularFiles);
      const source = result.source;

      expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
    });

  });
});
describe('compiler compliance: listen()', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  it('should create listener instruction on element', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`<div (click)="onClick($event); 1 == 2"></div>\`
              })
              export class MyComponent {
                onClick(event: any) {}
              }

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    // The template should look like this (where IDENT is a wild card for an identifier):
    const template = `
        const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
        …
        template: function MyComponent_Template(rf, ctx) {
          if (rf & 1) {
            $r3$.ɵelementStart(0, "div", $e0_attrs$);
            $r3$.ɵlistener("click", function MyComponent_Template_div_click_0_listener($event) {
              ctx.onClick($event);
              return (1 == 2);
            });
            $r3$.ɵelementEnd();
          }
        }
        `;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should create listener instruction on other components', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-app',
                template: \`<div>My App</div>\`
              })
              export class MyApp {}

              @Component({
                selector: 'my-component',
                template: \`<my-app (click)="onClick($event);"></my-app>\`
              })
              export class MyComponent {
                onClick(event: any) {}
              }

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    const template = `
        const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
        …
        template: function MyComponent_Template(rf, ctx) {
          if (rf & 1) {
            $r3$.ɵelementStart(0, "my-app", $e0_attrs$);
            $r3$.ɵlistener("click", function MyComponent_Template_my_app_click_0_listener($event) {
              return ctx.onClick($event);
            });
            $r3$.ɵelementEnd();
          }
        }
        `;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('should create multiple listener instructions that share a view snapshot', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, NgModule} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`
                  <div *ngIf="showing">
                    <div (click)="onClick(foo)"></div>
                    <button (click)="onClick2(bar)"></button>
                  </div>

                \`
              })
              export class MyComponent {
                onClick(name: any) {}
                onClick2(name: any) {}
              }

              @NgModule({declarations: [MyComponent]})
              export class MyModule {}
          `
      }
    };

    const template = `
        const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "ngIf"];
        const $e_attrs$ = [${AttributeMarker.SelectOnly}, "click"];

        function MyComponent_div_0_Template(rf, ctx) {
          if (rf & 1) {
            const $s$ = $r3$.ɵgetCurrentView();
            $r3$.ɵelementStart(0, "div");
            $r3$.ɵelementStart(1, "div", $e_attrs$);
            $r3$.ɵlistener("click", function MyComponent_div_0_Template_div_click_1_listener($event) {
              $r3$.ɵrestoreView($s$);
              const $comp$ = $r3$.ɵnextContext();
              return $comp$.onClick($comp$.foo);
            });
            $r3$.ɵelementEnd();
            $r3$.ɵelementStart(2, "button", $e_attrs$);
            $r3$.ɵlistener("click", function MyComponent_div_0_Template_button_click_2_listener($event) {
              $r3$.ɵrestoreView($s$);
              const $comp2$ = $r3$.ɵnextContext();
              return $comp2$.onClick2($comp2$.bar);
            });
            $r3$.ɵelementEnd();
            $r3$.ɵelementEnd();
          }
        }
        // ...
        template: function MyComponent_Template(rf, ctx) {
          if (rf & 1) {
            $r3$.ɵtemplate(0, MyComponent_div_0_Template, 3, 0, "div", $c0$);
          }
          if (rf & 2) {
            $i0$.ɵelementProperty(0, "ngIf", $i0$.ɵbind(ctx.showing));
          }
        }
        `;

    const result = compile(files, angularFiles);

    expectEmit(result.source, template, 'Incorrect template');
  });

  it('local refs in listeners defined before the local refs', () => {
    const files = {
      app: {
        'spec.ts': `
            import {Component, NgModule} from '@angular/core';

            @Component({
              selector: 'my-component',
              template: \`
                <button (click)="onClick(user.value)">Save</button>
                <input #user>
              \`
            })
            export class MyComponent {}

            @NgModule({declarations: [MyComponent]})
            export class MyModule {}
          `
      }
    };

    const MyComponentDefinition = `
        const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
        const $e2_refs$ = ["user", ""];
        …
        MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
          type: MyComponent,
          selectors: [["my-component"]],
          factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
          consts: 4,
          vars: 0,
          template:  function MyComponent_Template(rf, ctx) {
            if (rf & 1) {
              const $s$ = $r3$.ɵgetCurrentView();
              $r3$.ɵelementStart(0, "button", $e0_attrs$);
                $r3$.ɵlistener("click", function MyComponent_Template_button_click_0_listener($event) {
                   $r3$.ɵrestoreView($s$);
                   const $user$ = $r3$.ɵreference(3);
                   return ctx.onClick($user$.value);
                });
                $r3$.ɵtext(1, "Save");
              $r3$.ɵelementEnd();
              $r3$.ɵelement(2, "input", null, $e2_refs$);
            }
          },
          encapsulation: 2
        });
      `;

    const result = compile(files, angularFiles);
    const source = result.source;

    expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
  });

});
describe('compiler compliance: listen()', () => {
  const angularFiles = setup({
    compileAngular: false,
    compileFakeCore: true,
    compileAnimations: false,
  });

  it('should create declare inputs/outputs', () => {
    const files = {
      app: {
        'spec.ts': `
              import {Component, Directive, NgModule, Input, Output} from '@angular/core';

              @Component({
                selector: 'my-component',
                template: \`\`
              })
              export class MyComponent {
                @Input() componentInput;
                @Input('renamedComponentInput') originalComponentInput;

                @Output() componentOutput;
                @Output('renamedComponentOutput') originalComponentOutput;
              }

              @Directive({
                selector: '[my-directive]',
              })
              export class MyDirective {
                @Input() directiveInput;
                @Input('renamedDirectiveInput') originalDirectiveInput;

                @Output() directiveOutput;
                @Output('renamedDirectiveOutput') originalDirectiveOutput;
              }

              @NgModule({declarations: [MyComponent, MyDirective]})
              export class MyModule {}
          `
      }
    };

    const componentDef = `
      MyComponent.ngComponentDef = IDENT.ɵdefineComponent({
          …
          inputs:{
            componentInput: "componentInput",
            originalComponentInput: ["renamedComponentInput", "originalComponentInput"]
          },
          outputs: {
            componentOutput: "componentOutput",
            originalComponentOutput: "renamedComponentOutput"
          }
          …
        });`;

    const directiveDef = `
      MyDirective.ngDirectiveDef = IDENT.ɵdefineDirective({
        …
        inputs:{
          directiveInput: "directiveInput",
          originalDirectiveInput: ["renamedDirectiveInput", "originalDirectiveInput"]
        },
        outputs: {
          directiveOutput: "directiveOutput",
          originalDirectiveOutput: "renamedDirectiveOutput"
        }
        …
      });`;


    const result = compile(files, angularFiles);

    expectEmit(result.source, componentDef, 'Incorrect component definition');
    expectEmit(result.source, directiveDef, 'Incorrect directive definition');
  });

});