build: reformat repo to new clang@1.4.0 (#36613)

PR Close #36613
This commit is contained in:
Joey Perrott
2020-04-13 16:40:21 -07:00
committed by atscott
parent 5e80e7e216
commit 698b0288be
1160 changed files with 31667 additions and 24000 deletions

View File

@ -18,12 +18,9 @@ export class BrowserAnimationBuilder extends AnimationBuilder {
constructor(rootRenderer: RendererFactory2, @Inject(DOCUMENT) doc: any) {
super();
const typeData = {
id: '0',
encapsulation: ViewEncapsulation.None,
styles: [],
data: {animation: []}
} as RendererType2;
const typeData =
{id: '0', encapsulation: ViewEncapsulation.None, styles: [], data: {animation: []}} as
RendererType2;
this._renderer = rootRenderer.createRenderer(doc.body, typeData) as AnimationRenderer;
}
@ -37,7 +34,9 @@ export class BrowserAnimationBuilder extends AnimationBuilder {
}
export class BrowserAnimationFactory extends AnimationFactory {
constructor(private _id: string, private _renderer: AnimationRenderer) { super(); }
constructor(private _id: string, private _renderer: AnimationRenderer) {
super();
}
create(element: any, options?: AnimationOptions): AnimationPlayer {
return new RendererAnimationPlayer(this._id, element, options || {}, this._renderer);
@ -62,34 +61,58 @@ export class RendererAnimationPlayer implements AnimationPlayer {
return issueAnimationCommand(this._renderer, this.element, this.id, command, args);
}
onDone(fn: () => void): void { this._listen('done', fn); }
onDone(fn: () => void): void {
this._listen('done', fn);
}
onStart(fn: () => void): void { this._listen('start', fn); }
onStart(fn: () => void): void {
this._listen('start', fn);
}
onDestroy(fn: () => void): void { this._listen('destroy', fn); }
onDestroy(fn: () => void): void {
this._listen('destroy', fn);
}
init(): void { this._command('init'); }
init(): void {
this._command('init');
}
hasStarted(): boolean { return this._started; }
hasStarted(): boolean {
return this._started;
}
play(): void {
this._command('play');
this._started = true;
}
pause(): void { this._command('pause'); }
pause(): void {
this._command('pause');
}
restart(): void { this._command('restart'); }
restart(): void {
this._command('restart');
}
finish(): void { this._command('finish'); }
finish(): void {
this._command('finish');
}
destroy(): void { this._command('destroy'); }
destroy(): void {
this._command('destroy');
}
reset(): void { this._command('reset'); }
reset(): void {
this._command('reset');
}
setPosition(p: number): void { this._command('setPosition', p); }
setPosition(p: number): void {
this._command('setPosition', p);
}
getPosition(): number { return 0; }
getPosition(): number {
return 0;
}
public totalTime = 0;
}

View File

@ -15,7 +15,7 @@ const DISABLE_ANIMATIONS_FLAG = '@.disabled';
// Define a recursive type to allow for nested arrays of `AnimationTriggerMetadata`. Note that an
// interface declaration is used as TypeScript prior to 3.7 does not support recursive type
// references, see https://github.com/microsoft/TypeScript/pull/33050 for details.
type NestedAnimationTriggerMetadata = AnimationTriggerMetadata | RecursiveAnimationTriggerMetadata;
type NestedAnimationTriggerMetadata = AnimationTriggerMetadata|RecursiveAnimationTriggerMetadata;
interface RecursiveAnimationTriggerMetadata extends Array<NestedAnimationTriggerMetadata> {}
@Injectable()
@ -84,7 +84,9 @@ export class AnimationRendererFactory implements RendererFactory2 {
private _scheduleCountTask() {
// always use promise to schedule microtask instead of use Zone
this.promise.then(() => { this._microtaskId++; });
this.promise.then(() => {
this._microtaskId++;
});
}
/** @internal */
@ -125,16 +127,20 @@ export class AnimationRendererFactory implements RendererFactory2 {
}
}
whenRenderingDone(): Promise<any> { return this.engine.whenRenderingDone(); }
whenRenderingDone(): Promise<any> {
return this.engine.whenRenderingDone();
}
}
export class BaseAnimationRenderer implements Renderer2 {
constructor(
protected namespaceId: string, public delegate: Renderer2, public engine: AnimationEngine) {
this.destroyNode = this.delegate.destroyNode ? (n) => delegate.destroyNode !(n) : null;
this.destroyNode = this.delegate.destroyNode ? (n) => delegate.destroyNode!(n) : null;
}
get data() { return this.delegate.data; }
get data() {
return this.delegate.data;
}
destroyNode: ((n: any) => void)|null;
@ -147,9 +153,13 @@ export class BaseAnimationRenderer implements Renderer2 {
return this.delegate.createElement(name, namespace);
}
createComment(value: string) { return this.delegate.createComment(value); }
createComment(value: string) {
return this.delegate.createComment(value);
}
createText(value: string) { return this.delegate.createText(value); }
createText(value: string) {
return this.delegate.createText(value);
}
appendChild(parent: any, newChild: any): void {
this.delegate.appendChild(parent, newChild);
@ -169,9 +179,13 @@ export class BaseAnimationRenderer implements Renderer2 {
return this.delegate.selectRootElement(selectorOrNode, preserveContent);
}
parentNode(node: any) { return this.delegate.parentNode(node); }
parentNode(node: any) {
return this.delegate.parentNode(node);
}
nextSibling(node: any) { return this.delegate.nextSibling(node); }
nextSibling(node: any) {
return this.delegate.nextSibling(node);
}
setAttribute(el: any, name: string, value: string, namespace?: string|null|undefined): void {
this.delegate.setAttribute(el, name, value, namespace);
@ -181,9 +195,13 @@ export class BaseAnimationRenderer implements Renderer2 {
this.delegate.removeAttribute(el, name, namespace);
}
addClass(el: any, name: string): void { this.delegate.addClass(el, name); }
addClass(el: any, name: string): void {
this.delegate.addClass(el, name);
}
removeClass(el: any, name: string): void { this.delegate.removeClass(el, name); }
removeClass(el: any, name: string): void {
this.delegate.removeClass(el, name);
}
setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2|undefined): void {
this.delegate.setStyle(el, style, value, flags);
@ -201,7 +219,9 @@ export class BaseAnimationRenderer implements Renderer2 {
}
}
setValue(node: any, value: string): void { this.delegate.setValue(node, value); }
setValue(node: any, value: string): void {
this.delegate.setValue(node, value);
}
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void {
return this.delegate.listen(target, eventName, callback);
@ -253,7 +273,7 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer
}
}
function resolveElementFromTarget(target: 'window' | 'document' | 'body' | any): any {
function resolveElementFromTarget(target: 'window'|'document'|'body'|any): any {
switch (target) {
case 'body':
return document.body;

View File

@ -7,7 +7,7 @@
*/
import {AnimationBuilder} from '@angular/animations';
import {AnimationDriver, ɵAnimationEngine as AnimationEngine, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵCssKeyframesDriver as CssKeyframesDriver, ɵNoopAnimationDriver as NoopAnimationDriver, ɵWebAnimationsDriver as WebAnimationsDriver, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer, ɵsupportsWebAnimations as supportsWebAnimations} from '@angular/animations/browser';
import {AnimationDriver, ɵAnimationEngine as AnimationEngine, ɵAnimationStyleNormalizer as AnimationStyleNormalizer, ɵCssKeyframesDriver as CssKeyframesDriver, ɵNoopAnimationDriver as NoopAnimationDriver, ɵsupportsWebAnimations as supportsWebAnimations, ɵWebAnimationsDriver as WebAnimationsDriver, ɵWebAnimationsStyleNormalizer as WebAnimationsStyleNormalizer} from '@angular/animations/browser';
import {DOCUMENT} from '@angular/common';
import {Inject, Injectable, InjectionToken, NgZone, Provider, RendererFactory2} from '@angular/core';
import {ɵDomRendererFactory2 as DomRendererFactory2} from '@angular/platform-browser';

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationPlayer, AnimationTriggerMetadata, animate, state, style, transition, trigger} from '@angular/animations';
import {animate, AnimationPlayer, AnimationTriggerMetadata, state, style, transition, trigger} from '@angular/animations';
import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser';
import {Component, Injectable, NgZone, RendererFactory2, RendererType2, ViewChild} from '@angular/core';
import {TestBed} from '@angular/core/testing';
@ -15,289 +15,139 @@ import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_rendere
import {el} from '../../testing/src/browser_util';
(function() {
if (isNode) return;
describe('AnimationRenderer', () => {
let element: any;
beforeEach(() => {
element = el('<div></div>');
if (isNode) return;
describe('AnimationRenderer', () => {
let element: any;
beforeEach(() => {
element = el('<div></div>');
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: MockAnimationEngine}],
imports: [BrowserAnimationsModule]
});
});
function makeRenderer(animationTriggers: any[] = []) {
const type = <RendererType2>{
id: 'id',
encapsulation: null !,
styles: [],
data: {'animation': animationTriggers}
};
return (TestBed.inject(RendererFactory2) as AnimationRendererFactory)
.createRenderer(element, type);
}
it('should hook into the engine\'s insert operations when appending children', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
renderer.appendChild(container, element);
expect(engine.captures['onInsert'].pop()).toEqual([element]);
});
it('should hook into the engine\'s insert operations when inserting a child before another',
() => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
const element2 = el('<div></div>');
container.appendChild(element2);
renderer.insertBefore(container, element, element2);
expect(engine.captures['onInsert'].pop()).toEqual([element]);
});
it('should hook into the engine\'s insert operations when removing children', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
renderer.removeChild(container, element);
expect(engine.captures['onRemove'].pop()).toEqual([element]);
});
it('should hook into the engine\'s setProperty call if the property begins with `@`', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
renderer.setProperty(element, 'prop', 'value');
expect(engine.captures['setProperty']).toBeFalsy();
renderer.setProperty(element, '@prop', 'value');
expect(engine.captures['setProperty'].pop()).toEqual([element, 'prop', 'value']);
});
// https://github.com/angular/angular/issues/32794
it('should support nested animation triggers', () => {
makeRenderer([[trigger('myAnimation', [])]]);
const {triggers} = TestBed.inject(AnimationEngine) as MockAnimationEngine;
expect(triggers.length).toEqual(1);
expect(triggers[0].name).toEqual('myAnimation');
});
describe('listen', () => {
it('should hook into the engine\'s listen call if the property begins with `@`', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const cb = (event: any): boolean => { return true; };
renderer.listen(element, 'event', cb);
expect(engine.captures['listen']).toBeFalsy();
renderer.listen(element, '@event.phase', cb);
expect(engine.captures['listen'].pop()).toEqual([element, 'event', 'phase']);
});
it('should resolve the body|document|window nodes given their values as strings as input',
() => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const cb = (event: any): boolean => { return true; };
renderer.listen('body', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(document.body);
renderer.listen('document', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(document);
renderer.listen('window', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(window);
});
});
describe('registering animations', () => {
it('should only create a trigger definition once even if the registered multiple times');
});
describe('flushing animations', () => {
// these tests are only mean't to be run within the DOM
if (isNode) return;
it('should flush and fire callbacks when the zone becomes stable', (async) => {
@Component({
selector: 'my-cmp',
template: '<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)"></div>',
animations: [trigger(
'myAnimation',
[transition(
'* => state',
[style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
})
class Cmp {
exp: any;
event: any;
onStart(event: any) { this.event = event; }
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const engine = TestBed.inject(AnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = 'state';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(cmp.event.triggerName).toEqual('myAnimation');
expect(cmp.event.phaseName).toEqual('start');
cmp.event = null;
engine.flush();
expect(cmp.event).toBeFalsy();
async();
});
});
it('should properly insert/remove nodes through the animation renderer that do not contain animations',
(async) => {
@Component({
selector: 'my-cmp',
template: '<div #elm *ngIf="exp"></div>',
animations: [trigger(
'someAnimation',
[transition(
'* => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
})
class Cmp {
exp: any;
@ViewChild('elm') public element: any;
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
cmp.exp = false;
const element = cmp.element;
expect(element.nativeElement.parentNode).toBeTruthy();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.nativeElement.parentNode).toBeFalsy();
async();
});
});
});
it('should only queue up dom removals if the element itself contains a valid leave animation',
() => {
@Component({
selector: 'my-cmp',
template: `
<div #elm1 *ngIf="exp1"></div>
<div #elm2 @animation1 *ngIf="exp2"></div>
<div #elm3 @animation2 *ngIf="exp3"></div>
`,
animations: [
trigger('animation1', [transition('a => b', [])]),
trigger('animation2', [transition(':leave', [])]),
]
})
class Cmp {
exp1: any = true;
exp2: any = true;
exp3: any = true;
@ViewChild('elm1') public elm1: any;
@ViewChild('elm2') public elm2: any;
@ViewChild('elm3') public elm3: any;
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const engine = TestBed.inject(AnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
fixture.detectChanges();
const elm1 = cmp.elm1;
const elm2 = cmp.elm2;
const elm3 = cmp.elm3;
assertHasParent(elm1);
assertHasParent(elm2);
assertHasParent(elm3);
engine.flush();
finishPlayers(engine.players);
cmp.exp1 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(0);
cmp.exp2 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2, false);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(0);
cmp.exp3 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2, false);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(1);
});
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: MockAnimationEngine}],
imports: [BrowserAnimationsModule]
});
});
describe('AnimationRendererFactory', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{
provide: RendererFactory2,
useClass: ExtendedAnimationRendererFactory,
deps: [DomRendererFactory2, AnimationEngine, NgZone]
}],
imports: [BrowserAnimationsModule]
});
function makeRenderer(animationTriggers: any[] = []) {
const type = <RendererType2>{
id: 'id',
encapsulation: null!,
styles: [],
data: {'animation': animationTriggers}
};
return (TestBed.inject(RendererFactory2) as AnimationRendererFactory)
.createRenderer(element, type);
}
it('should hook into the engine\'s insert operations when appending children', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
renderer.appendChild(container, element);
expect(engine.captures['onInsert'].pop()).toEqual([element]);
});
it('should hook into the engine\'s insert operations when inserting a child before another',
() => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
const element2 = el('<div></div>');
container.appendChild(element2);
renderer.insertBefore(container, element, element2);
expect(engine.captures['onInsert'].pop()).toEqual([element]);
});
it('should hook into the engine\'s insert operations when removing children', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const container = el('<div></div>');
renderer.removeChild(container, element);
expect(engine.captures['onRemove'].pop()).toEqual([element]);
});
it('should hook into the engine\'s setProperty call if the property begins with `@`', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
renderer.setProperty(element, 'prop', 'value');
expect(engine.captures['setProperty']).toBeFalsy();
renderer.setProperty(element, '@prop', 'value');
expect(engine.captures['setProperty'].pop()).toEqual([element, 'prop', 'value']);
});
// https://github.com/angular/angular/issues/32794
it('should support nested animation triggers', () => {
makeRenderer([[trigger('myAnimation', [])]]);
const {triggers} = TestBed.inject(AnimationEngine) as MockAnimationEngine;
expect(triggers.length).toEqual(1);
expect(triggers[0].name).toEqual('myAnimation');
});
describe('listen', () => {
it('should hook into the engine\'s listen call if the property begins with `@`', () => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const cb = (event: any): boolean => {
return true;
};
renderer.listen(element, 'event', cb);
expect(engine.captures['listen']).toBeFalsy();
renderer.listen(element, '@event.phase', cb);
expect(engine.captures['listen'].pop()).toEqual([element, 'event', 'phase']);
});
it('should provide hooks at the start and end of change detection', () => {
it('should resolve the body|document|window nodes given their values as strings as input',
() => {
const renderer = makeRenderer();
const engine = TestBed.inject(AnimationEngine) as MockAnimationEngine;
const cb = (event: any): boolean => {
return true;
};
renderer.listen('body', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(document.body);
renderer.listen('document', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(document);
renderer.listen('window', '@event', cb);
expect(engine.captures['listen'].pop()[0]).toBe(window);
});
});
describe('registering animations', () => {
it('should only create a trigger definition once even if the registered multiple times');
});
describe('flushing animations', () => {
// these tests are only mean't to be run within the DOM
if (isNode) return;
it('should flush and fire callbacks when the zone becomes stable', (async) => {
@Component({
selector: 'my-cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [trigger('myAnimation', [])]
template: '<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)"></div>',
animations: [trigger(
'myAnimation',
[transition(
'* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
})
class Cmp {
public exp: any;
exp: any;
event: any;
onStart(event: any) {
this.event = event;
}
}
TestBed.configureTestingModule({
@ -305,19 +155,174 @@ import {el} from '../../testing/src/browser_util';
declarations: [Cmp]
});
const renderer = TestBed.inject(RendererFactory2) as ExtendedAnimationRendererFactory;
const engine = TestBed.inject(AnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
renderer.log = [];
cmp.exp = 'state';
fixture.detectChanges();
expect(renderer.log).toEqual(['begin', 'end']);
fixture.whenStable().then(() => {
expect(cmp.event.triggerName).toEqual('myAnimation');
expect(cmp.event.phaseName).toEqual('start');
cmp.event = null;
renderer.log = [];
fixture.detectChanges();
expect(renderer.log).toEqual(['begin', 'end']);
engine.flush();
expect(cmp.event).toBeFalsy();
async();
});
});
it('should properly insert/remove nodes through the animation renderer that do not contain animations',
(async) => {
@Component({
selector: 'my-cmp',
template: '<div #elm *ngIf="exp"></div>',
animations: [trigger(
'someAnimation',
[transition(
'* => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
})
class Cmp {
exp: any;
@ViewChild('elm') public element: any;
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
cmp.exp = false;
const element = cmp.element;
expect(element.nativeElement.parentNode).toBeTruthy();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.nativeElement.parentNode).toBeFalsy();
async();
});
});
});
it('should only queue up dom removals if the element itself contains a valid leave animation',
() => {
@Component({
selector: 'my-cmp',
template: `
<div #elm1 *ngIf="exp1"></div>
<div #elm2 @animation1 *ngIf="exp2"></div>
<div #elm3 @animation2 *ngIf="exp3"></div>
`,
animations: [
trigger('animation1', [transition('a => b', [])]),
trigger('animation2', [transition(':leave', [])]),
]
})
class Cmp {
exp1: any = true;
exp2: any = true;
exp3: any = true;
@ViewChild('elm1') public elm1: any;
@ViewChild('elm2') public elm2: any;
@ViewChild('elm3') public elm3: any;
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const engine = TestBed.inject(AnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
fixture.detectChanges();
const elm1 = cmp.elm1;
const elm2 = cmp.elm2;
const elm3 = cmp.elm3;
assertHasParent(elm1);
assertHasParent(elm2);
assertHasParent(elm3);
engine.flush();
finishPlayers(engine.players);
cmp.exp1 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(0);
cmp.exp2 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2, false);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(0);
cmp.exp3 = false;
fixture.detectChanges();
assertHasParent(elm1, false);
assertHasParent(elm2, false);
assertHasParent(elm3);
engine.flush();
expect(engine.players.length).toEqual(1);
});
});
});
describe('AnimationRendererFactory', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{
provide: RendererFactory2,
useClass: ExtendedAnimationRendererFactory,
deps: [DomRendererFactory2, AnimationEngine, NgZone]
}],
imports: [BrowserAnimationsModule]
});
});
it('should provide hooks at the start and end of change detection', () => {
@Component({
selector: 'my-cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [trigger('myAnimation', [])]
})
class Cmp {
public exp: any;
}
TestBed.configureTestingModule({
providers: [{provide: AnimationEngine, useClass: InjectableAnimationEngine}],
declarations: [Cmp]
});
const renderer = TestBed.inject(RendererFactory2) as ExtendedAnimationRendererFactory;
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
renderer.log = [];
fixture.detectChanges();
expect(renderer.log).toEqual(['begin', 'end']);
renderer.log = [];
fixture.detectChanges();
expect(renderer.log).toEqual(['begin', 'end']);
});
});
})();
@Injectable()
@ -336,7 +341,9 @@ class MockAnimationEngine extends InjectableAnimationEngine {
this.triggers.push(metadata);
}
onInsert(namespaceId: string, element: any): void { this._capture('onInsert', [element]); }
onInsert(namespaceId: string, element: any): void {
this._capture('onInsert', [element]);
}
onRemove(namespaceId: string, element: any, domFn: () => any): void {
this._capture('onRemove', [element]);

View File

@ -5,11 +5,11 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationBuilder, animate, style} from '@angular/animations';
import {animate, AnimationBuilder, style} from '@angular/animations';
import {AnimationDriver} from '@angular/animations/browser';
import {MockAnimationDriver} from '@angular/animations/browser/testing';
import {Component, ViewChild} from '@angular/core';
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
import {NoopAnimationsModule, ɵBrowserAnimationBuilder as BrowserAnimationBuilder} from '@angular/platform-browser/animations';
import {el} from '../../testing/src/browser_util';

View File

@ -13,7 +13,9 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
{
describe('NoopAnimationsModule', () => {
beforeEach(() => { TestBed.configureTestingModule({imports: [NoopAnimationsModule]}); });
beforeEach(() => {
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
});
it('should flush and fire callbacks when the zone becomes stable', (async) => {
@Component({
@ -29,8 +31,12 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
exp: any;
startEvent: any;
doneEvent: any;
onStart(event: any) { this.startEvent = event; }
onDone(event: any) { this.doneEvent = event; }
onStart(event: any) {
this.startEvent = event;
}
onDone(event: any) {
this.doneEvent = event;
}
}
TestBed.configureTestingModule({declarations: [Cmp]});
@ -63,8 +69,12 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
exp: any;
startEvent: any;
doneEvent: any;
onStart(event: any) { this.startEvent = event; }
onDone(event: any) { this.doneEvent = event; }
onStart(event: any) {
this.startEvent = event;
}
onDone(event: any) {
this.doneEvent = event;
}
}
TestBed.configureTestingModule({declarations: [Cmp]});

View File

@ -7,7 +7,8 @@
*/
import {CommonModule, DOCUMENT, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common';
import {APP_ID, ApplicationModule, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, createPlatformFactory, platformCore, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵsetDocument} from '@angular/core';
import {APP_ID, ApplicationModule, createPlatformFactory, ErrorHandler, Inject, ModuleWithProviders, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, platformCore, PlatformRef, RendererFactory2, Sanitizer, SkipSelf, StaticProvider, Testability, ɵConsole as Console, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵsetDocument} from '@angular/core';
import {BrowserDomAdapter} from './browser/browser_adapter';
import {SERVER_TRANSITION_PROVIDERS, TRANSITION_ID} from './browser/server-transition';
import {BrowserGetTestability} from './browser/testability';

View File

@ -29,8 +29,12 @@ const nodeContains: (this: Node, other: Node) => boolean = (() => {
*/
/* tslint:disable:requireParameterType no-console */
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); }
getProperty(el: Node, name: string): any { return (<any>el)[name]; }
static makeCurrent() {
setRootDomAdapter(new BrowserDomAdapter());
}
getProperty(el: Node, name: string): any {
return (<any>el)[name];
}
log(error: string): void {
if (window.console) {
@ -54,16 +58,22 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
el.addEventListener(evt, listener, false);
// Needed to follow Dart's subscription semantic, until fix of
// https://code.google.com/p/dart/issues/detail?id=17406
return () => { el.removeEventListener(evt, listener, false); };
return () => {
el.removeEventListener(evt, listener, false);
};
}
dispatchEvent(el: Node, evt: any) {
el.dispatchEvent(evt);
}
dispatchEvent(el: Node, evt: any) { el.dispatchEvent(evt); }
remove(node: Node): Node {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
return node;
}
getValue(el: any): string { return el.value; }
getValue(el: any): string {
return el.value;
}
createElement(tagName: string, doc?: Document): HTMLElement {
doc = doc || this.getDefaultDocument();
return doc.createElement(tagName);
@ -71,11 +81,17 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
createHtmlDocument(): HTMLDocument {
return document.implementation.createHTMLDocument('fakeTitle');
}
getDefaultDocument(): Document { return document; }
getDefaultDocument(): Document {
return document;
}
isElementNode(node: Node): boolean { return node.nodeType === Node.ELEMENT_NODE; }
isElementNode(node: Node): boolean {
return node.nodeType === Node.ELEMENT_NODE;
}
isShadowRoot(node: any): boolean { return node instanceof DocumentFragment; }
isShadowRoot(node: any): boolean {
return node instanceof DocumentFragment;
}
getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
if (target === 'window') {
@ -89,14 +105,22 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
}
return null;
}
getHistory(): History { return window.history; }
getLocation(): Location { return window.location; }
getHistory(): History {
return window.history;
}
getLocation(): Location {
return window.location;
}
getBaseHref(doc: Document): string|null {
const href = getBaseElementHref();
return href == null ? null : relativePath(href);
}
resetBaseElement(): void { baseElement = null; }
getUserAgent(): string { return window.navigator.userAgent; }
resetBaseElement(): void {
baseElement = null;
}
getUserAgent(): string {
return window.navigator.userAgent;
}
performanceNow(): number {
// performance.now() is not available in all browsers, see
// http://caniuse.com/#search=performance.now
@ -104,15 +128,19 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
new Date().getTime();
}
supportsCookies(): boolean { return true; }
supportsCookies(): boolean {
return true;
}
getCookie(name: string): string|null { return parseCookieValue(document.cookie, name); }
getCookie(name: string): string|null {
return parseCookieValue(document.cookie, name);
}
}
let baseElement: HTMLElement|null = null;
function getBaseElementHref(): string|null {
if (!baseElement) {
baseElement = document.querySelector('base') !;
baseElement = document.querySelector('base')!;
if (!baseElement) {
return null;
}

View File

@ -17,7 +17,11 @@ import {ɵDomAdapter as DomAdapter} from '@angular/common';
* can introduce XSS risks.
*/
export abstract class GenericBrowserDomAdapter extends DomAdapter {
constructor() { super(); }
constructor() {
super();
}
supportsDOMEvents(): boolean { return true; }
supportsDOMEvents(): boolean {
return true;
}
}

View File

@ -15,13 +15,16 @@ import {Inject, Injectable, ɵɵinject} from '@angular/core';
* @publicApi
*/
export type MetaDefinition = {
charset?: string; content?: string; httpEquiv?: string; id?: string; itemprop?: string;
charset?: string;
content?: string;
httpEquiv?: string;
id?: string;
itemprop?: string;
name?: string;
property?: string;
scheme?: string;
url?: string;
} &
{
}&{
// TODO(IgorMinar): this type looks wrong
[prop: string]: string;
};
@ -41,7 +44,9 @@ export function createMeta() {
@Injectable({providedIn: 'root', useFactory: createMeta, deps: []})
export class Meta {
private _dom: DomAdapter;
constructor(@Inject(DOCUMENT) private _doc: any) { this._dom = getDOM(); }
constructor(@Inject(DOCUMENT) private _doc: any) {
this._dom = getDOM();
}
addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement|null {
if (!tag) return null;
@ -72,14 +77,16 @@ export class Meta {
updateTag(tag: MetaDefinition, selector?: string): HTMLMetaElement|null {
if (!tag) return null;
selector = selector || this._parseSelector(tag);
const meta: HTMLMetaElement = this.getTag(selector) !;
const meta: HTMLMetaElement = this.getTag(selector)!;
if (meta) {
return this._setMetaElementAttributes(tag, meta);
}
return this._getOrCreateElement(tag, true);
}
removeTag(attrSelector: string): void { this.removeTagElement(this.getTag(attrSelector) !); }
removeTag(attrSelector: string): void {
this.removeTagElement(this.getTag(attrSelector)!);
}
removeTagElement(meta: HTMLMetaElement): void {
if (meta) {
@ -91,7 +98,7 @@ export class Meta {
HTMLMetaElement {
if (!forceCreation) {
const selector: string = this._parseSelector(meta);
const elem: HTMLMetaElement = this.getTag(selector) !;
const elem: HTMLMetaElement = this.getTag(selector)!;
// It's allowed to have multiple elements with the same name so it's not enough to
// just check that element with the same name already present on the page. We also need to
// check if element has tag attributes

View File

@ -7,10 +7,12 @@
*/
import {ɵgetDOM as getDOM} from '@angular/common';
import {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter, ɵglobal as global} from '@angular/core';
import {GetTestability, setTestabilityGetter, Testability, TestabilityRegistry, ɵglobal as global} from '@angular/core';
export class BrowserGetTestability implements GetTestability {
static init() { setTestabilityGetter(new BrowserGetTestability()); }
static init() {
setTestabilityGetter(new BrowserGetTestability());
}
addToWindow(registry: TestabilityRegistry): void {
global['getAngularTestability'] = (elem: any, findInAncestors: boolean = true) => {

View File

@ -33,11 +33,15 @@ export class Title {
/**
* Get the title of the current HTML document.
*/
getTitle(): string { return this._doc.title; }
getTitle(): string {
return this._doc.title;
}
/**
* Set the title of the current HTML document.
* @param newTitle
*/
setTitle(newTitle: string) { this._doc.title = newTitle || ''; }
setTitle(newTitle: string) {
this._doc.title = newTitle || '';
}
}

View File

@ -21,7 +21,9 @@ export class ChangeDetectionPerfRecord {
export class AngularProfiler {
appRef: ApplicationRef;
constructor(ref: ComponentRef<any>) { this.appRef = ref.injector.get(ApplicationRef); }
constructor(ref: ComponentRef<any>) {
this.appRef = ref.injector.get(ApplicationRef);
}
// tslint:disable:no-console
/**

View File

@ -45,7 +45,7 @@ export function unescapeHtml(text: string): string {
*
* @publicApi
*/
export type StateKey<T> = string & {__not_a_string: never};
export type StateKey<T> = string&{__not_a_string: never};
/**
* Create a `StateKey<T>` that can be used to store value of type T with `TransferState`.
@ -80,7 +80,7 @@ export function makeStateKey<T = void>(key: string): StateKey<T> {
*/
@Injectable()
export class TransferState {
private store: {[k: string]: {} | undefined} = {};
private store: {[k: string]: {}|undefined} = {};
private onSerializeCallbacks: {[k: string]: () => {} | undefined} = {};
/** @internal */
@ -100,17 +100,23 @@ export class TransferState {
/**
* Set the value corresponding to a key.
*/
set<T>(key: StateKey<T>, value: T): void { this.store[key] = value; }
set<T>(key: StateKey<T>, value: T): void {
this.store[key] = value;
}
/**
* Remove a key from the store.
*/
remove<T>(key: StateKey<T>): void { delete this.store[key]; }
remove<T>(key: StateKey<T>): void {
delete this.store[key];
}
/**
* Test whether a key exists in the store.
*/
hasKey<T>(key: StateKey<T>) { return this.store.hasOwnProperty(key); }
hasKey<T>(key: StateKey<T>) {
return this.store.hasOwnProperty(key);
}
/**
* Register a callback to provide the value for a key when `toJson` is called.

View File

@ -25,7 +25,9 @@ export class By {
*
* {@example platform-browser/dom/debug/ts/by/by.ts region='by_all'}
*/
static all(): Predicate<DebugNode> { return () => true; }
static all(): Predicate<DebugNode> {
return () => true;
}
/**
* Match elements by the given CSS selector.
@ -52,7 +54,7 @@ export class By {
* {@example platform-browser/dom/debug/ts/by/by.ts region='by_directive'}
*/
static directive(type: Type<any>): Predicate<DebugNode> {
return (debugNode) => debugNode.providerTokens !.indexOf(type) !== -1;
return (debugNode) => debugNode.providerTokens!.indexOf(type) !== -1;
}
}

View File

@ -137,11 +137,17 @@ class DefaultDomRenderer2 implements Renderer2 {
return document.createElement(name);
}
createComment(value: string): any { return document.createComment(value); }
createComment(value: string): any {
return document.createComment(value);
}
createText(value: string): any { return document.createTextNode(value); }
createText(value: string): any {
return document.createTextNode(value);
}
appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); }
appendChild(parent: any, newChild: any): void {
parent.appendChild(newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
if (parent) {
@ -167,9 +173,13 @@ class DefaultDomRenderer2 implements Renderer2 {
return el;
}
parentNode(node: any): any { return node.parentNode; }
parentNode(node: any): any {
return node.parentNode;
}
nextSibling(node: any): any { return node.nextSibling; }
nextSibling(node: any): any {
return node.nextSibling;
}
setAttribute(el: any, name: string, value: string, namespace?: string): void {
if (namespace) {
@ -205,9 +215,13 @@ class DefaultDomRenderer2 implements Renderer2 {
}
}
addClass(el: any, name: string): void { el.classList.add(name); }
addClass(el: any, name: string): void {
el.classList.add(name);
}
removeClass(el: any, name: string): void { el.classList.remove(name); }
removeClass(el: any, name: string): void {
el.classList.remove(name);
}
setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void {
if (flags & RendererStyleFlags2.DashCase) {
@ -233,7 +247,9 @@ class DefaultDomRenderer2 implements Renderer2 {
el[name] = value;
}
setValue(node: any, value: string): void { node.nodeValue = value; }
setValue(node: any, value: string): void {
node.nodeValue = value;
}
listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean):
() => void {
@ -243,15 +259,15 @@ class DefaultDomRenderer2 implements Renderer2 {
target, event, decoratePreventDefault(callback));
}
return <() => void>this.eventManager.addEventListener(
target, event, decoratePreventDefault(callback)) as() => void;
target, event, decoratePreventDefault(callback)) as () => void;
}
}
const AT_CHARCODE = (() => '@'.charCodeAt(0))();
function checkNoSyntheticProp(name: string, nameKind: string) {
if (name.charCodeAt(0) === AT_CHARCODE) {
throw new Error(
`Found the synthetic ${nameKind} ${name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`);
throw new Error(`Found the synthetic ${nameKind} ${
name}. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.`);
}
}
@ -270,7 +286,9 @@ class EmulatedEncapsulationDomRenderer2 extends DefaultDomRenderer2 {
this.hostAttr = shimHostAttribute(appId + '-' + component.id);
}
applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); }
applyToHost(element: any) {
super.setAttribute(element, this.hostAttr, '');
}
createElement(parent: any, name: string): Element {
const el = super.createElement(parent, name);
@ -300,9 +318,13 @@ class ShadowDomRenderer extends DefaultDomRenderer2 {
}
}
private nodeOrShadowRoot(node: any): any { return node === this.hostEl ? this.shadowRoot : node; }
private nodeOrShadowRoot(node: any): any {
return node === this.hostEl ? this.shadowRoot : node;
}
destroy() { this.sharedStylesHost.removeHost(this.shadowRoot); }
destroy() {
this.sharedStylesHost.removeHost(this.shadowRoot);
}
appendChild(parent: any, newChild: any): void {
return super.appendChild(this.nodeOrShadowRoot(parent), newChild);

View File

@ -13,11 +13,15 @@ import {EventManagerPlugin} from './event_manager';
@Injectable()
export class DomEventsPlugin extends EventManagerPlugin {
constructor(@Inject(DOCUMENT) doc: any) { super(doc); }
constructor(@Inject(DOCUMENT) doc: any) {
super(doc);
}
// This plugin should come last in the list of plugins, because it accepts all
// events.
supports(eventName: string): boolean { return true; }
supports(eventName: string): boolean {
return true;
}
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
element.addEventListener(eventName, handler as EventListener, false);

View File

@ -67,7 +67,9 @@ export class EventManager {
/**
* Retrieves the compilation zone in which event listeners are registered.
*/
getZone(): NgZone { return this._zone; }
getZone(): NgZone {
return this._zone;
}
/** @internal */
_findPluginFor(eventName: string): EventManagerPlugin {
@ -92,7 +94,7 @@ export abstract class EventManagerPlugin {
constructor(private _doc: any) {}
// TODO(issue/24571): remove '!'.
manager !: EventManager;
manager!: EventManager;
abstract supports(eventName: string): boolean;

View File

@ -99,21 +99,21 @@ export class HammerGestureConfig {
events: string[] = [];
/**
* Maps gesture event names to a set of configuration options
* that specify overrides to the default values for specific properties.
*
* The key is a supported event name to be configured,
* and the options object contains a set of properties, with override values
* to be applied to the named recognizer event.
* For example, to disable recognition of the rotate event, specify
* `{"rotate": {"enable": false}}`.
*
* Properties that are not present take the HammerJS default values.
* For information about which properties are supported for which events,
* and their allowed and default values, see
* [HammerJS documentation](http://hammerjs.github.io/).
*
*/
* Maps gesture event names to a set of configuration options
* that specify overrides to the default values for specific properties.
*
* The key is a supported event name to be configured,
* and the options object contains a set of properties, with override values
* to be applied to the named recognizer event.
* For example, to disable recognition of the rotate event, specify
* `{"rotate": {"enable": false}}`.
*
* Properties that are not present take the HammerJS default values.
* For information about which properties are supported for which events,
* and their allowed and default values, see
* [HammerJS documentation](http://hammerjs.github.io/).
*
*/
overrides: {[key: string]: Object} = {};
/**
@ -124,7 +124,9 @@ export class HammerGestureConfig {
* [HammerJS documentation](http://hammerjs.github.io/).
*/
options?: {
cssProps?: any; domEvents?: boolean; enable?: boolean | ((manager: any) => boolean);
cssProps?: any;
domEvents?: boolean;
enable?: boolean | ((manager: any) => boolean);
preset?: any[];
touchAction?: string;
recognizers?: any[];
@ -139,7 +141,7 @@ export class HammerGestureConfig {
* @returns A HammerJS event-manager object.
*/
buildHammer(element: HTMLElement): HammerInstance {
const mc = new Hammer !(element, this.options);
const mc = new Hammer!(element, this.options);
mc.get('pinch').set({enable: true});
mc.get('rotate').set({enable: true});
@ -192,7 +194,9 @@ export class HammerGesturesPlugin extends EventManagerPlugin {
// Until Hammer is loaded, the returned function needs to *cancel* the registration rather
// than remove anything.
let cancelRegistration = false;
let deregister: Function = () => { cancelRegistration = true; };
let deregister: Function = () => {
cancelRegistration = true;
};
this.loader()
.then(() => {
@ -220,14 +224,18 @@ export class HammerGesturesPlugin extends EventManagerPlugin {
// Return a function that *executes* `deregister` (and not `deregister` itself) so that we
// can change the behavior of `deregister` once the listener is added. Using a closure in
// this way allows us to avoid any additional data structures to track listener removal.
return () => { deregister(); };
return () => {
deregister();
};
}
return zone.runOutsideAngular(() => {
// Creating the manager bind events, must be done outside of angular
const mc = this._config.buildHammer(element);
const callback = function(eventObj: HammerInput) {
zone.runGuarded(function() { handler(eventObj); });
zone.runGuarded(function() {
handler(eventObj);
});
};
mc.on(eventName, callback);
return () => {
@ -240,7 +248,9 @@ export class HammerGesturesPlugin extends EventManagerPlugin {
});
}
isCustomEvent(eventName: string): boolean { return this._config.events.indexOf(eventName) > -1; }
isCustomEvent(eventName: string): boolean {
return this._config.events.indexOf(eventName) > -1;
}
}
/**

View File

@ -79,14 +79,18 @@ export class KeyEventsPlugin extends EventManagerPlugin {
* Initializes an instance of the browser plug-in.
* @param doc The document in which key events will be detected.
*/
constructor(@Inject(DOCUMENT) doc: any) { super(doc); }
constructor(@Inject(DOCUMENT) doc: any) {
super(doc);
}
/**
* Reports whether a named key event is supported.
* @param eventName The event name to query.
* @return True if the named key event is supported.
* Reports whether a named key event is supported.
* @param eventName The event name to query.
* @return True if the named key event is supported.
*/
supports(eventName: string): boolean { return KeyEventsPlugin.parseEventName(eventName) != null; }
supports(eventName: string): boolean {
return KeyEventsPlugin.parseEventName(eventName) != null;
}
/**
* Registers a handler for a specific element and key event.
@ -95,9 +99,9 @@ export class KeyEventsPlugin extends EventManagerPlugin {
* @param handler A function to call when the notification occurs. Receives the
* event object as an argument.
* @returns The key event that was registered.
*/
*/
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
const parsedEvent = KeyEventsPlugin.parseEventName(eventName) !;
const parsedEvent = KeyEventsPlugin.parseEventName(eventName)!;
const outsideHandler =
KeyEventsPlugin.eventCallback(parsedEvent['fullKey'], handler, this.manager.getZone());
@ -115,7 +119,7 @@ export class KeyEventsPlugin extends EventManagerPlugin {
return null;
}
const key = KeyEventsPlugin._normalizeKey(parts.pop() !);
const key = KeyEventsPlugin._normalizeKey(parts.pop()!);
let fullKey = '';
MODIFIER_KEYS.forEach(modifierName => {

View File

@ -27,7 +27,9 @@ export class SharedStylesHost {
onStylesAdded(additions: Set<string>): void {}
getAllStyles(): string[] { return Array.from(this._stylesSet); }
getAllStyles(): string[] {
return Array.from(this._stylesSet);
}
}
@Injectable()
@ -52,11 +54,15 @@ export class DomSharedStylesHost extends SharedStylesHost implements OnDestroy {
this._hostNodes.add(hostNode);
}
removeHost(hostNode: Node): void { this._hostNodes.delete(hostNode); }
removeHost(hostNode: Node): void {
this._hostNodes.delete(hostNode);
}
onStylesAdded(additions: Set<string>): void {
this._hostNodes.forEach(hostNode => this._addStylesToHost(additions, hostNode));
}
ngOnDestroy(): void { this._styleNodes.forEach(styleNode => getDOM().remove(styleNode)); }
ngOnDestroy(): void {
this._styleNodes.forEach(styleNode => getDOM().remove(styleNode));
}
}

View File

@ -33,7 +33,7 @@ export function exportNgVar(name: string, value: any): void {
// - closure declares globals itself for minified names, which sometimes clobber our `ng` global
// - we can't declare a closure extern as the namespace `ng` is already used within Google
// for typings for angularJS (via `goog.provide('ng....')`).
const ng = global['ng'] = (global['ng'] as{[key: string]: any} | undefined) || {};
const ng = global['ng'] = (global['ng'] as {[key: string]: any} | undefined) || {};
ng[name] = value;
}
}

View File

@ -10,7 +10,7 @@ export {BrowserModule, platformBrowser} from './browser';
export {Meta, MetaDefinition} from './browser/meta';
export {Title} from './browser/title';
export {disableDebugTools, enableDebugTools} from './browser/tools/tools';
export {BrowserTransferStateModule, StateKey, TransferState, makeStateKey} from './browser/transfer_state';
export {BrowserTransferStateModule, makeStateKey, StateKey, TransferState} from './browser/transfer_state';
export {By} from './dom/debug/by';
export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
export {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HAMMER_PROVIDERS__POST_R3__ as ɵHAMMER_PROVIDERS__POST_R3__, HammerGestureConfig, HammerLoader, HammerModule} from './dom/events/hammer_gestures';

View File

@ -7,13 +7,13 @@
*/
export {ɵgetDOM} from '@angular/common';
export {BROWSER_SANITIZATION_PROVIDERS as ɵBROWSER_SANITIZATION_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS__POST_R3__ as ɵBROWSER_SANITIZATION_PROVIDERS__POST_R3__, INTERNAL_BROWSER_PLATFORM_PROVIDERS as ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS, initDomAdapter as ɵinitDomAdapter} from './browser';
export {BROWSER_SANITIZATION_PROVIDERS as ɵBROWSER_SANITIZATION_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS__POST_R3__ as ɵBROWSER_SANITIZATION_PROVIDERS__POST_R3__, initDomAdapter as ɵinitDomAdapter, INTERNAL_BROWSER_PLATFORM_PROVIDERS as ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS} from './browser';
export {BrowserDomAdapter as ɵBrowserDomAdapter} from './browser/browser_adapter';
export {TRANSITION_ID as ɵTRANSITION_ID} from './browser/server-transition';
export {BrowserGetTestability as ɵBrowserGetTestability} from './browser/testability';
export {escapeHtml as ɵescapeHtml} from './browser/transfer_state';
export {ELEMENT_PROBE_PROVIDERS as ɵELEMENT_PROBE_PROVIDERS} from './dom/debug/ng_probe';
export {DomRendererFactory2 as ɵDomRendererFactory2, NAMESPACE_URIS as ɵNAMESPACE_URIS, flattenStyles as ɵflattenStyles, shimContentAttribute as ɵshimContentAttribute, shimHostAttribute as ɵshimHostAttribute} from './dom/dom_renderer';
export {DomRendererFactory2 as ɵDomRendererFactory2, flattenStyles as ɵflattenStyles, NAMESPACE_URIS as ɵNAMESPACE_URIS, shimContentAttribute as ɵshimContentAttribute, shimHostAttribute as ɵshimHostAttribute} from './dom/dom_renderer';
export {DomEventsPlugin as ɵDomEventsPlugin} from './dom/events/dom_events';
export {HammerGesturesPlugin as ɵHammerGesturesPlugin} from './dom/events/hammer_gestures';
export {KeyEventsPlugin as ɵKeyEventsPlugin} from './dom/events/key_events';

View File

@ -7,7 +7,7 @@
*/
import {DOCUMENT} from '@angular/common';
import {Inject, Injectable, Injector, Sanitizer, SecurityContext, forwardRef, ɵBypassType as BypassType, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core';
import {forwardRef, Inject, Injectable, Injector, Sanitizer, SecurityContext, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵBypassType as BypassType, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core';
export {SecurityContext};
@ -149,7 +149,9 @@ export function domSanitizerImplFactory(injector: Injector) {
@Injectable({providedIn: 'root', useFactory: domSanitizerImplFactory, deps: [Injector]})
export class DomSanitizerImpl extends DomSanitizer {
constructor(@Inject(DOCUMENT) private _doc: any) { super(); }
constructor(@Inject(DOCUMENT) private _doc: any) {
super();
}
sanitize(ctx: SecurityContext, value: SafeValue|string|null): string|null {
if (value == null) return null;
@ -188,12 +190,18 @@ export class DomSanitizerImpl extends DomSanitizer {
}
}
bypassSecurityTrustHtml(value: string): SafeHtml { return bypassSanitizationTrustHtml(value); }
bypassSecurityTrustStyle(value: string): SafeStyle { return bypassSanitizationTrustStyle(value); }
bypassSecurityTrustHtml(value: string): SafeHtml {
return bypassSanitizationTrustHtml(value);
}
bypassSecurityTrustStyle(value: string): SafeStyle {
return bypassSanitizationTrustStyle(value);
}
bypassSecurityTrustScript(value: string): SafeScript {
return bypassSanitizationTrustScript(value);
}
bypassSecurityTrustUrl(value: string): SafeUrl { return bypassSanitizationTrustUrl(value); }
bypassSecurityTrustUrl(value: string): SafeUrl {
return bypassSanitizationTrustUrl(value);
}
bypassSecurityTrustResourceUrl(value: string): SafeResourceUrl {
return bypassSanitizationTrustResourceUrl(value);
}

View File

@ -7,12 +7,12 @@
*/
import {DOCUMENT, isPlatformBrowser, ɵgetDOM as getDOM} from '@angular/common';
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, Provider, Sanitizer, StaticProvider, Type, VERSION, createPlatformFactory} from '@angular/core';
import {APP_INITIALIZER, Compiler, Component, createPlatformFactory, CUSTOM_ELEMENTS_SCHEMA, Directive, ErrorHandler, Inject, Injector, Input, LOCALE_ID, NgModule, OnDestroy, Pipe, PLATFORM_ID, PLATFORM_INITIALIZER, Provider, Sanitizer, StaticProvider, Type, VERSION} from '@angular/core';
import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref';
import {Console} from '@angular/core/src/console';
import {ComponentRef} from '@angular/core/src/linker/component_factory';
import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability';
import {AsyncTestCompleter, Log, afterEach, beforeEach, beforeEachProviders, describe, inject, it} from '@angular/core/testing/src/testing_internal';
import {afterEach, AsyncTestCompleter, beforeEach, beforeEachProviders, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -25,7 +25,9 @@ class NonExistentComp {
@Component({selector: 'hello-app', template: '{{greeting}} world!'})
class HelloRootCmp {
greeting: string;
constructor() { this.greeting = 'hello'; }
constructor() {
this.greeting = 'hello';
}
}
@Component({selector: 'hello-app', template: 'before: <ng-content></ng-content> after: done'})
@ -36,7 +38,9 @@ class HelloRootCmpContent {
@Component({selector: 'hello-app-2', template: '{{greeting}} world, again!'})
class HelloRootCmp2 {
greeting: string;
constructor() { this.greeting = 'hello'; }
constructor() {
this.greeting = 'hello';
}
}
@Component({selector: 'hello-app', template: ''})
@ -52,7 +56,9 @@ class HelloRootCmp3 {
class HelloRootCmp4 {
appRef: any /** TODO #9100 */;
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; }
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) {
this.appRef = appRef;
}
}
@Component({selector: 'hello-app'})
@ -66,9 +72,13 @@ class HelloRootDirectiveIsNotCmp {
@Component({selector: 'hello-app', template: ''})
class HelloOnDestroyTickCmp implements OnDestroy {
appRef: ApplicationRef;
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) { this.appRef = appRef; }
constructor(@Inject(ApplicationRef) appRef: ApplicationRef) {
this.appRef = appRef;
}
ngOnDestroy(): void { this.appRef.tick(); }
ngOnDestroy(): void {
this.appRef.tick();
}
}
@Component({selector: 'hello-app', templateUrl: './sometemplate.html'})
@ -79,13 +89,14 @@ class HelloUrlCmp {
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
class SomeDirective {
// TODO(issue/24571): remove '!'.
@Input()
someDir !: string;
@Input() someDir!: string;
}
@Pipe({name: 'somePipe'})
class SomePipe {
transform(value: string): any { return `transformed ${value}`; }
transform(value: string): any {
return `transformed ${value}`;
}
}
@Component({selector: 'hello-app', template: `<div [someDir]="'someValue' | somePipe"></div>`})
@ -99,7 +110,9 @@ class HelloCmpUsingCustomElement {
class MockConsole {
res: any[][] = [];
error(...s: any[]): void { this.res.push(s); }
error(...s: any[]): void {
this.res.push(s);
}
}
@ -107,7 +120,9 @@ class DummyConsole implements Console {
public warnings: string[] = [];
log(message: string) {}
warn(message: string) { this.warnings.push(message); }
warn(message: string) {
this.warnings.push(message);
}
}
@ -135,7 +150,9 @@ function bootstrap(
if (isNode) return;
let compilerConsole: DummyConsole;
beforeEachProviders(() => { return [Log]; });
beforeEachProviders(() => {
return [Log];
});
beforeEach(inject([DOCUMENT], (doc: any) => {
destroyPlatform();
@ -419,7 +436,6 @@ function bootstrap(
it('should remove styles when transitioning from a server render',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
@Component({
selector: 'root',
template: 'root',
@ -449,8 +465,9 @@ function bootstrap(
platform.bootstrapModule(TestModule).then(() => {
const styles: HTMLElement[] =
Array.prototype.slice.apply(document.getElementsByTagName('style') || []);
styles.forEach(
style => { expect(style.getAttribute('ng-transition')).not.toBe('my-app'); });
styles.forEach(style => {
expect(style.getAttribute('ng-transition')).not.toBe('my-app');
});
async.done();
});
}));
@ -487,7 +504,9 @@ function bootstrap(
})
class CompA {
title: string = '';
ngDoCheck() { log.push('CompA:ngDoCheck'); }
ngDoCheck() {
log.push('CompA:ngDoCheck');
}
onClick() {
this.title = 'CompA';
log.push('CompA:onClick');
@ -500,7 +519,9 @@ function bootstrap(
})
class CompB {
title: string = '';
ngDoCheck() { log.push('CompB:ngDoCheck'); }
ngDoCheck() {
log.push('CompB:ngDoCheck');
}
onClick() {
this.title = 'CompB';
log.push('CompB:onClick');

View File

@ -30,14 +30,14 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
afterEach(() => getDOM().remove(defaultMeta));
it('should return meta tag matching selector', () => {
const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"') !;
const actual: HTMLMetaElement = metaService.getTag('property="fb:app_id"')!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('123456789');
});
it('should return all meta tags matching selector', () => {
const tag1 = metaService.addTag({name: 'author', content: 'page author'}) !;
const tag2 = metaService.addTag({name: 'author', content: 'another page author'}) !;
const tag1 = metaService.addTag({name: 'author', content: 'page author'})!;
const tag2 = metaService.addTag({name: 'author', content: 'another page author'})!;
const actual: HTMLMetaElement[] = metaService.getTags('name=author');
expect(actual.length).toEqual(2);
@ -50,7 +50,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
it('should return null if meta tag does not exist', () => {
const actual: HTMLMetaElement = metaService.getTag('fake=fake') !;
const actual: HTMLMetaElement = metaService.getTag('fake=fake')!;
expect(actual).toBeNull();
});
@ -73,7 +73,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.addTags([{name: 'keywords', content: 'meta test'}]);
const meta = metaService.getTag(selector) !;
const meta = metaService.getTag(selector)!;
expect(meta).not.toBeNull();
metaService.removeTagElement(meta);
@ -87,7 +87,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const actual = metaService.getTag(selector);
expect(actual).not.toBeNull();
expect(actual !.getAttribute('content')).toEqual('4321');
expect(actual!.getAttribute('content')).toEqual('4321');
});
it('should extract selector from the tag definition', () => {
@ -96,7 +96,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const actual = metaService.getTag(selector);
expect(actual).not.toBeNull();
expect(actual !.getAttribute('content')).toEqual('666');
expect(actual!.getAttribute('content')).toEqual('666');
});
it('should create meta tag if it does not exist', () => {
@ -104,7 +104,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.updateTag({name: 'twitter:title', content: 'Content Title'}, selector);
const actual = metaService.getTag(selector) !;
const actual = metaService.getTag(selector)!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('Content Title');
@ -118,7 +118,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
metaService.addTag({name: 'og:title', content: 'Content Title'});
const actual = metaService.getTag(selector) !;
const actual = metaService.getTag(selector)!;
expect(actual).not.toBeNull();
expect(actual.getAttribute('content')).toEqual('Content Title');
@ -136,8 +136,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
{name: 'twitter:title', content: 'Content Title'},
{property: 'og:title', content: 'Content Title'}
]);
const twitterMeta = metaService.getTag(nameSelector) !;
const fbMeta = metaService.getTag(propertySelector) !;
const twitterMeta = metaService.getTag(nameSelector)!;
const fbMeta = metaService.getTag(propertySelector)!;
expect(twitterMeta).not.toBeNull();
expect(fbMeta).not.toBeNull();
@ -160,7 +160,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const selector = 'property="fb:app_id"';
expect(metaService.getTags(selector).length).toEqual(1);
const meta = metaService.addTag({property: 'fb:app_id', content: '666'}) !;
const meta = metaService.addTag({property: 'fb:app_id', content: '666'})!;
expect(metaService.getTags(selector).length).toEqual(2);
@ -172,18 +172,16 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const selector = 'property="fb:app_id"';
expect(metaService.getTags(selector).length).toEqual(1);
const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true) !;
const meta = metaService.addTag({property: 'fb:app_id', content: '123456789'}, true)!;
expect(metaService.getTags(selector).length).toEqual(2);
// clean up
metaService.removeTagElement(meta);
});
});
describe('integration test', () => {
@Injectable()
class DependsOnMeta {
constructor(public meta: Meta) {}

View File

@ -24,10 +24,13 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
titleService = new Title(doc);
});
afterEach(() => { doc.title = initialTitle; });
afterEach(() => {
doc.title = initialTitle;
});
it('should allow reading initial title',
() => { expect(titleService.getTitle()).toEqual(initialTitle); });
it('should allow reading initial title', () => {
expect(titleService.getTitle()).toEqual(initialTitle);
});
it('should set a title on the injected document', () => {
titleService.setTitle('test title');
@ -36,13 +39,12 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
it('should reset title to empty string if title not provided', () => {
titleService.setTitle(null !);
titleService.setTitle(null!);
expect(doc.title).toEqual('');
});
});
describe('integration test', () => {
@Injectable()
class DependsOnTitle {
constructor(public title: Title) {}
@ -55,7 +57,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
});
});
it('should inject Title service when using BrowserModule',
() => { expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title); });
it('should inject Title service when using BrowserModule', () => {
expect(TestBed.inject(DependsOnTitle).title).toBeAnInstanceOf(Title);
});
});
}

View File

@ -11,7 +11,9 @@ import {ApplicationRef} from '@angular/core/src/application_ref';
import {SpyObject} from '@angular/core/testing/src/testing_internal';
export class SpyApplicationRef extends SpyObject {
constructor() { super(ApplicationRef); }
constructor() {
super(ApplicationRef);
}
}
export class SpyComponentRef extends SpyObject {

View File

@ -8,18 +8,25 @@
import {disableDebugTools, enableDebugTools} from '@angular/platform-browser';
import {SpyComponentRef, callNgProfilerTimeChangeDetection} from './spies';
import {callNgProfilerTimeChangeDetection, SpyComponentRef} from './spies';
{
describe('profiler', () => {
if (isNode) return;
beforeEach(() => { enableDebugTools((<any>new SpyComponentRef())); });
beforeEach(() => {
enableDebugTools((<any>new SpyComponentRef()));
});
afterEach(() => { disableDebugTools(); });
afterEach(() => {
disableDebugTools();
});
it('should time change detection', () => { callNgProfilerTimeChangeDetection(); });
it('should time change detection', () => {
callNgProfilerTimeChangeDetection();
});
it('should time change detection with recording',
() => { callNgProfilerTimeChangeDetection({'record': true}); });
it('should time change detection with recording', () => {
callNgProfilerTimeChangeDetection({'record': true});
});
});
}

View File

@ -9,124 +9,126 @@
import {DOCUMENT} from '@angular/common';
import {TestBed} from '@angular/core/testing';
import {BrowserModule, BrowserTransferStateModule, TransferState} from '@angular/platform-browser';
import {StateKey, escapeHtml, makeStateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state';
import {escapeHtml, makeStateKey, StateKey, unescapeHtml} from '@angular/platform-browser/src/browser/transfer_state';
(function() {
function removeScriptTag(doc: Document, id: string) {
const existing = doc.getElementById(id);
if (existing) {
doc.body.removeChild(existing);
}
function removeScriptTag(doc: Document, id: string) {
const existing = doc.getElementById(id);
if (existing) {
doc.body.removeChild(existing);
}
}
function addScriptTag(doc: Document, appId: string, data: {}) {
const script = doc.createElement('script');
const id = appId + '-state';
script.id = id;
script.setAttribute('type', 'application/json');
script.textContent = escapeHtml(JSON.stringify(data));
function addScriptTag(doc: Document, appId: string, data: {}) {
const script = doc.createElement('script');
const id = appId + '-state';
script.id = id;
script.setAttribute('type', 'application/json');
script.textContent = escapeHtml(JSON.stringify(data));
// Remove any stale script tags.
removeScriptTag(doc, id);
// Remove any stale script tags.
removeScriptTag(doc, id);
doc.body.appendChild(script);
}
doc.body.appendChild(script);
}
describe('TransferState', () => {
const APP_ID = 'test-app';
let doc: Document;
describe('TransferState', () => {
const APP_ID = 'test-app';
let doc: Document;
const TEST_KEY = makeStateKey<number>('test');
const DELAYED_KEY = makeStateKey<string>('delayed');
const TEST_KEY = makeStateKey<number>('test');
const DELAYED_KEY = makeStateKey<string>('delayed');
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule.withServerTransition({appId: APP_ID}),
BrowserTransferStateModule,
]
});
doc = TestBed.inject(DOCUMENT);
});
afterEach(() => { removeScriptTag(doc, APP_ID + '-state'); });
it('is initialized from script tag', () => {
addScriptTag(doc, APP_ID, {test: 10});
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(10);
});
it('is initialized to empty state if script tag not found', () => {
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
});
it('supports adding new keys using set', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.get(TEST_KEY, 0)).toBe(20);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'0\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 0);
expect(transferState.get(TEST_KEY, 20)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'false\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, false);
expect(transferState.get(TEST_KEY, true)).toBe(false);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'null\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, null);
expect(transferState.get(TEST_KEY, 20 as any)).toBe(null);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports removing keys', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
transferState.remove(TEST_KEY);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(false);
});
it('supports serialization using toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.toJson()).toBe('{"test":20}');
});
it('calls onSerialize callbacks when calling toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
let value = 'initial';
transferState.onSerialize(DELAYED_KEY, () => value);
value = 'changed';
expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}');
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule.withServerTransition({appId: APP_ID}),
BrowserTransferStateModule,
]
});
doc = TestBed.inject(DOCUMENT);
});
describe('escape/unescape', () => {
it('works with all escaped characters', () => {
const testString = '</script><script>alert(\'Hello&\' + "World");';
const testObj = {testString};
const escaped = escapeHtml(JSON.stringify(testObj));
expect(escaped).toBe(
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}');
const unescapedObj = JSON.parse(unescapeHtml(escaped));
expect(unescapedObj['testString']).toBe(testString);
});
afterEach(() => {
removeScriptTag(doc, APP_ID + '-state');
});
it('is initialized from script tag', () => {
addScriptTag(doc, APP_ID, {test: 10});
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(10);
});
it('is initialized to empty state if script tag not found', () => {
const transferState: TransferState = TestBed.inject(TransferState);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
});
it('supports adding new keys using set', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.get(TEST_KEY, 0)).toBe(20);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'0\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 0);
expect(transferState.get(TEST_KEY, 20)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'false\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, false);
expect(transferState.get(TEST_KEY, true)).toBe(false);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports setting and accessing value \'null\' via get', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, null);
expect(transferState.get(TEST_KEY, 20 as any)).toBe(null);
expect(transferState.hasKey(TEST_KEY)).toBe(true);
});
it('supports removing keys', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
transferState.remove(TEST_KEY);
expect(transferState.get(TEST_KEY, 0)).toBe(0);
expect(transferState.hasKey(TEST_KEY)).toBe(false);
});
it('supports serialization using toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
expect(transferState.toJson()).toBe('{"test":20}');
});
it('calls onSerialize callbacks when calling toJson()', () => {
const transferState: TransferState = TestBed.inject(TransferState);
transferState.set(TEST_KEY, 20);
let value = 'initial';
transferState.onSerialize(DELAYED_KEY, () => value);
value = 'changed';
expect(transferState.toJson()).toBe('{"test":20,"delayed":"changed"}');
});
});
describe('escape/unescape', () => {
it('works with all escaped characters', () => {
const testString = '</script><script>alert(\'Hello&\' + "World");';
const testObj = {testString};
const escaped = escapeHtml(JSON.stringify(testObj));
expect(escaped).toBe(
'{&q;testString&q;:&q;&l;/script&g;&l;script&g;' +
'alert(&s;Hello&a;&s; + \\&q;World\\&q;);&q;}');
const unescapedObj = JSON.parse(unescapeHtml(escaped));
expect(unescapedObj['testString']).toBe(testString);
});
});
})();

View File

@ -11,7 +11,6 @@ import {BrowserDetection} from '../testing/src/browser_util';
{
describe('BrowserDetection', () => {
const browsers = [
{
name: 'Chrome',
@ -224,7 +223,7 @@ import {BrowserDetection} from '../testing/src/browser_util';
];
browsers.forEach((browser: {[key: string]: any}) => {
it(`should detect ${browser[ 'name']}`, () => {
it(`should detect ${browser['name']}`, () => {
const bd = new BrowserDetection(<string>browser['ua']);
expect(bd.isFirefox).toBe(browser['isFirefox']);
expect(bd.isAndroid).toBe(browser['isAndroid']);

View File

@ -14,361 +14,393 @@ import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/do
import {createMouseEvent, el} from '../../../testing/src/browser_util';
(function() {
if (isNode) return;
let domEventPlugin: DomEventsPlugin;
let doc: any;
let zone: NgZone;
if (isNode) return;
let domEventPlugin: DomEventsPlugin;
let doc: any;
let zone: NgZone;
describe('EventManager', () => {
beforeEach(() => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({});
domEventPlugin = new DomEventsPlugin(doc);
describe('EventManager', () => {
beforeEach(() => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({});
domEventPlugin = new DomEventsPlugin(doc);
});
it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one',
() => {
const element = el('<div></div>');
const handler = (e: any /** TODO #9100 */) => e;
const plugin = new FakeEventManagerPlugin(doc, ['click']);
const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin.eventHandler['click']).toBe(handler);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
const element = el('<div></div>');
const clickHandler = (e: any /** TODO #9100 */) => e;
const dblClickHandler = (e: any /** TODO #9100 */) => e;
const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']);
const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']);
const manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin2.eventHandler['click']).toBe(clickHandler);
expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler);
});
it('should throw when no plugin can handle the event', () => {
const element = el('<div></div>');
const plugin = new FakeEventManagerPlugin(doc, ['dblclick']);
const manager = new EventManager([plugin], new FakeNgZone());
expect(() => manager.addEventListener(element, 'click', null!))
.toThrowError('No event manager plugin found for event click');
});
it('events are caught when fired from a child', () => {
const element = el('<div><div></div></div>');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
doc.body.appendChild(element);
const child = element.firstChild as Element;
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
getDOM().dispatchEvent(child, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
});
it('should add and remove global event listeners', () => {
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
const remover = manager.addGlobalEventListener('document', 'click', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
receivedEvent = null;
remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should keep zone when addEventListener', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover: any = null;
Zone.root.run(() => {
remover = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).toBe(Zone.root.name);
it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one',
() => {
const element = el('<div></div>');
const handler = (e: any /** TODO #9100 */) => e;
const plugin = new FakeEventManagerPlugin(doc, ['click']);
const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin.eventHandler['click']).toBe(handler);
});
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should delegate event bindings to the first plugin supporting the event', () => {
const element = el('<div></div>');
const clickHandler = (e: any /** TODO #9100 */) => e;
const dblClickHandler = (e: any /** TODO #9100 */) => e;
const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']);
const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']);
const manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
expect(plugin2.eventHandler['click']).toBe(clickHandler);
expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler);
it('should keep zone when addEventListener multiple times', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should throw when no plugin can handle the event', () => {
const element = el('<div></div>');
const plugin = new FakeEventManagerPlugin(doc, ['dblclick']);
const manager = new EventManager([plugin], new FakeNgZone());
expect(() => manager.addEventListener(element, 'click', null !))
.toThrowError('No event manager plugin found for event click');
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
it('events are caught when fired from a child', () => {
const element = el('<div><div></div></div>');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
doc.body.appendChild(element);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const child = element.firstChild as Element;
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; };
const manager = new EventManager([domEventPlugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
getDOM().dispatchEvent(child, dispatchedEvent);
it('should support event.stopImmediatePropagation', () => {
const Zone = (window as any)['Zone'];
expect(receivedEvent).toBe(dispatchedEvent);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
e.stopImmediatePropagation();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should add and remove global event listeners', () => {
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; };
const manager = new EventManager([domEventPlugin], new FakeNgZone());
const remover = manager.addGlobalEventListener('document', 'click', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
receivedEvent = null;
remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
it('should keep zone when addEventListener', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should handle event correctly when one handler remove itself ', () => {
const Zone = (window as any)['Zone'];
let remover: any = null;
Zone.root.run(() => { remover = manager.addEventListener(element, 'click', handler); });
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).toBe(Zone.root.name);
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should keep zone when addEventListener multiple times', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
receivedEvents = [];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
let remover1: any = null;
let remover2: any = null;
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should support event.stopImmediatePropagation', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
e.stopImmediatePropagation();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
it('should handle event correctly when one handler remove itself ', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
let remover1: any = null;
let remover2: any = null;
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
remover1 && remover1();
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should only add same callback once when addEventListener', () => {
const Zone = (window as any)['Zone'];
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'test']);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler);
});
it('should only add same callback once when addEventListener', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover1: any = null;
let remover2: any = null;
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler); });
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
Zone.root.fork({name: 'test'}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name]);
it('should be able to remove event listener which was added inside of ngZone', () => {
const Zone = (window as any)['Zone'];
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([]);
});
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
it('should be able to remove event listener which was added inside of ngZone', () => {
const Zone = (window as any)['Zone'];
let remover1: any = null;
let remover2: any = null;
// handler1 is added in root zone
Zone.root.run(() => { remover1 = manager.addEventListener(element, 'click', handler1); });
// handler2 is added in 'angular' zone
Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']);
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any[] /** TODO #9100 */ = [];
let receivedZones: any[] = [];
const handler1 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const handler2 = (e: any /** TODO #9100 */) => {
receivedEvents.push(e);
receivedZones.push(Zone.current.name);
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
// handler1 and handler2 are added in different zone
// one is angular zone, the other is not
// should still be able to remove them correctly
expect(receivedEvents).toEqual([]);
let remover1: any = null;
let remover2: any = null;
// handler1 is added in root zone
Zone.root.run(() => {
remover1 = manager.addEventListener(element, 'click', handler1);
});
it('should run blackListedEvents handler outside of ngZone', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('scroll');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
let remover = manager.addEventListener(element, 'scroll', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).not.toEqual('angular');
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
// handler2 is added in 'angular' zone
Zone.root.fork({name: 'fakeAngularZone', properties: {isAngularZone: true}}).run(() => {
remover2 = manager.addEventListener(element, 'click', handler2);
});
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvents).toEqual([dispatchedEvent, dispatchedEvent]);
expect(receivedZones).toEqual([Zone.root.name, 'fakeAngularZone']);
it('should only trigger one Change detection when bubbling', (done: DoneFn) => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({shouldCoalesceEventChangeDetection: true});
domEventPlugin = new DomEventsPlugin(doc);
const element = el('<div></div>');
const child = el('<div></div>');
element.appendChild(child);
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any = [];
let stables: any = [];
const handler = (e: any) => { receivedEvents.push(e); };
const manager = new EventManager([domEventPlugin], zone);
let removerChild: any;
let removerParent: any;
receivedEvents = [];
remover1 && remover1();
remover2 && remover2();
getDOM().dispatchEvent(element, dispatchedEvent);
// handler1 and handler2 are added in different zone
// one is angular zone, the other is not
// should still be able to remove them correctly
expect(receivedEvents).toEqual([]);
});
zone.run(() => {
removerChild = manager.addEventListener(child, 'click', handler);
removerParent = manager.addEventListener(element, 'click', handler);
});
zone.onStable.subscribe((isStable: any) => { stables.push(isStable); });
getDOM().dispatchEvent(child, dispatchedEvent);
requestAnimationFrame(() => {
expect(receivedEvents.length).toBe(2);
expect(stables.length).toBe(1);
it('should run blackListedEvents handler outside of ngZone', () => {
const Zone = (window as any)['Zone'];
const element = el('<div><div></div></div>');
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('scroll');
let receivedEvent: any /** TODO #9100 */ = null;
let receivedZone: any = null;
const handler = (e: any /** TODO #9100 */) => {
receivedEvent = e;
receivedZone = Zone.current;
};
const manager = new EventManager([domEventPlugin], new FakeNgZone());
removerChild && removerChild();
removerParent && removerParent();
done();
});
let remover = manager.addEventListener(element, 'scroll', handler);
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(receivedZone.name).not.toEqual('angular');
receivedEvent = null;
remover && remover();
getDOM().dispatchEvent(element, dispatchedEvent);
expect(receivedEvent).toBe(null);
});
it('should only trigger one Change detection when bubbling', (done: DoneFn) => {
doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
zone = new NgZone({shouldCoalesceEventChangeDetection: true});
domEventPlugin = new DomEventsPlugin(doc);
const element = el('<div></div>');
const child = el('<div></div>');
element.appendChild(child);
doc.body.appendChild(element);
const dispatchedEvent = createMouseEvent('click');
let receivedEvents: any = [];
let stables: any = [];
const handler = (e: any) => {
receivedEvents.push(e);
};
const manager = new EventManager([domEventPlugin], zone);
let removerChild: any;
let removerParent: any;
zone.run(() => {
removerChild = manager.addEventListener(child, 'click', handler);
removerParent = manager.addEventListener(element, 'click', handler);
});
zone.onStable.subscribe((isStable: any) => {
stables.push(isStable);
});
getDOM().dispatchEvent(child, dispatchedEvent);
requestAnimationFrame(() => {
expect(receivedEvents.length).toBe(2);
expect(stables.length).toBe(1);
removerChild && removerChild();
removerParent && removerParent();
done();
});
});
});
})();
/** @internal */
class FakeEventManagerPlugin extends EventManagerPlugin {
eventHandler: {[event: string]: Function} = {};
constructor(doc: any, public supportedEvents: string[]) { super(doc); }
constructor(doc: any, public supportedEvents: string[]) {
super(doc);
}
supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; }
supports(eventName: string): boolean {
return this.supportedEvents.indexOf(eventName) > -1;
}
addEventListener(element: any, eventName: string, handler: Function) {
this.eventHandler[eventName] = handler;
return () => { delete this.eventHandler[eventName]; };
return () => {
delete this.eventHandler[eventName];
};
}
}
class FakeNgZone extends NgZone {
constructor() { super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true}); }
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T { return fn(); }
runOutsideAngular(fn: Function) { return fn(); }
constructor() {
super({enableLongStackTrace: false, shouldCoalesceEventChangeDetection: true});
}
run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T {
return fn();
}
runOutsideAngular(fn: Function) {
return fn();
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation, destroyPlatform} from '@angular/core';
import {Component, destroyPlatform, EventEmitter, Injector, Input, NgModule, Output, Renderer2, ViewEncapsulation} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
@ -15,14 +15,15 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
if (browserDetection.supportsShadowDom) {
describe('ShadowDOM Support', () => {
let testContainer: HTMLDivElement;
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
beforeEach(() => {
TestBed.configureTestingModule({imports: [TestModule]});
});
it('should attach and use a shadowRoot when ViewEncapsulation.Native is set', () => {
const compEl = TestBed.createComponent(ShadowComponent).nativeElement;
expect(compEl.shadowRoot !.textContent).toEqual('Hello World');
expect(compEl.shadowRoot!.textContent).toEqual('Hello World');
});
it('should use the shadow root to encapsulate styles', () => {
@ -40,10 +41,10 @@ if (browserDetection.supportsShadowDom) {
const el = TestBed.createComponent(ShadowSlotComponent).nativeElement;
const projectedContent = document.createTextNode('Hello Slot!');
el.appendChild(projectedContent);
const slot = el.shadowRoot !.querySelector('slot');
const slot = el.shadowRoot!.querySelector('slot');
expect(slot !.assignedNodes().length).toBe(1);
expect(slot !.assignedNodes()[0].textContent).toBe('Hello Slot!');
expect(slot!.assignedNodes().length).toBe(1);
expect(slot!.assignedNodes()[0].textContent).toBe('Hello Slot!');
});
it('should allow the usage of named <slot> elements', () => {
@ -65,16 +66,16 @@ if (browserDetection.supportsShadowDom) {
el.appendChild(articleContent);
el.appendChild(articleSubcontent);
const headerSlot = el.shadowRoot !.querySelector('slot[name=header]') as HTMLSlotElement;
const articleSlot = el.shadowRoot !.querySelector('slot[name=article]') as HTMLSlotElement;
const headerSlot = el.shadowRoot!.querySelector('slot[name=header]') as HTMLSlotElement;
const articleSlot = el.shadowRoot!.querySelector('slot[name=article]') as HTMLSlotElement;
expect(headerSlot !.assignedNodes().length).toBe(1);
expect(headerSlot !.assignedNodes()[0].textContent).toBe('Header Text!');
expect(headerSlot!.assignedNodes().length).toBe(1);
expect(headerSlot!.assignedNodes()[0].textContent).toBe('Header Text!');
expect(headerContent.assignedSlot).toBe(headerSlot);
expect(articleSlot !.assignedNodes().length).toBe(2);
expect(articleSlot !.assignedNodes()[0].textContent).toBe('Article Text!');
expect(articleSlot !.assignedNodes()[1].textContent).toBe('Article Subtext!');
expect(articleSlot!.assignedNodes().length).toBe(2);
expect(articleSlot!.assignedNodes()[0].textContent).toBe('Article Text!');
expect(articleSlot!.assignedNodes()[1].textContent).toBe('Article Subtext!');
expect(articleContent.assignedSlot).toBe(articleSlot);
expect(articleSubcontent.assignedSlot).toBe(articleSlot);
});

View File

@ -7,8 +7,8 @@
*/
import {CompilerConfig, ResourceLoader} from '@angular/compiler';
import {CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core';
import {TestBed, async, fakeAsync, getTestBed, inject, tick, withModule} from '@angular/core/testing';
import {Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, Inject, Injectable, InjectionToken, Injector, Input, NgModule, Optional, Pipe, SkipSelf, ɵstringify as stringify} from '@angular/core';
import {async, fakeAsync, getTestBed, inject, TestBed, tick, withModule} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
@ -18,7 +18,9 @@ import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/priv
@Injectable()
class ChildComp {
childBinding: string;
constructor() { this.childBinding = 'Child'; }
constructor() {
this.childBinding = 'Child';
}
}
@Component({selector: 'child-comp', template: `<span>Mock</span>`})
@ -52,12 +54,16 @@ class ChildChildComp {
@Injectable()
class ChildWithChildComp {
childBinding: string;
constructor() { this.childBinding = 'Child'; }
constructor() {
this.childBinding = 'Child';
}
}
class FancyService {
value: string = 'real value';
getAsyncValue() { return Promise.resolve('async value'); }
getAsyncValue() {
return Promise.resolve('async value');
}
getTimeoutValue() {
return new Promise<string>((resolve, reject) => setTimeout(() => resolve('timeout value'), 10));
}
@ -88,13 +94,14 @@ class TestViewProvidersComp {
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
class SomeDirective {
// TODO(issue/24571): remove '!'.
@Input()
someDir !: string;
@Input() someDir!: string;
}
@Pipe({name: 'somePipe'})
class SomePipe {
transform(value: string) { return `transformed ${value}`; }
transform(value: string) {
return `transformed ${value}`;
}
}
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
@ -120,11 +127,17 @@ const bTok = new InjectionToken<string>('b');
describe('using the async helper with context passing', () => {
type TestContext = {actuallyDone: boolean};
beforeEach(function(this: TestContext) { this.actuallyDone = false; });
beforeEach(function(this: TestContext) {
this.actuallyDone = false;
});
afterEach(function(this: TestContext) { expect(this.actuallyDone).toEqual(true); });
afterEach(function(this: TestContext) {
expect(this.actuallyDone).toEqual(true);
});
it('should run normal tests', function(this: TestContext) { this.actuallyDone = true; });
it('should run normal tests', function(this: TestContext) {
this.actuallyDone = true;
});
it('should run normal async tests', function(this: TestContext, done) {
setTimeout(() => {
@ -133,8 +146,9 @@ const bTok = new InjectionToken<string>('b');
}, 0);
});
it('should run async tests with tasks',
async(function(this: TestContext) { setTimeout(() => this.actuallyDone = true, 0); }));
it('should run async tests with tasks', async(function(this: TestContext) {
setTimeout(() => this.actuallyDone = true, 0);
}));
it('should run async tests with promises', async(function(this: TestContext) {
const p = new Promise((resolve, reject) => setTimeout(resolve, 10));
@ -149,18 +163,26 @@ const bTok = new InjectionToken<string>('b');
type TestContext = {contextModified: boolean};
beforeEach(function(this: TestContext) { this.contextModified = false; });
beforeEach(function(this: TestContext) {
this.contextModified = false;
});
afterEach(function(this: TestContext) { expect(this.contextModified).toEqual(true); });
afterEach(function(this: TestContext) {
expect(this.contextModified).toEqual(true);
});
it('should pass context to inject helper',
inject([], function(this: TestContext) { this.contextModified = true; }));
it('should pass context to inject helper', inject([], function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to fakeAsync helper',
fakeAsync(function(this: TestContext) { this.contextModified = true; }));
it('should pass context to fakeAsync helper', fakeAsync(function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to withModule helper - simple',
withModule(moduleConfig, function(this: TestContext) { this.contextModified = true; }));
withModule(moduleConfig, function(this: TestContext) {
this.contextModified = true;
}));
it('should pass context to withModule helper - advanced',
withModule(moduleConfig)
@ -199,7 +221,7 @@ const bTok = new InjectionToken<string>('b');
it('should allow the use of fakeAsync',
fakeAsync(inject([FancyService], (service: FancyService) => {
let value: string = undefined !;
let value: string = undefined!;
service.getAsyncValue().then((val) => value = val);
tick();
expect(value).toEqual('async value');
@ -329,7 +351,9 @@ const bTok = new InjectionToken<string>('b');
describe('overwriting metadata', () => {
@Pipe({name: 'undefined'})
class SomePipe {
transform(value: string): string { return `transformed ${value}`; }
transform(value: string): string {
return `transformed ${value}`;
}
}
@Directive({selector: '[undefined]'})
@ -418,7 +442,6 @@ const bTok = new InjectionToken<string>('b');
});
describe('overriding providers', () => {
describe('in core', () => {
it('ComponentFactoryResolver', () => {
const componentFactoryMock =
@ -429,7 +452,6 @@ const bTok = new InjectionToken<string>('b');
});
describe('in NgModules', () => {
it('should support useValue', () => {
TestBed.configureTestingModule({
providers: [
@ -501,7 +523,9 @@ const bTok = new InjectionToken<string>('b');
@NgModule()
class SomeModule {
constructor() { someModule = this; }
constructor() {
someModule = this;
}
}
TestBed.configureTestingModule({
@ -731,11 +755,12 @@ const bTok = new InjectionToken<string>('b');
@Directive({selector: '[test]'})
class TestDir {
constructor() { testDir = this; }
constructor() {
testDir = this;
}
// TODO(issue/24571): remove '!'.
@Input('test')
test !: string;
@Input('test') test!: string;
}
TestBed.overrideTemplateUsingTestingModule(
@ -746,7 +771,7 @@ const bTok = new InjectionToken<string>('b');
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('Hello world!');
expect(testDir).toBeAnInstanceOf(TestDir);
expect(testDir !.test).toBe('some prop');
expect(testDir!.test).toBe('some prop');
});
it('should throw if the TestBed is already created', () => {
@ -776,9 +801,7 @@ const bTok = new InjectionToken<string>('b');
});
describe('setting up the compiler', () => {
describe('providers', () => {
it('should use set up providers', fakeAsync(() => {
// Keeping this component inside the test is needed to make sure it's not resolved
// prior to this test, thus having ɵcmp and a reference in resource
@ -869,8 +892,9 @@ const bTok = new InjectionToken<string>('b');
const itPromise = patchJasmineIt();
const barError = new Error('bar');
it('throws an async error',
async(inject([], () => setTimeout(() => { throw barError; }, 0))));
it('throws an async error', async(inject([], () => setTimeout(() => {
throw barError;
}, 0))));
itPromise.then(() => done.fail('Expected test to fail, but it did not'), (err) => {
expect(err).toEqual(barError);
@ -883,7 +907,7 @@ const bTok = new InjectionToken<string>('b');
const itPromise = patchJasmineIt();
it('should fail with an error from a promise', async(inject([], () => {
let reject: (error: any) => void = undefined !;
let reject: (error: any) => void = undefined!;
const promise = new Promise((_, rej) => reject = rej);
const p = promise.then(() => expect(1).toEqual(2));
@ -919,21 +943,23 @@ const bTok = new InjectionToken<string>('b');
}
expect(
() => it(
'should fail', withModule(
{declarations: [InlineCompWithUrlTemplate]},
() => TestBed.createComponent(InlineCompWithUrlTemplate))))
() =>
it('should fail',
withModule(
{declarations: [InlineCompWithUrlTemplate]},
() => TestBed.createComponent(InlineCompWithUrlTemplate))))
.toThrowError(
ivyEnabled ?
`Component 'InlineCompWithUrlTemplate' is not resolved:
- templateUrl: /base/angular/packages/platform-browser/test/static_assets/test.html
Did you run and wait for 'resolveComponentResources()'?` :
`This test module uses the component ${stringify(InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`This test module uses the component ${
stringify(
InlineCompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
`Please call "TestBed.compileComponents" before your test.`);
restoreJasmineIt();
});
});
@ -972,7 +998,6 @@ Did you run and wait for 'resolveComponentResources()'?` :
});
describe('creating components', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
@ -1008,7 +1033,6 @@ Did you run and wait for 'resolveComponentResources()'?` :
const componentFixture = TestBed.createComponent(ChildComp);
componentFixture.detectChanges();
expect(componentFixture.nativeElement).toHaveText('Mock');
}));
it('should override a provider', async(() => {

View File

@ -5,8 +5,9 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {APP_ID, NgModule, NgZone, PLATFORM_INITIALIZER, PlatformRef, StaticProvider, createPlatformFactory, platformCore} from '@angular/core';
import {APP_ID, createPlatformFactory, NgModule, NgZone, PLATFORM_INITIALIZER, platformCore, PlatformRef, StaticProvider} from '@angular/core';
import {BrowserModule, ɵBrowserDomAdapter as BrowserDomAdapter, ɵELEMENT_PROBE_PROVIDERS as ELEMENT_PROBE_PROVIDERS} from '@angular/platform-browser';
import {BrowserDetection, createNgZone} from './browser_util';
function initBrowserTests() {

View File

@ -19,11 +19,17 @@ export class BrowserDetection {
return getDOM() ? getDOM().getUserAgent() : '';
}
static setup() { return new BrowserDetection(null); }
static setup() {
return new BrowserDetection(null);
}
constructor(ua: string|null) { this._overrideUa = ua; }
constructor(ua: string|null) {
this._overrideUa = ua;
}
get isFirefox(): boolean { return this._ua.indexOf('Firefox') > -1; }
get isFirefox(): boolean {
return this._ua.indexOf('Firefox') > -1;
}
get isAndroid(): boolean {
return this._ua.indexOf('Mozilla/5.0') > -1 && this._ua.indexOf('Android') > -1 &&
@ -31,9 +37,13 @@ export class BrowserDetection {
this._ua.indexOf('IEMobile') == -1;
}
get isEdge(): boolean { return this._ua.indexOf('Edge') > -1; }
get isEdge(): boolean {
return this._ua.indexOf('Edge') > -1;
}
get isIE(): boolean { return this._ua.indexOf('Trident') > -1; }
get isIE(): boolean {
return this._ua.indexOf('Trident') > -1;
}
get isWebkit(): boolean {
return this._ua.indexOf('AppleWebKit') > -1 && this._ua.indexOf('Edge') == -1 &&
@ -45,7 +55,9 @@ export class BrowserDetection {
this._ua.indexOf('IEMobile') == -1;
}
get isSlow(): boolean { return this.isAndroid || this.isIE || this.isIOS7; }
get isSlow(): boolean {
return this.isAndroid || this.isIE || this.isIOS7;
}
// The Intl API is only natively supported in Chrome, Firefox, IE11 and Edge.
// This detector is needed in tests to make the difference between:
@ -67,13 +79,17 @@ export class BrowserDetection {
this._ua.indexOf('Edge') == -1;
}
get supportsCustomElements() { return (typeof(<any>global).customElements !== 'undefined'); }
get supportsDeprecatedCustomCustomElementsV0() {
return (typeof(document as any).registerElement !== 'undefined');
get supportsCustomElements() {
return (typeof (<any>global).customElements !== 'undefined');
}
get supportsRegExUnicodeFlag(): boolean { return RegExp.prototype.hasOwnProperty('unicode'); }
get supportsDeprecatedCustomCustomElementsV0() {
return (typeof (document as any).registerElement !== 'undefined');
}
get supportsRegExUnicodeFlag(): boolean {
return RegExp.prototype.hasOwnProperty('unicode');
}
get supportsShadowDom() {
const testEl = document.createElement('div');
@ -203,10 +219,10 @@ export function setCookie(name: string, value: string) {
}
export function supportsWebAnimation(): boolean {
return typeof(<any>Element).prototype['animate'] === 'function';
return typeof (<any>Element).prototype['animate'] === 'function';
}
export function hasStyle(element: any, styleName: string, styleValue?: string | null): boolean {
export function hasStyle(element: any, styleName: string, styleValue?: string|null): boolean {
const value = element.style[styleName] || '';
return styleValue ? value == styleValue : value.length > 0;
}

View File

@ -123,7 +123,9 @@ export const expect: <T = any>(actual: T) => NgMatchers<T> = _global.expect;
return '' + m;
}
const res: any[] = [];
m.forEach((v: any, k: any) => { res.push(`${String(k)}:${String(v)}`); });
m.forEach((v: any, k: any) => {
res.push(`${String(k)}:${String(v)}`);
});
return `{ ${res.join(',')} }`;
};
@ -141,7 +143,7 @@ _global.beforeEach(function() {
return pass;
} else {
// TODO(misko): we should change the return, but jasmine.d.ts is not null safe
return undefined !;
return undefined!;
}
});
jasmine.addMatchers({
@ -149,7 +151,12 @@ _global.beforeEach(function() {
return {
compare: function(actual: any) {
const pass = typeof actual === 'object' && typeof actual.then === 'function';
return {pass: pass, get message() { return 'Expected ' + actual + ' to be a promise'; }};
return {
pass: pass,
get message() {
return 'Expected ' + actual + ' to be a promise';
}
};
}
};
},
@ -174,7 +181,9 @@ _global.beforeEach(function() {
const actualText = elementText(actual);
return {
pass: actualText == expectedText,
get message() { return 'Expected ' + actualText + ' to be equal to ' + expectedText; }
get message() {
return 'Expected ' + actualText + ' to be equal to ' + expectedText;
}
};
}
};
@ -188,7 +197,8 @@ _global.beforeEach(function() {
return {
pass: hasClass(actual, className) == !isNot,
get message() {
return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
return `Expected ${actual.outerHTML} ${
isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
}
};
};
@ -203,8 +213,9 @@ _global.beforeEach(function() {
allPassed = hasStyle(actual, styles);
} else {
allPassed = Object.keys(styles).length !== 0;
Object.keys(styles).forEach(
prop => { allPassed = allPassed && hasStyle(actual, prop, styles[prop]); });
Object.keys(styles).forEach(prop => {
allPassed = allPassed && hasStyle(actual, prop, styles[prop]);
});
}
return {
@ -212,7 +223,8 @@ _global.beforeEach(function() {
get message() {
const expectedValueStr = typeof styles === 'string' ? styles : JSON.stringify(styles);
return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the
CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${expectedValueStr}"`;
CSS ${typeof styles === 'string' ? 'property' : 'styles'} "${
expectedValueStr}"`;
}
};
}
@ -225,7 +237,9 @@ _global.beforeEach(function() {
const errorMessage = actual.toString();
return {
pass: errorMessage.indexOf(expectedText) > -1,
get message() { return 'Expected ' + errorMessage + ' to contain ' + expectedText; }
get message() {
return 'Expected ' + errorMessage + ' to contain ' + expectedText;
}
};
}
};
@ -244,8 +258,8 @@ _global.beforeEach(function() {
return {
pass: missedMethods.length == 0,
get message() {
return 'Expected ' + actualObject + ' to have the following methods: ' +
missedMethods.join(', ');
return 'Expected ' + actualObject +
' to have the following methods: ' + missedMethods.join(', ');
}
};
}
@ -262,8 +276,8 @@ _global.beforeEach(function() {
if (!(actualFixture instanceof ComponentFixture)) {
return {
pass: false,
message: msgFn(
`Expected actual to be of type \'ComponentFixture\' [actual=${actualFixture.constructor.name}]`)
message: msgFn(`Expected actual to be of type \'ComponentFixture\' [actual=${
actualFixture.constructor.name}]`)
};
}