test(ivy): add missing host listener and host attribute binding tests (#22213)

PR Close #22213
This commit is contained in:
Kara Erickson 2018-02-16 11:36:50 -08:00 committed by Victor Berchet
parent 5c320b4c2a
commit 6b627f67db
5 changed files with 244 additions and 23 deletions

View File

@ -60,6 +60,7 @@ export {
e as ɵe, e as ɵe,
p as ɵp, p as ɵp,
pD as ɵpD, pD as ɵpD,
a as ɵa,
s as ɵs, s as ɵs,
t as ɵt, t as ɵt,
v as ɵv, v as ɵv,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, ContentChild, ContentChildren, Directive, HostBinding, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../src/core'; import {Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../src/core';
import * as $r3$ from '../../src/core_render3_private_export'; import * as $r3$ from '../../src/core_render3_private_export';
import {renderComponent, toHtml} from '../render_util'; import {renderComponent, toHtml} from '../render_util';
@ -174,7 +174,102 @@ describe('compiler specification', () => {
} }
expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`); expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`);
});
it('should support host listeners', () => {
type $MyApp$ = MyApp;
@Directive({selector: '[hostlistenerDir]'})
class HostListenerDir {
@HostListener('click')
onClick() {}
// NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostListenerDir,
factory: function HostListenerDir_Factory() {
const $dir$ = new HostListenerDir();
$r3$.ɵL('click', function HostListenerDir_click_Handler(event) { $dir$.onClick(); });
return $dir$;
},
});
// /NORMATIVE
}
const $e0_attrs$ = ['hostListenerDir', ''];
const $e0_dirs$ = [HostListenerDir];
@Component({
selector: 'my-app',
template: `
<button hostListenerDir>Click</button>
`
})
class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
tag: 'my-app',
factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) {
$r3$.ɵE(0, 'button', $e0_attrs$, $e0_dirs$);
$r3$.ɵT(2, 'Click');
$r3$.ɵe();
}
HostListenerDir.ngDirectiveDef.h(1, 0);
$r3$.ɵr(1, 0);
}
});
}
expect(renderComp(MyApp)).toEqual(`<button hostlistenerdir="">Click</button>`);
});
it('should support bindings of host attributes', () => {
type $MyApp$ = MyApp;
@Directive({selector: '[hostBindingDir]'})
class HostBindingDir {
@HostBinding('attr.aria-label') label = 'some label';
// NORMATIVE
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir,
factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
hostBindings: function HostBindingDir_HostBindings(
dirIndex: $number$, elIndex: $number$) {
$r3$.ɵa(elIndex, 'aria-label', $r3$.ɵb($r3$.ɵm<HostBindingDir>(dirIndex).label));
}
});
// /NORMATIVE
}
const $e0_attrs$ = ['hostBindingDir', ''];
const $e0_dirs$ = [HostBindingDir];
@Component({
selector: 'my-app',
template: `
<div hostBindingDir></div>
`
})
class MyApp {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
tag: 'my-app',
factory: function MyApp_Factory() { return new MyApp(); },
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
if (cm) {
$r3$.ɵE(0, 'div', $e0_attrs$, $e0_dirs$);
$r3$.ɵe();
}
HostBindingDir.ngDirectiveDef.h(1, 0);
$r3$.ɵr(1, 0);
}
});
}
expect(renderComp(MyApp)).toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
}); });
xit('should support structural directives', () => { xit('should support structural directives', () => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {defineComponent} from '../../src/render3/index'; import {defineComponent, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, componentRefresh, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, memory, projection, projectionDef, text, textBinding} from '../../src/render3/instructions'; import {NO_CHANGE, bind, componentRefresh, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, memory, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {containerEl, renderToHtml} from './render_util'; import {containerEl, renderToHtml} from './render_util';
@ -664,6 +664,41 @@ describe('render3 integration test', () => {
expect(renderToHtml(Template, ctx)) expect(renderToHtml(Template, ctx))
.toEqual('<span title="Hello"><b title="Goodbye"></b></span>'); .toEqual('<span title="Hello"><b title="Goodbye"></b></span>');
}); });
it('should support host attribute bindings', () => {
let hostBindingDir: HostBindingDir;
class HostBindingDir {
/* @HostBinding('attr.aria-label') */
label = 'some label';
static ngDirectiveDef = defineDirective({
type: HostBindingDir,
factory: function HostBindingDir_Factory() {
return hostBindingDir = new HostBindingDir();
},
hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) {
elementAttribute(elIndex, 'aria-label', bind(memory<HostBindingDir>(dirIndex).label));
}
});
}
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'div', ['hostBindingDir', ''], [HostBindingDir]);
elementEnd();
}
HostBindingDir.ngDirectiveDef.h(1, 0);
componentRefresh(1, 0);
}
expect(renderToHtml(Template, {}))
.toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
hostBindingDir !.label = 'other label';
expect(renderToHtml(Template, {}))
.toEqual(`<div aria-label="other label" hostbindingdir=""></div>`);
});
}); });
describe('elementStyle', () => { describe('elementStyle', () => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {defineComponent} from '../../src/render3/index'; import {defineComponent, defineDirective} from '../../src/render3/index';
import {componentRefresh, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, listener, text} from '../../src/render3/instructions'; import {componentRefresh, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, listener, text} from '../../src/render3/instructions';
import {containerEl, renderComponent, renderToHtml} from './render_util'; import {containerEl, renderComponent, renderToHtml} from './render_util';
@ -29,7 +29,7 @@ describe('event listeners', () => {
if (cm) { if (cm) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', function() { ctx.onClick(); });
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
@ -55,6 +55,40 @@ describe('event listeners', () => {
expect(comp.counter).toEqual(2); expect(comp.counter).toEqual(2);
}); });
it('should call function chain on event emit', () => {
/** <button (click)="onClick(); onClick2(); "> Click me </button> */
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button');
{
listener('click', function() {
ctx.onClick();
ctx.onClick2();
});
text(1, 'Click me');
}
elementEnd();
}
}
const ctx = {
counter: 0,
counter2: 0,
onClick: function() { this.counter++; },
onClick2: function() { this.counter2++; }
};
renderToHtml(Template, ctx);
const button = containerEl.querySelector('button') !;
button.click();
expect(ctx.counter).toBe(1);
expect(ctx.counter2).toBe(1);
button.click();
expect(ctx.counter).toBe(2);
expect(ctx.counter2).toBe(2);
});
it('should evaluate expression on event emit', () => { it('should evaluate expression on event emit', () => {
/** <button (click)="showing=!showing"> Click me </button> */ /** <button (click)="showing=!showing"> Click me </button> */
@ -62,7 +96,7 @@ describe('event listeners', () => {
if (cm) { if (cm) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', () => ctx.showing = !ctx.showing); listener('click', function() { ctx.showing = !ctx.showing; });
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
@ -97,7 +131,7 @@ describe('event listeners', () => {
if (embeddedViewStart(1)) { if (embeddedViewStart(1)) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', function() { ctx.onClick(); });
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
@ -125,6 +159,42 @@ describe('event listeners', () => {
expect(comp.counter).toEqual(2); expect(comp.counter).toEqual(2);
}); });
it('should support host listeners', () => {
let events: string[] = [];
class HostListenerDir {
/* @HostListener('click') */
onClick() { events.push('click!'); }
static ngDirectiveDef = defineDirective({
type: HostListenerDir,
factory: function HostListenerDir_Factory() {
const $dir$ = new HostListenerDir();
listener('click', function() { $dir$.onClick(); });
return $dir$;
},
});
}
function Template(ctx: any, cm: boolean) {
if (cm) {
elementStart(0, 'button', ['hostListenerDir', ''], [HostListenerDir]);
text(2, 'Click');
elementEnd();
}
HostListenerDir.ngDirectiveDef.h(1, 0);
componentRefresh(1, 0);
}
renderToHtml(Template, {});
const button = containerEl.querySelector('button') !;
button.click();
expect(events).toEqual(['click!']);
button.click();
expect(events).toEqual(['click!', 'click!']);
});
it('should destroy listeners in nested views', () => { it('should destroy listeners in nested views', () => {
/** /**
@ -152,7 +222,7 @@ describe('event listeners', () => {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', function() { ctx.onClick(); });
text(1, 'Click'); text(1, 'Click');
} }
elementEnd(); elementEnd();
@ -267,7 +337,7 @@ describe('event listeners', () => {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', () => ctx.counter1++); listener('click', function() { ctx.counter1++; });
text(1, 'Click'); text(1, 'Click');
} }
elementEnd(); elementEnd();
@ -282,7 +352,7 @@ describe('event listeners', () => {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', () => ctx.counter2++); listener('click', function() { ctx.counter2++; });
text(1, 'Click'); text(1, 'Click');
} }
elementEnd(); elementEnd();

View File

@ -46,7 +46,9 @@ describe('outputs', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -70,8 +72,8 @@ describe('outputs', () => {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ {
listener('change', ctx.onChange.bind(ctx)); listener('change', function() { ctx.onChange(); });
listener('reset', ctx.onReset.bind(ctx)); listener('reset', function() { ctx.onReset(); });
} }
elementEnd(); elementEnd();
} }
@ -96,7 +98,9 @@ describe('outputs', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ listener('change', () => ctx.counter++); } {
listener('change', function() { ctx.counter++; });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -130,7 +134,9 @@ describe('outputs', () => {
if (ctx.condition) { if (ctx.condition) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -180,7 +186,9 @@ describe('outputs', () => {
if (ctx.condition2) { if (ctx.condition2) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -241,12 +249,14 @@ describe('outputs', () => {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', function() { ctx.onClick(); });
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
elementStart(2, ButtonToggle); elementStart(2, ButtonToggle);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
elementStart(4, DestroyComp); elementStart(4, DestroyComp);
elementEnd(); elementEnd();
@ -300,7 +310,9 @@ describe('outputs', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, 'button', null, [MyButton]); elementStart(0, 'button', null, [MyButton]);
{ listener('click', ctx.onClick.bind(ctx)); } {
listener('click', function() { ctx.onClick(); });
}
elementEnd(); elementEnd();
} }
} }
@ -323,7 +335,9 @@ describe('outputs', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle, null, [OtherDir]); elementStart(0, ButtonToggle, null, [OtherDir]);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -354,7 +368,9 @@ describe('outputs', () => {
function Template(ctx: any, cm: boolean) { function Template(ctx: any, cm: boolean) {
if (cm) { if (cm) {
elementStart(0, ButtonToggle, null, [OtherDir]); elementStart(0, ButtonToggle, null, [OtherDir]);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
elementProperty(0, 'change', bind(ctx.change)); elementProperty(0, 'change', bind(ctx.change));
@ -387,7 +403,7 @@ describe('outputs', () => {
if (cm) { if (cm) {
elementStart(0, 'button'); elementStart(0, 'button');
{ {
listener('click', ctx.onClick.bind(ctx)); listener('click', function() { ctx.onClick(); });
text(1, 'Click me'); text(1, 'Click me');
} }
elementEnd(); elementEnd();
@ -398,7 +414,9 @@ describe('outputs', () => {
if (ctx.condition) { if (ctx.condition) {
if (embeddedViewStart(0)) { if (embeddedViewStart(0)) {
elementStart(0, ButtonToggle); elementStart(0, ButtonToggle);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
ButtonToggle.ngComponentDef.h(1, 0); ButtonToggle.ngComponentDef.h(1, 0);
@ -407,7 +425,9 @@ describe('outputs', () => {
} else { } else {
if (embeddedViewStart(1)) { if (embeddedViewStart(1)) {
elementStart(0, 'div', null, [OtherDir]); elementStart(0, 'div', null, [OtherDir]);
{ listener('change', ctx.onChange.bind(ctx)); } {
listener('change', function() { ctx.onChange(); });
}
elementEnd(); elementEnd();
} }
embeddedViewEnd(); embeddedViewEnd();