@JiaLiPassion
A Zone is an execution context that persists across async tasks.
Created by Brian Ford inspired by Dart.
Provide execution context
Provide async task lifecycle interceptable hook
Provide async error handler
Zone.current.fork({name: 'a'})
.run(function() {
setTimeout(function () {
// Zone.current is a
}, 100);
setTimeout(function() {
// Zone.current is a
}, 200);
});
function outside() {
// Execution context of outside
function inside1() {
// Execution context of inside1
}
function inside2() {
// Execution context of inside2
}
}
setTimeout(function() {
console.log('callback invoked');
}, 1000);
var zone = Zone.current.fork({
name: 'hook',
onScheduleTask: ...,
onInvokeTask: ....,
onHasTask: ...
});
zone.run(() => {
setTimeout(() => {
console.log('timer callback invoked');
}, 100);
});
https://zone-ebb17.firebaseapp.com/
Zone.current.fork(
{
name: 'error',
onHandleError: (delegate, curr, target, error) => {
logger.error(error);
return delegate.handleError(target, error);
}
}).runGuarded(() => {
setTimeout(() => {
throw new Error('timeout error');
});
setTimeout(() => {
throw new Error('another timeout error');
});
});
const nativeTimeout = window.setTimeout;
window.setTimeout = function(callback) {
const zone = Zone.current;
const wrappedCallback = function() {
zone.onInvokeTask('setTimeout'); // invoke hook
return callback.apply(this, arguments);
}
zone.onScheduleTask('setTimeout'); // schedule hook
nativeTimeout.apply(this, wrappedCallback);
}
Change Detection
AsyncTest
FakeAsyncTest
SyncTest
Error Handling
Debug/Tracing
DOM API
setTimeout
Promise
EventTarget
...
Zone.js
Monkey-Patch
Angular
Timer
Promise
EventTarget
requestAnimationFrame
...
Notification
MediaQuery
UserMedia
...
requestAnimationFrame
__Zone_disable_requestAnimationFrame=true
__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove'];
@HostListener('window:mousemove.nozone')
mouseMoveListener() {
// will not in ngZone.
}
<div (scroll.nozone)="scrollHandler();"></div>
Proposal?
declare let Zone: any;
(window as any)[Zone.__symbol__('setTimeout')](() => {
// will not in zone.
}, 100);
import 'zone.js/dist/zone';
import 'core-js/promise';
import 'zone.js/dist/zone-error';
import 'zone.js/dist/zone-error';
class MyError extends Error {
}
try {
throw new MyError('myError');
} catch (error: Error) {
if (error instanceof MyError) {
// do some special error handling
}
}
(window as any).__Zone_Error_BlacklistedStackFrames_policy = 'disable'; // 'default', 'lazy'
import 'zone.js/dist/zone-mix';
import 'zone.js/dist/zone-patch-electron';
MediaQuery
Notification
RTCPeerConnction
ShadyDom
Cordova
ResizeObserver
SocketIO
UserMedia
Jsonp Helper
Zone.js can be a standalone test library.
import 'zone.js/dist/long-stack-trace';
import 'zone.js/dist/proxy';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async';
import 'zone.js/dist/promise-testing';
import 'zone.js/dist/zone-testing';
import 'zone.js/dist/zone-testing';
describe('zone-testing', () => {
it('fakeAsync', fakeAsyncTest(() => {
tick(100);
discardPeriodicTasks();
flushMicrotasks();
});
it ('async', asyncTest(() => {}));
});
it('should advance Date when tick in fakeAsync', fakeAsync(() => {
const start = Date.now();
tick(100);
const end = Date.now();
expect(end - start).toBe(100);
}));
beforeEach(() => {
jasmine.clock().install();
});
afterEach(() => {
jasmine.clock().uninstall();
});
it('should get date diff correctly', () => { // we don't need fakeAsync here.
// automatically run into fake async zone, because jasmine.clock() is installed.
const start = Date.now();
jasmine.clock().tick(100);
const end = Date.now();
expect(end - start).toBe(100);
});
__zone_symbol__fakeAsyncPatchLock
it('rxjs scheduler should be advanced by tick in fakeAsync', fakeAsync(() => {
observable.delay(1000).subscribe(v => {
result = v;
});
expect(result).toBeNull();
testZoneSpec.tick(1000);
expect(result).toBe('hello');
}));
it('should timeout', async(() => {
setTimeout(() => {}, 10000);
fixture.whenStable(() => {});
}));
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
--Pendng async tasks are: [type: macroTask, source: setTimeout, args: {handleId:3,isPeriodic:false,delay:10000,args:[]}
describe('BDD style', () => {});
xdescribe('BDD style', () => {});
suite('mocha style', () => {});
describe('', () => {
beforeEach(() => {});
suiteSetup(() => {});
before(() => {});
beforeAll(() => {});
});
describe('', () => {
it('case', () => {});
xit('case', () => {});
it.skip('mocha skip', () => {});
it.only('mocha only', () => {});
specify('mocha specify', () => {});
});
Jasmine | Mocha | Jest |
---|---|---|
beforeAll | before | beforeAll |
afterAll | after | afterAll |
xdescribe | describe.skip | describe.skip |
fdescribe | describe.only | describe.only |
xit | it.skip | it.skip |
fit | it.only | it.only |
Mocha BDD I/F
Specify, suite, suiteSetup, suiteTearDown...
it ('expect', () => {
expect(a).toEqual('test'); // jasmine
expect(b).toBeInstanceOf(BType); // jest
expect(Promise.resolve('lemon')).resolves.toBe('lemon'); // jest
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(expect.arrayContaining(expected)); // jest
const onPress = {x: 100, y: 200, z: 300};
expect(onPress).toEqual(expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}), ); // jest
// ...
});
it ('expect', async() => {
const spy = jasmine.createSpy();
const spy1 = spyOn(obj);
const spy2 = jest.mockFn();
const myMockFn = jest.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call'); // jest
const asyncMock = jest.fn()
.mockResolvedValue('default');
expect(await asyncMock()).toEqual('default'); // jest
});
describe('', () => {
beforeEach(() => jasmine.clock().install);
afterEach(() => jasmine.clock().uninstall);
});
describe('', () => {
beforeEach(() => {jest.useFakeTimers()});
afterEach(() => {jest.resetFakeTimers();});
});
Web App
JQuery
React
Angular Elements
Zone.js
Zone.js
Zone.js
async function test() {
return 1;
}
async function main() {
console.log('before await', Zone.current.name);
await test();
console.log('after await', Zone.current.name);
}
Zone.current.fork({name: 'test'}).run(main);