Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
feat(ProxySpec): create a ProxySpec which can proxy to other ZoneSpecs.
Browse files Browse the repository at this point in the history
This allows changing of the zone behavior ofter it has been created.
  • Loading branch information
IgorMinar authored and mhevery committed Aug 15, 2016
1 parent dafad98 commit 2d02e39
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 0 deletions.
10 changes: 10 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ gulp.task('build/long-stack-trace-zone.min.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/long-stack-trace.ts', 'long-stack-trace-zone.min.js', true, cb);
});

gulp.task('build/proxy-zone.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.js', false, cb);
});

gulp.task('build/proxy-zone.min.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.min.js', true, cb);
});

gulp.task('build/wtf.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/wtf.ts', 'wtf.js', false, cb);
});
Expand Down Expand Up @@ -126,6 +134,8 @@ gulp.task('build', [
'build/jasmine-patch.min.js',
'build/long-stack-trace-zone.js',
'build/long-stack-trace-zone.min.js',
'build/proxy-zone.js',
'build/proxy-zone.min.js',
'build/wtf.js',
'build/wtf.min.js',
'build/async-test.js',
Expand Down
128 changes: 128 additions & 0 deletions lib/zone-spec/proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
(function () {
class ProxyZoneSpec implements ZoneSpec {
name: string = 'ProxyZone';

private _delegateSpec: ZoneSpec;

properties: {[k: string]: any} = {'ProxyZoneSpec': this};
propertyKeys: string[] = null;

static get(): ProxyZoneSpec {
return Zone.current.get('ProxyZoneSpec');
}

static isLoaded(): boolean {
return ProxyZoneSpec.get() instanceof ProxyZoneSpec;
}

static assertPresent(): ProxyZoneSpec {
if (!this.isLoaded()) {
throw new Error(`Expected to be running in 'ProxyZone', but it was not found.`);
}
return ProxyZoneSpec.get();
}

constructor(private defaultSpecDelegate: ZoneSpec = null) {
this.setDelegate(defaultSpecDelegate);
}


setDelegate(delegateSpec: ZoneSpec) {
this._delegateSpec = delegateSpec;
this.propertyKeys && this.propertyKeys.forEach((key) => delete this.properties[key]);
this.propertyKeys = null;
if (delegateSpec && delegateSpec.properties) {
this.propertyKeys = Object.keys(delegateSpec.properties);
this.propertyKeys.forEach((k) => this.properties[k] = delegateSpec.properties[k]);
}
}

getDelegate() {
return this._delegateSpec;
}


resetDelegate() {
this.setDelegate(this.defaultSpecDelegate);
}


onFork(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
zoneSpec: ZoneSpec): Zone {
if (this._delegateSpec && this._delegateSpec.onFork) {
return this._delegateSpec.onFork(parentZoneDelegate, currentZone, targetZone, zoneSpec);
} else {
return parentZoneDelegate.fork(targetZone, zoneSpec);
}
}


onIntercept(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
delegate: Function, source: string): Function {
if (this._delegateSpec && this._delegateSpec.onIntercept) {
return this._delegateSpec.onIntercept(parentZoneDelegate, currentZone, targetZone, delegate, source);
} else {
return parentZoneDelegate.intercept(targetZone, delegate, source);
}
}


onInvoke(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
delegate: Function, applyThis: any, applyArgs: any[], source: string): any {
if (this._delegateSpec && this._delegateSpec.onInvoke) {
return this._delegateSpec.onInvoke(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source);
} else {
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
}
}

onHandleError(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
error: any): boolean {
if (this._delegateSpec && this._delegateSpec.onHandleError) {
return this._delegateSpec.onHandleError(parentZoneDelegate, currentZone, targetZone, error);
} else {
return parentZoneDelegate.handleError(targetZone, error);
}
}

onScheduleTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task): Task {
if (this._delegateSpec && this._delegateSpec.onScheduleTask) {
return this._delegateSpec.onScheduleTask(parentZoneDelegate, currentZone, targetZone, task);
} else {
return parentZoneDelegate.scheduleTask(targetZone, task);
}
}

onInvokeTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task, applyThis: any, applyArgs: any): any {
if (this._delegateSpec && this._delegateSpec.onFork) {
return this._delegateSpec.onInvokeTask(parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs);
} else {
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
}
}

onCancelTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task): any {
if (this._delegateSpec && this._delegateSpec.onCancelTask) {
return this._delegateSpec.onCancelTask(parentZoneDelegate, currentZone, targetZone, task);
} else {
return parentZoneDelegate.cancelTask(targetZone, task);
}
}

onHasTask(delegate: ZoneDelegate, current: Zone, target: Zone,
hasTaskState: HasTaskState): void {
if (this._delegateSpec && this._delegateSpec.onHasTask) {
this._delegateSpec.onHasTask(delegate, current, target, hasTaskState);
} else {
delegate.hasTask(target, hasTaskState);
}
}
}

// Export the class so that new instances can be created with proper
// constructor params.
Zone['ProxyZoneSpec'] = ProxyZoneSpec;
})();
1 change: 1 addition & 0 deletions test/browser_entry_point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import '../lib/zone';
import '../lib/browser/browser.ts';
import '../lib/zone-spec/long-stack-trace';
import '../lib/zone-spec/wtf';
import '../lib/zone-spec/proxy';

// Setup test environment
import './test-env-setup';
Expand Down
1 change: 1 addition & 0 deletions test/common_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ import './zone-spec/long-stack-trace-zone.spec';
import './zone-spec/async-test.spec';
import './zone-spec/sync-test.spec';
import './zone-spec/fake-async-test.spec';
import './zone-spec/proxy.spec';
130 changes: 130 additions & 0 deletions test/zone-spec/proxy.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import '../../lib/zone-spec/proxy';

describe('ProxySpec', () => {
let ProxyZoneSpec: any;
let delegate: ZoneSpec;
let proxyZoneSpec: any;
let proxyZone: Zone;

beforeEach(() => {
ProxyZoneSpec = Zone['ProxyZoneSpec'];
expect(typeof ProxyZoneSpec).toBe('function');
delegate = {name: 'delegate'};
proxyZoneSpec = new ProxyZoneSpec(delegate);
proxyZone = Zone.current.fork(proxyZoneSpec);
});

describe('properties', () => {
it('should expose ProxyZone in the properties', () => {
expect(proxyZone.get('ProxyZoneSpec')).toBe(proxyZoneSpec);
});

it('should assert that it is in or out of ProxyZone', () => {
expect(() => ProxyZoneSpec.assertPresent()).toThrow();
expect(ProxyZoneSpec.isLoaded()).toBe(false);
expect(ProxyZoneSpec.get()).toBe(undefined);
proxyZone.run(() => {
expect(ProxyZoneSpec.isLoaded()).toBe(true);
expect(() => ProxyZoneSpec.assertPresent()).not.toThrow();
expect(ProxyZoneSpec.get()).toBe(proxyZoneSpec);
});
});

it('should reset properties', () => {
expect(proxyZone.get('myTestKey')).toBe(undefined);
proxyZoneSpec.setDelegate({name: 'd1', properties: {'myTestKey': 'myTestValue'}});
expect(proxyZone.get('myTestKey')).toBe('myTestValue');
proxyZoneSpec.resetDelegate();
expect(proxyZone.get('myTestKey')).toBe(undefined);
});
});

describe('delegate', () => {
it('should set/reset delegate', () => {
const defaultDelegate: ZoneSpec = {name: 'defaultDelegate'};
const otherDelegate: ZoneSpec = {name: 'otherDelegate'};
const proxyZoneSpec = new ProxyZoneSpec(defaultDelegate);
const proxyZone = Zone.current.fork(proxyZoneSpec);

expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate);

proxyZoneSpec.setDelegate(otherDelegate);
expect(proxyZoneSpec.getDelegate()).toEqual(otherDelegate);
proxyZoneSpec.resetDelegate();
expect(proxyZoneSpec.getDelegate()).toEqual(defaultDelegate);
});
});

describe('forwarding', () => {
beforeEach(() => {
proxyZoneSpec = new ProxyZoneSpec();
proxyZone = Zone.current.fork(proxyZoneSpec);
});

it('should fork', () => {
const forkeZone = proxyZone.fork({name: 'fork'});
expect(forkeZone).not.toBe(proxyZone);
expect(forkeZone.name).toBe('fork');
var called = false;
proxyZoneSpec.setDelegate({
name: '.',
onFork: (parentZoneDelegate, currentZone, targetZone, zoneSpec) => {
expect(currentZone).toBe(proxyZone);
expect(targetZone).toBe(proxyZone),
expect(zoneSpec.name).toBe('fork2');
called = true;
}
});
proxyZone.fork({name: 'fork2'});
expect(called).toBe(true);
});

it('should intercept', () => {
const fn = (a) => a;
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('works');
proxyZoneSpec.setDelegate({
name: '.',
onIntercept: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
delegate: Function, source: string): Function => {
return () => '(works)';
}
});
expect(proxyZone.wrap(fn, 'test')('works')).toEqual('(works)');
});

it('should invoke', () => {
const fn = () => 'works';
expect(proxyZone.run(fn)).toEqual('works');
proxyZoneSpec.setDelegate({
name: '.',
onInvoke: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
delegate: Function, applyThis: any, applyArgs: any[], source: string) => {
return `(${parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source)})`;
}
});
expect(proxyZone.run(fn)).toEqual('(works)');
});

it('should handleError', () => {
const error = new Error("TestError");
const fn = () => { throw error };
expect(() => proxyZone.run(fn)).toThrow(error);
proxyZoneSpec.setDelegate({
name: '.',
onHandleError: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
error: any): boolean => {
expect(error).toEqual(error);
return false;
}
});
expect(() => proxyZone.runGuarded(fn)).not.toThrow();
});

it('should Task', () => {
const fn = () => null;
const task = proxyZone.scheduleMacroTask('test', fn, {}, () => null, () => null);
expect(task.source).toEqual('test');
proxyZone.cancelTask(task);
});
});
});

0 comments on commit 2d02e39

Please sign in to comment.