fix(WebWorker): Add zone support to MessageBus

Closes #4053
This commit is contained in:
Jason Teplitz
2015-09-08 10:52:06 -07:00
parent 3b9c08676a
commit f3da37c92f
35 changed files with 628 additions and 365 deletions
@@ -10,9 +10,11 @@ import {
SpyObject,
proxy
} from 'angular2/test_lib';
import {ObservableWrapper} from 'angular2/src/core/facade/async';
import {ObservableWrapper, TimerWrapper} from 'angular2/src/core/facade/async';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {createConnectedMessageBus} from './message_bus_util';
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
export function main() {
/**
@@ -27,6 +29,7 @@ export function main() {
inject([AsyncTestCompleter], (async) => {
const CHANNEL = "CHANNEL 1";
const MESSAGE = "Test message";
bus.initChannel(CHANNEL, false);
var fromEmitter = bus.from(CHANNEL);
ObservableWrapper.subscribe(fromEmitter, (message: any) => {
@@ -41,6 +44,7 @@ export function main() {
const CHANNEL = "CHANNEL 1";
const MESSAGE = "TESTING";
const NUM_LISTENERS = 2;
bus.initChannel(CHANNEL, false);
var callCount = 0;
var emitHandler = (message: any) => {
@@ -66,6 +70,8 @@ export function main() {
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);
ObservableWrapper.subscribe(firstFromEmitter, (message) => {
@@ -91,4 +97,55 @@ export function main() {
ObservableWrapper.callNext(secondToEmitter, 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.
*/
function flushMessages(fn: () => void) { TimerWrapper.setTimeout(fn, 10); }
beforeEach(() => { bus = createConnectedMessageBus(); });
it("should buffer messages and wait for the zone to exit before sending",
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
setup(true, zone);
var wasCalled = false;
ObservableWrapper.subscribe(bus.from(CHANNEL), (message) => { wasCalled = true; });
ObservableWrapper.callNext(bus.to(CHANNEL), "hi");
flushMessages(() => {
expect(wasCalled).toBeFalsy();
zone.simulateZoneExit();
flushMessages(() => {
expect(wasCalled).toBeTruthy();
async.done();
});
});
}));
it("should send messages immediatly when run outside the zone",
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
setup(false, zone);
var wasCalled = false;
ObservableWrapper.subscribe(bus.from(CHANNEL), (message) => { wasCalled = true; });
ObservableWrapper.callNext(bus.to(CHANNEL), "hi");
flushMessages(() => {
expect(wasCalled).toBeTruthy();
async.done();
});
}));
});
}
@@ -39,7 +39,11 @@ export function main() {
describe("UIMessageBroker", () => {
var messageBuses;
beforeEach(() => { messageBuses = createPairedMessageBuses(); });
beforeEach(() => {
messageBuses = createPairedMessageBuses();
messageBuses.ui.initChannel(CHANNEL);
messageBuses.worker.initChannel(CHANNEL);
});
it("should call registered method with correct arguments",
inject([Serializer], (serializer) => {
var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL);
@@ -5,6 +5,8 @@ import {
MessageBus
} from 'angular2/src/web_workers/shared/message_bus';
import {MockEventEmitter} from './mock_event_emitter';
import {BaseException} from 'angular2/src/core/facade/lang';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
/**
* Returns two MessageBus instances that are attached to each other.
@@ -30,30 +32,56 @@ export class PairedMessageBuses {
export class MockMessageBusSource implements MessageBusSource {
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
from(channel: string): MockEventEmitter {
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
from(channel: string): MockEventEmitter {
if (!StringMapWrapper.contains(this._channels, channel)) {
throw new BaseException(`${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: StringMap<string, MockEventEmitter>) {}
initChannel(channel: string, runInZone = true) {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
}
to(channel: string): MockEventEmitter {
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 { return this.sink.to(channel); }
from(channel: string): MockEventEmitter { return this.source.from(channel); }
attachToZone(zone: NgZone) {}
}