refactor(webworkers): move webworkers to separate @angular/platform-webworker and @angular/platform-webworker-dynamic packages

BREAKING CHANGE: web worker platform is now exported via separate packages.

Please use @angular/platform-webworker and @angular/platform-webworker-dynamic
This commit is contained in:
Igor Minar
2016-08-30 11:15:25 -07:00
committed by Victor Berchet
parent 0f68351979
commit 71ae2c4525
84 changed files with 561 additions and 305 deletions

View File

@ -1,160 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {NgZone} from '@angular/core';
import {withModule} from '@angular/core/testing/test_bed';
import {AsyncTestCompleter, MockNgZone, beforeEach, beforeEachProviders, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {MessageBus} from '@angular/platform-browser/src/web_workers/shared/message_bus';
import {createConnectedMessageBus} from './message_bus_util';
export function main() {
/**
* Tests the PostMessageBus
*/
describe('MessageBus', () => {
var bus: MessageBus;
beforeEach(() => { bus = createConnectedMessageBus(); });
it('should pass messages in the same channel from sink to source',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
const CHANNEL = 'CHANNEL 1';
const MESSAGE = 'Test message';
bus.initChannel(CHANNEL, false);
var fromEmitter = bus.from(CHANNEL);
fromEmitter.subscribe({
next: (message: any) => {
expect(message).toEqual(MESSAGE);
async.done();
}
});
var toEmitter = bus.to(CHANNEL);
toEmitter.emit(MESSAGE);
}));
it('should broadcast', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
const CHANNEL = 'CHANNEL 1';
const MESSAGE = 'TESTING';
const NUM_LISTENERS = 2;
bus.initChannel(CHANNEL, false);
var callCount = 0;
var emitHandler = (message: any) => {
expect(message).toEqual(MESSAGE);
callCount++;
if (callCount == NUM_LISTENERS) {
async.done();
}
};
for (var i = 0; i < NUM_LISTENERS; i++) {
var emitter = bus.from(CHANNEL);
emitter.subscribe({next: emitHandler});
}
var toEmitter = bus.to(CHANNEL);
toEmitter.emit(MESSAGE);
}));
it('should keep channels independent',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
const CHANNEL_ONE = 'CHANNEL 1';
const CHANNEL_TWO = 'CHANNEL 2';
const MESSAGE_ONE = 'This is a message on CHANNEL 1';
const MESSAGE_TWO = 'This is a message on CHANNEL 2';
var callCount = 0;
bus.initChannel(CHANNEL_ONE, false);
bus.initChannel(CHANNEL_TWO, false);
var firstFromEmitter = bus.from(CHANNEL_ONE);
firstFromEmitter.subscribe({
next: (message: any) => {
expect(message).toEqual(MESSAGE_ONE);
callCount++;
if (callCount == 2) {
async.done();
}
}
});
var secondFromEmitter = bus.from(CHANNEL_TWO);
secondFromEmitter.subscribe({
next: (message: any) => {
expect(message).toEqual(MESSAGE_TWO);
callCount++;
if (callCount == 2) {
async.done();
}
}
});
var firstToEmitter = bus.to(CHANNEL_ONE);
firstToEmitter.emit(MESSAGE_ONE);
var secondToEmitter = bus.to(CHANNEL_TWO);
secondToEmitter.emit(MESSAGE_TWO);
}));
});
describe('PostMessageBusSink', () => {
var bus: MessageBus;
const CHANNEL = 'Test Channel';
function setup(runInZone: boolean, zone: NgZone) {
bus.attachToZone(zone);
bus.initChannel(CHANNEL, runInZone);
}
/**
* Flushes pending messages and then runs the given function.
*/
// TODO(mlaval): timeout is fragile, test to be rewritten
function flushMessages(fn: () => void) { setTimeout(fn, 50); }
it('should buffer messages and wait for the zone to exit before sending',
withModule({providers: [{provide: NgZone, useClass: MockNgZone}]})
.inject(
[AsyncTestCompleter, NgZone],
(async: AsyncTestCompleter, zone: MockNgZone) => {
bus = createConnectedMessageBus();
setup(true, zone);
var wasCalled = false;
bus.from(CHANNEL).subscribe({next: (message: any) => { wasCalled = true; }});
bus.to(CHANNEL).emit('hi');
flushMessages(() => {
expect(wasCalled).toBeFalsy();
zone.simulateZoneExit();
flushMessages(() => {
expect(wasCalled).toBeTruthy();
async.done();
});
});
}),
500);
it('should send messages immediatly when run outside the zone',
inject([AsyncTestCompleter, NgZone], (async: AsyncTestCompleter, zone: MockNgZone) => {
bus = createConnectedMessageBus();
setup(false, zone);
var wasCalled = false;
bus.from(CHANNEL).subscribe({next: (message: any) => { wasCalled = true; }});
bus.to(CHANNEL).emit('hi');
flushMessages(() => {
expect(wasCalled).toBeTruthy();
async.done();
});
}), 10000);
});
}

View File

@ -1,35 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {MessageBus} from '@angular/platform-browser/src/web_workers/shared/message_bus';
import {PostMessageBus, PostMessageBusSink, PostMessageBusSource} from '@angular/platform-browser/src/web_workers/shared/post_message_bus';
/*
* Returns a PostMessageBus thats sink is connected to its own source.
* Useful for testing the sink and source.
*/
export function createConnectedMessageBus(): MessageBus {
var mockPostMessage = new MockPostMessage();
var source = new PostMessageBusSource(<any>mockPostMessage);
var sink = new PostMessageBusSink(mockPostMessage);
return new PostMessageBus(sink, source);
}
class MockPostMessage {
private _listener: EventListener;
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void {
if (type === 'message') {
this._listener = listener;
}
}
postMessage(data: any, transfer?: [ArrayBuffer]): void { this._listener(<any>{data: data}); }
}

View File

@ -1,27 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {EventEmitter} from '../../../src/facade/async';
export class MockEventEmitter<T> extends EventEmitter<T> {
private _nextFns: Function[] = [];
constructor() { super(); }
subscribe(generator: any): any {
this._nextFns.push(generator.next);
return new MockDisposable();
}
emit(value: any) { this._nextFns.forEach(fn => fn(value)); }
}
class MockDisposable {
isUnsubscribed: boolean = false;
unsubscribe(): void {}
}

View File

@ -1,37 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {beforeEach, ddescribe, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {RenderStore} from '@angular/platform-browser/src/web_workers/shared/render_store';
export function main() {
describe('RenderStoreSpec', () => {
var store: RenderStore;
beforeEach(() => { store = new RenderStore(); });
it('should allocate ids', () => {
expect(store.allocateId()).toBe(0);
expect(store.allocateId()).toBe(1);
});
it('should serialize objects', () => {
var id = store.allocateId();
var obj = 'testObject';
store.store(obj, id);
expect(store.serialize(obj)).toBe(id);
});
it('should deserialize objects', () => {
var id = store.allocateId();
var obj = 'testObject';
store.store(obj, id);
expect(store.deserialize(id)).toBe(obj);
});
});
}

View File

@ -1,70 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {beforeEach, beforeEachProviders, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
import {ON_WEB_WORKER} from '@angular/platform-browser/src/web_workers/shared/api';
import {RenderStore} from '@angular/platform-browser/src/web_workers/shared/render_store';
import {PRIMITIVE, Serializer} from '@angular/platform-browser/src/web_workers/shared/serializer';
import {ServiceMessageBroker, ServiceMessageBroker_} from '@angular/platform-browser/src/web_workers/shared/service_message_broker';
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
import {createPairedMessageBuses} from './web_worker_test_util';
export function main() {
const CHANNEL = 'UIMessageBroker Test Channel';
const TEST_METHOD = 'TEST_METHOD';
const PASSED_ARG_1 = 5;
const PASSED_ARG_2 = 'TEST';
const RESULT = 20;
const ID = 'methodId';
beforeEachProviders(() => [Serializer, {provide: ON_WEB_WORKER, useValue: true}, RenderStore]);
describe('UIMessageBroker', () => {
var messageBuses: any /** TODO #9100 */;
beforeEach(() => {
messageBuses = createPairedMessageBuses();
messageBuses.ui.initChannel(CHANNEL);
messageBuses.worker.initChannel(CHANNEL);
});
it('should call registered method with correct arguments',
inject([Serializer], (serializer: Serializer) => {
var broker = new ServiceMessageBroker_(messageBuses.ui, serializer, CHANNEL);
broker.registerMethod(TEST_METHOD, [PRIMITIVE, PRIMITIVE], (arg1, arg2) => {
expect(arg1).toEqual(PASSED_ARG_1);
expect(arg2).toEqual(PASSED_ARG_2);
});
messageBuses.worker.to(CHANNEL).emit(
{'method': TEST_METHOD, 'args': [PASSED_ARG_1, PASSED_ARG_2]});
}));
it('should return promises to the worker', inject([Serializer], (serializer: Serializer) => {
var broker = new ServiceMessageBroker_(messageBuses.ui, serializer, CHANNEL);
broker.registerMethod(TEST_METHOD, [PRIMITIVE], (arg1) => {
expect(arg1).toEqual(PASSED_ARG_1);
return new Promise((res, rej) => {
try {
res(RESULT);
} catch (e) {
rej(e);
}
});
});
messageBuses.worker.to(CHANNEL).emit(
{'method': TEST_METHOD, 'id': ID, 'args': [PASSED_ARG_1]});
messageBuses.worker.from(CHANNEL).subscribe({
next: (data: any) => {
expect(data.type).toEqual('result');
expect(data.id).toEqual(ID);
expect(data.value).toEqual(RESULT);
},
});
}));
});
}

View File

@ -1,140 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {Type} from '@angular/core';
import {NgZone} from '@angular/core/src/zone/ng_zone';
import {ClientMessageBroker, ClientMessageBrokerFactory_, UiArguments} from '@angular/platform-browser/src/web_workers/shared/client_message_broker';
import {MessageBus, MessageBusSink, MessageBusSource} from '@angular/platform-browser/src/web_workers/shared/message_bus';
import {ListWrapper, StringMapWrapper} from '../../../src/facade/collection';
import {isPresent} from '../../../src/facade/lang';
import {SpyMessageBroker} from '../worker/spies';
import {MockEventEmitter} from './mock_event_emitter';
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
/**
* Returns two MessageBus instances that are attached to each other.
* Such that whatever goes into one's sink comes out the others source.
*/
export function createPairedMessageBuses(): PairedMessageBuses {
var firstChannels: {[key: string]: MockEventEmitter<any>} = {};
var workerMessageBusSink = new MockMessageBusSink(firstChannels);
var uiMessageBusSource = new MockMessageBusSource(firstChannels);
var secondChannels: {[key: string]: MockEventEmitter<any>} = {};
var uiMessageBusSink = new MockMessageBusSink(secondChannels);
var workerMessageBusSource = new MockMessageBusSource(secondChannels);
return new PairedMessageBuses(
new MockMessageBus(uiMessageBusSink, uiMessageBusSource),
new MockMessageBus(workerMessageBusSink, workerMessageBusSource));
}
/**
* Spies on the given {@link SpyMessageBroker} and expects a call with the given methodName
* andvalues.
* If a handler is provided it will be called to handle the request.
* Only intended to be called on a given broker instance once.
*/
export function expectBrokerCall(
broker: SpyMessageBroker, methodName: string, vals?: Array<any>,
handler?: (..._: any[]) => Promise<any>| void): void {
broker.spy('runOnService').andCallFake((args: UiArguments, returnType: Type<any>) => {
expect(args.method).toEqual(methodName);
if (isPresent(vals)) {
expect(args.args.length).toEqual(vals.length);
ListWrapper.forEachWithIndex(vals, (v, i) => { expect(v).toEqual(args.args[i].value); });
}
var promise: any /** TODO #9100 */ = null;
if (isPresent(handler)) {
let givenValues = args.args.map((arg) => arg.value);
if (givenValues.length > 0) {
promise = handler(givenValues);
} else {
promise = handler();
}
}
if (promise == null) {
promise = new Promise((res, rej) => {
try {
res();
} catch (e) {
rej(e);
}
});
}
return promise;
});
}
export class PairedMessageBuses {
constructor(public ui: MessageBus, public worker: MessageBus) {}
}
export class MockMessageBusSource implements MessageBusSource {
constructor(private _channels: {[key: string]: MockEventEmitter<any>}) {}
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
from(channel: string): MockEventEmitter<any> {
if (!StringMapWrapper.contains(this._channels, channel)) {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
}
return this._channels[channel];
}
attachToZone(zone: NgZone) {}
}
export class MockMessageBusSink implements MessageBusSink {
constructor(private _channels: {[key: string]: MockEventEmitter<any>}) {}
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
to(channel: string): MockEventEmitter<any> {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
return this._channels[channel];
}
attachToZone(zone: NgZone) {}
}
/**
* Mock implementation of the {@link MessageBus} for tests.
* Runs syncronously, and does not support running within the zone.
*/
export class MockMessageBus extends MessageBus {
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { super(); }
initChannel(channel: string, runInZone = true) {
this.sink.initChannel(channel, runInZone);
this.source.initChannel(channel, runInZone);
}
to(channel: string): MockEventEmitter<any> { return this.sink.to(channel); }
from(channel: string): MockEventEmitter<any> { return this.source.from(channel); }
attachToZone(zone: NgZone) {}
}
export class MockMessageBrokerFactory extends ClientMessageBrokerFactory_ {
constructor(private _messageBroker: ClientMessageBroker) { super(null, null); }
createMessageBroker(channel: string, runInZone = true) { return this._messageBroker; }
}

View File

@ -1,93 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {Type} from '@angular/core';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {UiArguments} from '@angular/platform-browser/src/web_workers/shared/client_message_broker';
import {MessageBus} from '@angular/platform-browser/src/web_workers/shared/message_bus';
import {LocationType} from '@angular/platform-browser/src/web_workers/shared/serialized_types';
import {WebWorkerPlatformLocation} from '@angular/platform-browser/src/web_workers/worker/platform_location';
import {MockMessageBrokerFactory, createPairedMessageBuses, expectBrokerCall} from '../shared/web_worker_test_util';
import {SpyMessageBroker} from './spies';
export function main() {
describe('WebWorkerPlatformLocation', () => {
var uiBus: MessageBus = null;
var workerBus: MessageBus = null;
var broker: any = null;
var TEST_LOCATION = new LocationType(
'http://www.example.com', 'http', 'example.com', 'example.com', '80', '/', '', '',
'http://www.example.com');
function createWebWorkerPlatformLocation(loc: LocationType): WebWorkerPlatformLocation {
broker.spy('runOnService').andCallFake((args: UiArguments, returnType: Type<any>) => {
if (args.method === 'getLocation') {
return Promise.resolve(loc);
}
});
var factory = new MockMessageBrokerFactory(broker);
return new WebWorkerPlatformLocation(factory, workerBus, null);
}
function testPushOrReplaceState(pushState: boolean) {
let platformLocation = createWebWorkerPlatformLocation(null);
const TITLE = 'foo';
const URL = 'http://www.example.com/foo';
expectBrokerCall(broker, pushState ? 'pushState' : 'replaceState', [null, TITLE, URL]);
if (pushState) {
platformLocation.pushState(null, TITLE, URL);
} else {
platformLocation.replaceState(null, TITLE, URL);
}
}
beforeEach(() => {
var buses = createPairedMessageBuses();
uiBus = buses.ui;
workerBus = buses.worker;
workerBus.initChannel('ng-Router');
uiBus.initChannel('ng-Router');
broker = new SpyMessageBroker();
});
it('should throw if getBaseHrefFromDOM is called', () => {
let platformLocation = createWebWorkerPlatformLocation(null);
expect(() => platformLocation.getBaseHrefFromDOM()).toThrowError();
});
it('should get location on init', () => {
let platformLocation = createWebWorkerPlatformLocation(null);
expectBrokerCall(broker, 'getLocation');
platformLocation.init();
});
it('should throw if set pathname is called before init finishes', () => {
let platformLocation = createWebWorkerPlatformLocation(null);
platformLocation.init();
expect(() => platformLocation.pathname = 'TEST').toThrowError();
});
it('should send pathname to render thread',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let platformLocation = createWebWorkerPlatformLocation(TEST_LOCATION);
platformLocation.init().then((_) => {
let PATHNAME = '/test';
expectBrokerCall(broker, 'setPathname', [PATHNAME]);
platformLocation.pathname = PATHNAME;
async.done();
});
}));
it('should send pushState to render thread', () => { testPushOrReplaceState(true); });
it('should send replaceState to render thread', () => { testPushOrReplaceState(false); });
});
}

View File

@ -1,220 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {Component, ComponentRef, Injectable, Injector} from '@angular/core';
import {DebugDomRootRenderer} from '@angular/core/src/debug/debug_renderer';
import {RootRenderer} from '@angular/core/src/render/api';
import {TestBed} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {DomRootRenderer, DomRootRenderer_} from '@angular/platform-browser/src/dom/dom_renderer';
import {ClientMessageBrokerFactory, ClientMessageBrokerFactory_} from '@angular/platform-browser/src/web_workers/shared/client_message_broker';
import {RenderStore} from '@angular/platform-browser/src/web_workers/shared/render_store';
import {Serializer} from '@angular/platform-browser/src/web_workers/shared/serializer';
import {ServiceMessageBrokerFactory_} from '@angular/platform-browser/src/web_workers/shared/service_message_broker';
import {MessageBasedRenderer} from '@angular/platform-browser/src/web_workers/ui/renderer';
import {WebWorkerRootRenderer} from '@angular/platform-browser/src/web_workers/worker/renderer';
import {expect} from '@angular/platform-browser/testing/matchers';
import {platformBrowserDynamicTesting} from '../../../../platform-browser-dynamic/testing';
import {dispatchEvent} from '../../../../platform-browser/testing/browser_util';
import {BrowserTestingModule} from '../../../testing';
import {PairedMessageBuses, createPairedMessageBuses} from '../shared/web_worker_test_util';
export function main() {
function createWebWorkerBrokerFactory(
messageBuses: PairedMessageBuses, workerSerializer: Serializer, uiSerializer: Serializer,
domRootRenderer: DomRootRenderer, uiRenderStore: RenderStore): ClientMessageBrokerFactory {
var uiMessageBus = messageBuses.ui;
var workerMessageBus = messageBuses.worker;
// set up the worker side
var webWorkerBrokerFactory =
new ClientMessageBrokerFactory_(workerMessageBus, workerSerializer);
// set up the ui side
var uiMessageBrokerFactory = new ServiceMessageBrokerFactory_(uiMessageBus, uiSerializer);
var renderer = new MessageBasedRenderer(
uiMessageBrokerFactory, uiMessageBus, uiSerializer, uiRenderStore, domRootRenderer);
renderer.start();
return webWorkerBrokerFactory;
}
function createWorkerRenderer(
workerSerializer: Serializer, uiSerializer: Serializer, domRootRenderer: DomRootRenderer,
uiRenderStore: RenderStore, workerRenderStore: RenderStore): RootRenderer {
var messageBuses = createPairedMessageBuses();
var brokerFactory = createWebWorkerBrokerFactory(
messageBuses, workerSerializer, uiSerializer, domRootRenderer, uiRenderStore);
var workerRootRenderer = new WebWorkerRootRenderer(
brokerFactory, messageBuses.worker, workerSerializer, workerRenderStore);
return new DebugDomRootRenderer(workerRootRenderer);
}
describe('Web Worker Renderer', () => {
var uiInjector: Injector;
var uiRenderStore: RenderStore;
var workerRenderStore: RenderStore;
beforeEach(() => {
uiRenderStore = new RenderStore();
var testUiInjector = new TestBed();
testUiInjector.platform = platformBrowserDynamicTesting();
testUiInjector.ngModule = BrowserTestingModule;
testUiInjector.configureTestingModule({
providers: [
Serializer, {provide: RenderStore, useValue: uiRenderStore},
{provide: DomRootRenderer, useClass: DomRootRenderer_},
{provide: RootRenderer, useExisting: DomRootRenderer}
]
});
var uiSerializer = testUiInjector.get(Serializer);
var domRootRenderer = testUiInjector.get(DomRootRenderer);
workerRenderStore = new RenderStore();
TestBed.configureTestingModule({
declarations: [MyComp2],
providers: [
Serializer, {provide: RenderStore, useValue: workerRenderStore}, {
provide: RootRenderer,
useFactory: (workerSerializer: Serializer) => {
return createWorkerRenderer(
workerSerializer, uiSerializer, domRootRenderer, uiRenderStore,
workerRenderStore);
},
deps: [Serializer]
}
]
});
});
function getRenderElement(workerEl: any) {
var id = workerRenderStore.serialize(workerEl);
return uiRenderStore.deserialize(id);
}
function getRenderer(componentRef: ComponentRef<any>) {
return (<any>componentRef.hostView).internalView.renderer;
}
it('should update text nodes', () => {
TestBed.overrideComponent(MyComp2, {set: {template: '<div>{{ctxProp}}</div>'}});
const fixture = TestBed.createComponent(MyComp2);
var renderEl = getRenderElement(fixture.debugElement.nativeElement);
expect(renderEl).toHaveText('');
fixture.debugElement.componentInstance.ctxProp = 'Hello World!';
fixture.detectChanges();
expect(renderEl).toHaveText('Hello World!');
});
it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements',
() => {
TestBed.overrideComponent(
MyComp2, {set: {template: '<input [title]="y" style="position:absolute">'}});
const fixture = TestBed.createComponent(MyComp2);
var checkSetters =
(componentRef: any /** TODO #9100 */, workerEl: any /** TODO #9100 */) => {
var renderer = getRenderer(componentRef);
var el = getRenderElement(workerEl);
renderer.setElementProperty(workerEl, 'tabIndex', 1);
expect((<HTMLInputElement>el).tabIndex).toEqual(1);
renderer.setElementClass(workerEl, 'a', true);
expect(getDOM().hasClass(el, 'a')).toBe(true);
renderer.setElementClass(workerEl, 'a', false);
expect(getDOM().hasClass(el, 'a')).toBe(false);
renderer.setElementStyle(workerEl, 'width', '10px');
expect(getDOM().getStyle(el, 'width')).toEqual('10px');
renderer.setElementStyle(workerEl, 'width', null);
expect(getDOM().getStyle(el, 'width')).toEqual('');
renderer.setElementAttribute(workerEl, 'someattr', 'someValue');
expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue');
};
// root element
checkSetters(fixture.componentRef, fixture.debugElement.nativeElement);
// nested elements
checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement);
});
it('should update any template comment property/attributes', () => {
TestBed.overrideComponent(
MyComp2, {set: {template: '<template [ngIf]="ctxBoolProp"></template>'}});
const fixture = TestBed.createComponent(MyComp2);
(<MyComp2>fixture.debugElement.componentInstance).ctxBoolProp = true;
fixture.detectChanges();
var el = getRenderElement(fixture.debugElement.nativeElement);
expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
});
it('should add and remove fragments', () => {
TestBed.overrideComponent(
MyComp2, {set: {template: '<template [ngIf]="ctxBoolProp">hello</template>'}});
const fixture = TestBed.createComponent(MyComp2);
var rootEl = getRenderElement(fixture.debugElement.nativeElement);
expect(rootEl).toHaveText('');
fixture.debugElement.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
expect(rootEl).toHaveText('hello');
fixture.debugElement.componentInstance.ctxBoolProp = false;
fixture.detectChanges();
expect(rootEl).toHaveText('');
});
if (getDOM().supportsDOMEvents()) {
it('should call actions on the element', () => {
TestBed.overrideComponent(MyComp2, {set: {template: '<input [title]="y">'}});
const fixture = TestBed.createComponent(MyComp2);
var el = fixture.debugElement.children[0];
getRenderer(fixture.componentRef).invokeElementMethod(el.nativeElement, 'setAttribute', [
'a', 'b'
]);
expect(getDOM().getAttribute(getRenderElement(el.nativeElement), 'a')).toEqual('b');
});
it('should listen to events', () => {
TestBed.overrideComponent(MyComp2, {set: {template: '<input (change)="ctxNumProp = 1">'}});
const fixture = TestBed.createComponent(MyComp2);
var el = fixture.debugElement.children[0];
dispatchEvent(getRenderElement(el.nativeElement), 'change');
expect(fixture.componentInstance.ctxNumProp).toBe(1);
fixture.destroy();
});
}
});
}
@Component({selector: 'my-comp'})
@Injectable()
class MyComp2 {
ctxProp: string;
ctxNumProp: any /** TODO #9100 */;
ctxBoolProp: boolean;
constructor() {
this.ctxProp = 'initial value';
this.ctxNumProp = 0;
this.ctxBoolProp = false;
}
throwError() { throw 'boom'; }
}

View File

@ -1,14 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {SpyObject, proxy} from '@angular/core/testing/testing_internal';
import {ClientMessageBroker} from '@angular/platform-browser/src/web_workers/shared/client_message_broker';
export class SpyMessageBroker extends SpyObject {
constructor() { super(ClientMessageBroker); }
}