test(ivy): add missing host listener and host attribute binding tests (#22213)
PR Close #22213
This commit is contained in:
parent
5c320b4c2a
commit
6b627f67db
|
@ -60,6 +60,7 @@ export {
|
|||
e as ɵe,
|
||||
p as ɵp,
|
||||
pD as ɵpD,
|
||||
a as ɵa,
|
||||
s as ɵs,
|
||||
t as ɵt,
|
||||
v as ɵv,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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 {renderComponent, toHtml} from '../render_util';
|
||||
|
@ -174,7 +174,102 @@ describe('compiler specification', () => {
|
|||
}
|
||||
|
||||
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', () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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 {containerEl, renderToHtml} from './render_util';
|
||||
|
@ -664,6 +664,41 @@ describe('render3 integration test', () => {
|
|||
expect(renderToHtml(Template, ctx))
|
||||
.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', () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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 {containerEl, renderComponent, renderToHtml} from './render_util';
|
||||
|
@ -29,7 +29,7 @@ describe('event listeners', () => {
|
|||
if (cm) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', ctx.onClick.bind(ctx));
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
text(1, 'Click me');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -55,6 +55,40 @@ describe('event listeners', () => {
|
|||
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', () => {
|
||||
|
||||
/** <button (click)="showing=!showing"> Click me </button> */
|
||||
|
@ -62,7 +96,7 @@ describe('event listeners', () => {
|
|||
if (cm) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', () => ctx.showing = !ctx.showing);
|
||||
listener('click', function() { ctx.showing = !ctx.showing; });
|
||||
text(1, 'Click me');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -97,7 +131,7 @@ describe('event listeners', () => {
|
|||
if (embeddedViewStart(1)) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', ctx.onClick.bind(ctx));
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
text(1, 'Click me');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -125,6 +159,42 @@ describe('event listeners', () => {
|
|||
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', () => {
|
||||
|
||||
/**
|
||||
|
@ -152,7 +222,7 @@ describe('event listeners', () => {
|
|||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', ctx.onClick.bind(ctx));
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
text(1, 'Click');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -267,7 +337,7 @@ describe('event listeners', () => {
|
|||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', () => ctx.counter1++);
|
||||
listener('click', function() { ctx.counter1++; });
|
||||
text(1, 'Click');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -282,7 +352,7 @@ describe('event listeners', () => {
|
|||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', () => ctx.counter2++);
|
||||
listener('click', function() { ctx.counter2++; });
|
||||
text(1, 'Click');
|
||||
}
|
||||
elementEnd();
|
||||
|
|
|
@ -46,7 +46,9 @@ describe('outputs', () => {
|
|||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -70,8 +72,8 @@ describe('outputs', () => {
|
|||
if (cm) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{
|
||||
listener('change', ctx.onChange.bind(ctx));
|
||||
listener('reset', ctx.onReset.bind(ctx));
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
listener('reset', function() { ctx.onReset(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
|
@ -96,7 +98,9 @@ describe('outputs', () => {
|
|||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{ listener('change', () => ctx.counter++); }
|
||||
{
|
||||
listener('change', function() { ctx.counter++; });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -130,7 +134,9 @@ describe('outputs', () => {
|
|||
if (ctx.condition) {
|
||||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -180,7 +186,9 @@ describe('outputs', () => {
|
|||
if (ctx.condition2) {
|
||||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -241,12 +249,14 @@ describe('outputs', () => {
|
|||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', ctx.onClick.bind(ctx));
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
text(1, 'Click me');
|
||||
}
|
||||
elementEnd();
|
||||
elementStart(2, ButtonToggle);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
elementStart(4, DestroyComp);
|
||||
elementEnd();
|
||||
|
@ -300,7 +310,9 @@ describe('outputs', () => {
|
|||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
elementStart(0, 'button', null, [MyButton]);
|
||||
{ listener('click', ctx.onClick.bind(ctx)); }
|
||||
{
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +335,9 @@ describe('outputs', () => {
|
|||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
elementStart(0, ButtonToggle, null, [OtherDir]);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -354,7 +368,9 @@ describe('outputs', () => {
|
|||
function Template(ctx: any, cm: boolean) {
|
||||
if (cm) {
|
||||
elementStart(0, ButtonToggle, null, [OtherDir]);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
elementProperty(0, 'change', bind(ctx.change));
|
||||
|
@ -387,7 +403,7 @@ describe('outputs', () => {
|
|||
if (cm) {
|
||||
elementStart(0, 'button');
|
||||
{
|
||||
listener('click', ctx.onClick.bind(ctx));
|
||||
listener('click', function() { ctx.onClick(); });
|
||||
text(1, 'Click me');
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -398,7 +414,9 @@ describe('outputs', () => {
|
|||
if (ctx.condition) {
|
||||
if (embeddedViewStart(0)) {
|
||||
elementStart(0, ButtonToggle);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
ButtonToggle.ngComponentDef.h(1, 0);
|
||||
|
@ -407,7 +425,9 @@ describe('outputs', () => {
|
|||
} else {
|
||||
if (embeddedViewStart(1)) {
|
||||
elementStart(0, 'div', null, [OtherDir]);
|
||||
{ listener('change', ctx.onChange.bind(ctx)); }
|
||||
{
|
||||
listener('change', function() { ctx.onChange(); });
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
|
Loading…
Reference in New Issue