refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* @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, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {MessageBus} from '@angular/platform-webworker/src/web_workers/shared/message_bus';
|
||||
|
||||
import {createConnectedMessageBus} from './message_bus_util';
|
||||
|
||||
export function main() {
|
||||
/**
|
||||
* Tests the PostMessageBus
|
||||
*/
|
||||
describe('MessageBus', () => {
|
||||
let 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);
|
||||
|
||||
const fromEmitter = bus.from(CHANNEL);
|
||||
fromEmitter.subscribe({
|
||||
next: (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
const 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);
|
||||
|
||||
let callCount = 0;
|
||||
const emitHandler = (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
callCount++;
|
||||
if (callCount == NUM_LISTENERS) {
|
||||
async.done();
|
||||
}
|
||||
};
|
||||
|
||||
for (let i = 0; i < NUM_LISTENERS; i++) {
|
||||
const emitter = bus.from(CHANNEL);
|
||||
emitter.subscribe({next: emitHandler});
|
||||
}
|
||||
|
||||
const 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';
|
||||
let callCount = 0;
|
||||
bus.initChannel(CHANNEL_ONE, false);
|
||||
bus.initChannel(CHANNEL_TWO, false);
|
||||
|
||||
const firstFromEmitter = bus.from(CHANNEL_ONE);
|
||||
firstFromEmitter.subscribe({
|
||||
next: (message: any) => {
|
||||
expect(message).toEqual(MESSAGE_ONE);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
const secondFromEmitter = bus.from(CHANNEL_TWO);
|
||||
secondFromEmitter.subscribe({
|
||||
next: (message: any) => {
|
||||
expect(message).toEqual(MESSAGE_TWO);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const firstToEmitter = bus.to(CHANNEL_ONE);
|
||||
firstToEmitter.emit(MESSAGE_ONE);
|
||||
|
||||
const secondToEmitter = bus.to(CHANNEL_TWO);
|
||||
secondToEmitter.emit(MESSAGE_TWO);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('PostMessageBusSink', () => {
|
||||
let 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);
|
||||
|
||||
let 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);
|
||||
|
||||
let wasCalled = false;
|
||||
bus.from(CHANNEL).subscribe({next: (message: any) => { wasCalled = true; }});
|
||||
bus.to(CHANNEL).emit('hi');
|
||||
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
}), 10000);
|
||||
});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @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-webworker/src/web_workers/shared/message_bus';
|
||||
import {PostMessageBus, PostMessageBusSink, PostMessageBusSource} from '@angular/platform-webworker/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 {
|
||||
const mockPostMessage = new MockPostMessage();
|
||||
const source = new PostMessageBusSource(<any>mockPostMessage);
|
||||
const 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}); }
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @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 '@angular/core';
|
||||
|
||||
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 {}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @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 {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store';
|
||||
|
||||
export function main() {
|
||||
describe('RenderStoreSpec', () => {
|
||||
let store: RenderStore;
|
||||
beforeEach(() => { store = new RenderStore(); });
|
||||
|
||||
it('should allocate ids', () => {
|
||||
expect(store.allocateId()).toBe(0);
|
||||
expect(store.allocateId()).toBe(1);
|
||||
});
|
||||
|
||||
it('should serialize objects', () => {
|
||||
const id = store.allocateId();
|
||||
const obj = 'testObject';
|
||||
store.store(obj, id);
|
||||
expect(store.serialize(obj)).toBe(id);
|
||||
});
|
||||
|
||||
it('should deserialize objects', () => {
|
||||
const id = store.allocateId();
|
||||
const obj = 'testObject';
|
||||
store.store(obj, id);
|
||||
expect(store.deserialize(id)).toBe(obj);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @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, inject, it} from '@angular/core/testing/testing_internal';
|
||||
import {ON_WEB_WORKER} from '@angular/platform-webworker/src/web_workers/shared/api';
|
||||
import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store';
|
||||
import {Serializer, SerializerTypes} from '@angular/platform-webworker/src/web_workers/shared/serializer';
|
||||
import {ServiceMessageBroker_} from '@angular/platform-webworker/src/web_workers/shared/service_message_broker';
|
||||
|
||||
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', () => {
|
||||
let 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) => {
|
||||
const broker = new ServiceMessageBroker_(messageBuses.ui, serializer, CHANNEL);
|
||||
broker.registerMethod(
|
||||
TEST_METHOD, [SerializerTypes.PRIMITIVE, SerializerTypes.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) => {
|
||||
const broker = new ServiceMessageBroker_(messageBuses.ui, serializer, CHANNEL);
|
||||
broker.registerMethod(TEST_METHOD, [SerializerTypes.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);
|
||||
},
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @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-webworker/src/web_workers/shared/client_message_broker';
|
||||
import {MessageBus, MessageBusSink, MessageBusSource} from '@angular/platform-webworker/src/web_workers/shared/message_bus';
|
||||
import {SpyMessageBroker} from '../worker/spies';
|
||||
|
||||
import {MockEventEmitter} from './mock_event_emitter';
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
const firstChannels: {[key: string]: MockEventEmitter<any>} = {};
|
||||
const workerMessageBusSink = new MockMessageBusSink(firstChannels);
|
||||
const uiMessageBusSource = new MockMessageBusSource(firstChannels);
|
||||
|
||||
const secondChannels: {[key: string]: MockEventEmitter<any>} = {};
|
||||
const uiMessageBusSink = new MockMessageBusSink(secondChannels);
|
||||
const 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').and.callFake((args: UiArguments, returnType: Type<any>) => {
|
||||
expect(args.method).toEqual(methodName);
|
||||
if (vals != null) {
|
||||
expect(args.args.length).toEqual(vals.length);
|
||||
vals.forEach((v, i) => { expect(v).toEqual(args.args[i].value); });
|
||||
}
|
||||
let promise: Promise<any>|void = null;
|
||||
if (handler != null) {
|
||||
const 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 (!this._channels.hasOwnProperty(channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
}
|
||||
|
||||
from(channel: string): MockEventEmitter<any> {
|
||||
if (!this._channels.hasOwnProperty(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 (!this._channels.hasOwnProperty(channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
}
|
||||
|
||||
to(channel: string): MockEventEmitter<any> {
|
||||
if (!this._channels.hasOwnProperty(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; }
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @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 {UiArguments} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker';
|
||||
import {MessageBus} from '@angular/platform-webworker/src/web_workers/shared/message_bus';
|
||||
import {LocationType, SerializerTypes} from '@angular/platform-webworker/src/web_workers/shared/serializer';
|
||||
import {WebWorkerPlatformLocation} from '@angular/platform-webworker/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', () => {
|
||||
let uiBus: MessageBus = null;
|
||||
let workerBus: MessageBus = null;
|
||||
let broker: any = null;
|
||||
|
||||
const 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')
|
||||
.and.callFake((args: UiArguments, returnType: Type<any>| SerializerTypes) => {
|
||||
if (args.method === 'getLocation') {
|
||||
return Promise.resolve(loc);
|
||||
}
|
||||
});
|
||||
const factory = new MockMessageBrokerFactory(broker);
|
||||
return new WebWorkerPlatformLocation(factory, workerBus, null);
|
||||
}
|
||||
|
||||
function testPushOrReplaceState(pushState: boolean) {
|
||||
const 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(() => {
|
||||
const 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', () => {
|
||||
const platformLocation = createWebWorkerPlatformLocation(null);
|
||||
expect(() => platformLocation.getBaseHrefFromDOM()).toThrowError();
|
||||
});
|
||||
|
||||
it('should get location on init', () => {
|
||||
const platformLocation = createWebWorkerPlatformLocation(null);
|
||||
expectBrokerCall(broker, 'getLocation');
|
||||
platformLocation.init();
|
||||
});
|
||||
|
||||
it('should throw if set pathname is called before init finishes', () => {
|
||||
const platformLocation = createWebWorkerPlatformLocation(null);
|
||||
platformLocation.init();
|
||||
expect(() => platformLocation.pathname = 'TEST').toThrowError();
|
||||
});
|
||||
|
||||
it('should send pathname to render thread', done => {
|
||||
const platformLocation = createWebWorkerPlatformLocation(TEST_LOCATION);
|
||||
platformLocation.init().then((_) => {
|
||||
const PATHNAME = '/test';
|
||||
expectBrokerCall(broker, 'setPathname', [PATHNAME]);
|
||||
platformLocation.pathname = PATHNAME;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send pushState to render thread', () => { testPushOrReplaceState(true); });
|
||||
|
||||
it('should send replaceState to render thread', () => { testPushOrReplaceState(false); });
|
||||
});
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* @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, Renderer2, RendererFactory2, RendererType2, RootRenderer} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||
import {BrowserTestingModule} from '@angular/platform-browser/testing';
|
||||
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {ClientMessageBrokerFactory, ClientMessageBrokerFactory_} from '../../../src/web_workers/shared/client_message_broker';
|
||||
import {RenderStore} from '../../../src/web_workers/shared/render_store';
|
||||
import {Serializer} from '../../../src/web_workers/shared/serializer';
|
||||
import {ServiceMessageBrokerFactory_} from '../../../src/web_workers/shared/service_message_broker';
|
||||
import {MessageBasedRenderer2} from '../../../src/web_workers/ui/renderer';
|
||||
import {WebWorkerRendererFactory2} from '../../../src/web_workers/worker/renderer';
|
||||
import {PairedMessageBuses, createPairedMessageBuses} from '../shared/web_worker_test_util';
|
||||
|
||||
let lastCreatedRenderer: Renderer2;
|
||||
|
||||
export function main() {
|
||||
describe('Web Worker Renderer v2', () => {
|
||||
// Don't run on server...
|
||||
if (!getDOM().supportsDOMEvents()) return;
|
||||
|
||||
let uiRenderStore: RenderStore;
|
||||
let wwRenderStore: RenderStore;
|
||||
|
||||
beforeEach(() => {
|
||||
// UI side
|
||||
uiRenderStore = new RenderStore();
|
||||
const uiInjector = new TestBed();
|
||||
uiInjector.platform = platformBrowserDynamicTesting();
|
||||
uiInjector.ngModule = BrowserTestingModule;
|
||||
uiInjector.configureTestingModule({
|
||||
providers: [
|
||||
Serializer,
|
||||
{provide: RenderStore, useValue: uiRenderStore},
|
||||
DomRendererFactory2,
|
||||
{provide: RendererFactory2, useExisting: DomRendererFactory2},
|
||||
]
|
||||
});
|
||||
const uiSerializer = uiInjector.get(Serializer);
|
||||
const domRendererFactory = uiInjector.get(RendererFactory2);
|
||||
|
||||
// Worker side
|
||||
lastCreatedRenderer = null;
|
||||
|
||||
wwRenderStore = new RenderStore();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp2],
|
||||
providers: [
|
||||
Serializer,
|
||||
{provide: RenderStore, useValue: wwRenderStore},
|
||||
{
|
||||
provide: RendererFactory2,
|
||||
useFactory:
|
||||
(wwSerializer: Serializer) => createWebWorkerRendererFactory2(
|
||||
wwSerializer, uiSerializer, domRendererFactory, uiRenderStore, wwRenderStore),
|
||||
deps: [Serializer],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
function getRenderElement(workerEl: any): any {
|
||||
const id = wwRenderStore.serialize(workerEl);
|
||||
return uiRenderStore.deserialize(id);
|
||||
}
|
||||
|
||||
it('should update text nodes', () => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<div>{{ctxProp}}</div>').createComponent(MyComp2);
|
||||
const renderEl = getRenderElement(fixture.nativeElement);
|
||||
expect(renderEl).toHaveText('');
|
||||
|
||||
fixture.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',
|
||||
() => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<input [title]="y" style="position:absolute">')
|
||||
.createComponent(MyComp2);
|
||||
|
||||
const checkSetters = (componentRef: ComponentRef<any>, workerEl: any) => {
|
||||
expect(lastCreatedRenderer).not.toEqual(null);
|
||||
|
||||
const el = getRenderElement(workerEl);
|
||||
lastCreatedRenderer.setProperty(workerEl, 'tabIndex', 1);
|
||||
expect(el.tabIndex).toEqual(1);
|
||||
|
||||
lastCreatedRenderer.addClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(true);
|
||||
|
||||
lastCreatedRenderer.removeClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(false);
|
||||
|
||||
lastCreatedRenderer.setStyle(workerEl, 'width', '10px', false, false);
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('10px');
|
||||
|
||||
lastCreatedRenderer.removeStyle(workerEl, 'width', false);
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('');
|
||||
|
||||
lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue');
|
||||
expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue');
|
||||
};
|
||||
|
||||
// root element
|
||||
checkSetters(fixture.componentRef, fixture.nativeElement);
|
||||
// nested elements
|
||||
checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement);
|
||||
});
|
||||
|
||||
it('should update any template comment property/attributes', () => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<ng-container *ngIf="ctxBoolProp"></ng-container>')
|
||||
.createComponent(MyComp2);
|
||||
fixture.componentInstance.ctxBoolProp = true;
|
||||
fixture.detectChanges();
|
||||
const el = getRenderElement(fixture.nativeElement);
|
||||
expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
|
||||
});
|
||||
|
||||
it('should add and remove fragments', () => {
|
||||
const fixture =
|
||||
TestBed
|
||||
.overrideTemplate(MyComp2, '<ng-container *ngIf="ctxBoolProp">hello</ng-container>')
|
||||
.createComponent(MyComp2);
|
||||
|
||||
const rootEl = getRenderElement(fixture.nativeElement);
|
||||
expect(rootEl).toHaveText('');
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = true;
|
||||
fixture.detectChanges();
|
||||
expect(rootEl).toHaveText('hello');
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = false;
|
||||
fixture.detectChanges();
|
||||
expect(rootEl).toHaveText('');
|
||||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
it('should listen to events', () => {
|
||||
const fixture = TestBed.overrideTemplate(MyComp2, '<input (change)="ctxNumProp = 1">')
|
||||
.createComponent(MyComp2);
|
||||
|
||||
const el = fixture.debugElement.children[0];
|
||||
dispatchEvent(getRenderElement(el.nativeElement), 'change');
|
||||
expect(fixture.componentInstance.ctxNumProp).toBe(1);
|
||||
|
||||
fixture.destroy();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
class MyComp2 {
|
||||
ctxProp = 'initial value';
|
||||
ctxNumProp = 0;
|
||||
ctxBoolProp = false;
|
||||
}
|
||||
|
||||
function createWebWorkerBrokerFactory(
|
||||
messageBuses: PairedMessageBuses, wwSerializer: Serializer, uiSerializer: Serializer,
|
||||
domRendererFactory: DomRendererFactory2,
|
||||
uiRenderStore: RenderStore): ClientMessageBrokerFactory {
|
||||
const uiMessageBus = messageBuses.ui;
|
||||
const wwMessageBus = messageBuses.worker;
|
||||
|
||||
// set up the worker side
|
||||
const wwBrokerFactory = new ClientMessageBrokerFactory_(wwMessageBus, wwSerializer);
|
||||
|
||||
// set up the ui side
|
||||
const uiBrokerFactory = new ServiceMessageBrokerFactory_(uiMessageBus, uiSerializer);
|
||||
const renderer = new MessageBasedRenderer2(
|
||||
uiBrokerFactory, uiMessageBus, uiSerializer, uiRenderStore, domRendererFactory);
|
||||
renderer.start();
|
||||
|
||||
return wwBrokerFactory;
|
||||
}
|
||||
|
||||
function createWebWorkerRendererFactory2(
|
||||
workerSerializer: Serializer, uiSerializer: Serializer, domRendererFactory: DomRendererFactory2,
|
||||
uiRenderStore: RenderStore, workerRenderStore: RenderStore): RendererFactory2 {
|
||||
const messageBuses = createPairedMessageBuses();
|
||||
const brokerFactory = createWebWorkerBrokerFactory(
|
||||
messageBuses, workerSerializer, uiSerializer, domRendererFactory, uiRenderStore);
|
||||
|
||||
const rendererFactory =
|
||||
new RenderFactory(brokerFactory, messageBuses.worker, workerSerializer, workerRenderStore);
|
||||
|
||||
return rendererFactory;
|
||||
}
|
||||
|
||||
class RenderFactory extends WebWorkerRendererFactory2 {
|
||||
createRenderer(element: any, type: RendererType2): Renderer2 {
|
||||
lastCreatedRenderer = super.createRenderer(element, type);
|
||||
return lastCreatedRenderer;
|
||||
}
|
||||
}
|
14
packages/platform-webworker/test/web_workers/worker/spies.ts
Normal file
14
packages/platform-webworker/test/web_workers/worker/spies.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @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} from '@angular/core/testing/testing_internal';
|
||||
import {ClientMessageBroker} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker';
|
||||
|
||||
export class SpyMessageBroker extends SpyObject {
|
||||
constructor() { super(ClientMessageBroker); }
|
||||
}
|
Reference in New Issue
Block a user