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

Commit f584945

Browse files
committed
feat(test): add jest timers patch, refactor jasmine.expect
1 parent dc1a083 commit f584945

File tree

8 files changed

+345
-300
lines changed

8 files changed

+345
-300
lines changed

lib/mocha/jasmine-bridge/jasmine.expect.ts

Lines changed: 157 additions & 290 deletions
Large diffs are not rendered by default.

lib/mocha/jasmine-bridge/jasmine.spy.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,14 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
265265
});
266266
}
267267

268+
clearAllSpies() {
269+
if (this.registeredSpies.length === 0) {
270+
return;
271+
}
272+
this.registeredSpies.forEach(spy => spy.unRegister());
273+
this.registeredSpies.length = 0;
274+
}
275+
268276
clearSpies(testInfo: any) {
269277
if (this.registeredSpies.length === 0) {
270278
return;
@@ -289,6 +297,10 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
289297
spyRegistry.clearSpies(testInfo);
290298
};
291299

300+
Mocha.clearAllSpies = function() {
301+
spyRegistry.clearAllSpies();
302+
};
303+
292304
jasmine.createSpy = function(spyName: string|Function, originalFn?: Function) {
293305
if (typeof spyName === 'function') {
294306
originalFn = spyName;

lib/mocha/jasmine-bridge/jasmine.util.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ export function eq(a: any, b: any) {
135135
if (Object.keys(a).length !== Object.keys(b).length) {
136136
return false;
137137
}
138-
138+
if (a instanceof Error && b instanceof Error) {
139+
return a.message === b.message;
140+
}
139141
let isEqual = true;
140142

141143
for (let prop in a) {
@@ -154,10 +156,44 @@ export function eq(a: any, b: any) {
154156
return b.eq(a);
155157
}
156158

159+
if (a instanceof Error && typeof b === 'string') {
160+
return a.message === b;
161+
}
162+
if (b instanceof Error && typeof a === 'string') {
163+
return a === b.message;
164+
}
165+
157166
return false;
158167
}
159168

160169
export function toMatch(actual: any, expected: any) {
161-
const regExp = actual instanceof RegExp ? actual : new RegExp(actual);
162-
return regExp.test(expected);
170+
const regExp = expected instanceof RegExp ? expected : new RegExp(expected);
171+
return regExp.test(actual);
172+
}
173+
174+
const Mocha: any = typeof window === 'undefined' ? (global as any).Mocha : (window as any).Mocha;
175+
176+
export function formatObject(obj: any) {
177+
const stringify = Mocha.utils && Mocha.utils.stringify;
178+
return stringify(obj);
179+
}
180+
181+
export function buildFailureMessage(
182+
matcherName: string, isNot: boolean, actual: any, ...expected: any[]) {
183+
const englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) {
184+
return ' ' + s.toLowerCase();
185+
});
186+
187+
var message = 'Expected ' + formatObject(actual) + (isNot ? ' not ' : ' ') + englishyPredicate;
188+
189+
if (expected.length > 0) {
190+
for (var i = 0; i < expected.length; i++) {
191+
if (i > 0) {
192+
message += ',';
193+
}
194+
message += ' ' + formatObject(expected[i]);
195+
}
196+
}
197+
198+
return message + '.';
163199
}

lib/mocha/jest-bridge/jest.clock.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
export function addJestTimer(jest: any, global: any) {
9+
const {resetFakeAsyncZone, flushMicrotasks, discardPeriodicTasks, tick, flush, fakeAsync} =
10+
(Zone as any)[Zone.__symbol__('fakeAsyncTest')];
11+
const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
12+
const ProxyZoneSpec = (Zone as any)['ProxyZoneSpec'];
13+
14+
function getFakeAsyncTestZoneSpec() {
15+
return Zone.current.get('FakeAsyncTestZoneSpec');
16+
}
17+
18+
jest.clearAllTimers = function() {
19+
const zs = getFakeAsyncTestZoneSpec();
20+
if (!zs) {
21+
return;
22+
}
23+
// TODO: @JiaLiPassion, add clear method in fakeAsyncZoneSpec
24+
// flush();
25+
};
26+
27+
jest.runAllTimers = function() {
28+
const zs = getFakeAsyncTestZoneSpec();
29+
if (!zs) {
30+
return;
31+
}
32+
// TODO: @JiaLiPassion, now flush can only flush
33+
// non periodic timers, should flush periodic too.
34+
flush();
35+
};
36+
37+
jest.runAllImmediates = function() {
38+
const zs = getFakeAsyncTestZoneSpec();
39+
if (!zs) {
40+
return;
41+
}
42+
// TODO: @JiaLiPassion, should we support this one?
43+
flush();
44+
};
45+
46+
jest.runOnlyPendingTimers = function() {
47+
const zs = getFakeAsyncTestZoneSpec();
48+
if (!zs) {
49+
return;
50+
}
51+
// TODO: @JiaLiPassion, should we support this one?
52+
flush();
53+
};
54+
55+
jest.advanceTimersByTime = function(msToRun: number) {
56+
const zs = getFakeAsyncTestZoneSpec();
57+
if (!zs) {
58+
return;
59+
}
60+
tick(msToRun);
61+
};
62+
63+
64+
jest.runAllTicks = function() {
65+
const zs = getFakeAsyncTestZoneSpec();
66+
if (!zs) {
67+
return;
68+
}
69+
flushMicrotasks();
70+
};
71+
72+
jest.useFakeTimers = function() {
73+
const zs = getFakeAsyncTestZoneSpec();
74+
if (zs) {
75+
return;
76+
}
77+
const fakeAsyncTestZoneSpec = new FakeAsyncTestZoneSpec()
78+
const proxyZoneSpec = ProxyZoneSpec.get();
79+
jest.__zone_symbol__last_delegate_spec = proxyZoneSpec.getDelegate();
80+
proxyZoneSpec.setDelegate(fakeAsyncTestZoneSpec);
81+
fakeAsyncTestZoneSpec.lockDatePatch();
82+
};
83+
84+
jest.useRealTimers = function() {
85+
const zs = getFakeAsyncTestZoneSpec();
86+
if (!zs) {
87+
throw new Error('Must use real timers in the same block with useFakeTimers');
88+
}
89+
const proxyZoneSpec = ProxyZoneSpec.get();
90+
const lastDelegate = jest.__zone_symbol__last_delegate_spec;
91+
jest.__zone_symbol__last_delegate_spec = null;
92+
proxyZoneSpec.setDelegate(lastDelegate);
93+
zs.unlockDatePatch();
94+
}
95+
}

lib/mocha/jest-bridge/jest.expect.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export function expandExpect(global: any) {
109109
if (b instanceof ObjectContaining) {
110110
Object.keys(b.expectedObject).forEach(key => {
111111
if (b.expectedObject.hasOwnProperty(key)) {
112-
if (!eq(a[key], b.expectedObject[key]) || !toMatch(b.expectedObject[key], a[key])) {
112+
if (!eq(a[key], b.expectedObject[key]) || !toMatch(a[key], b.expectedObject[key])) {
113113
return false;
114114
}
115115
}
@@ -131,7 +131,7 @@ export function expandExpect(global: any) {
131131
if (typeof a !== 'string') {
132132
astr = Object.prototype.toString.call(a);
133133
}
134-
return toMatch(b.expectedMatcher, astr);
134+
return toMatch(astr, b.expectedMatcher);
135135
}
136136
});
137137

@@ -156,21 +156,21 @@ export function expandExpect(global: any) {
156156
toHaveBeenCalledTimes: function(util: any, customEqualityTester: any) {
157157
return {
158158
compare: function(actual: any, expected: any) {
159-
return {pass: expected.calls.count() === actual};
159+
return {pass: actual.calls.count() === expected};
160160
}
161161
};
162162
},
163163
lastCalledWith: function(util: any, customEqualityTester: any) {
164164
return {
165165
compare: function(actual: any, expected: any) {
166-
return {pass: util.equals(actual, expected.calls.last().args)};
166+
return {pass: util.equals(actual.calls.last().args, expected)};
167167
}
168168
};
169169
},
170170
toHaveBeenLastCalledWith: function(util: any, customEqualityTester: any) {
171171
return {
172172
compare: function(actual: any, expected: any) {
173-
return {pass: util.equals(actual, expected.calls.last().args)};
173+
return {pass: util.equals(actual.calls.last().args, expected)};
174174
}
175175
};
176176
},

lib/mocha/jest-bridge/jest.spy.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
export function mappingSpy(jest: any, jasmine: any, global: any) {
9+
jest.__zone_symbol__mocks = [];
910
function createSpy(spyFactory: (implFn?: Function) => any, implFn?: Function) {
10-
const spy = jasmine.createSpy('jestSpy', implFn);
11+
const spy = spyFactory(implFn);
1112
spy.defaultFn = implFn;
1213
const instances: any[] = [];
1314
const mockFn: any = function MockFn() {
@@ -101,6 +102,7 @@ export function mappingSpy(jest: any, jasmine: any, global: any) {
101102
global.Mocha.clearSpies(global.Mocha.__zone_symbol__current_ctx);
102103
};
103104

105+
jest.__zone_symbol__mocks.push(mockFn);
104106
return mockFn;
105107
}
106108

@@ -112,4 +114,27 @@ export function mappingSpy(jest: any, jasmine: any, global: any) {
112114
return accessType ? createSpy(() => global['spyOnProperty'](obj, methodName, accessType)) :
113115
createSpy(() => global['spyOn'](obj, methodName));
114116
};
117+
118+
jest.clearAllMocks = function() {
119+
jest.__zone_symbol__mocks.forEach((mock: any) => {
120+
mock.mockClear();
121+
});
122+
return jest;
123+
};
124+
125+
jest.resetAllMocks = function() {
126+
jest.__zone_symbol__mocks.forEach((mock: any) => {
127+
mock.mockReset();
128+
});
129+
return jest;
130+
};
131+
132+
jest.restoreAllMocks = function() {
133+
global.Mocha.clearAllSpies();
134+
return jest;
135+
};
136+
137+
jest.isMockFunction = function(fn: Function) {
138+
return jest.__zone_symbol__mocks.filter((m: any) => m === fn).length > 0;
139+
};
115140
}

lib/mocha/jest-bridge/jest.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {mappingBDD} from './jest.bdd';
9+
import {addJestTimer} from './jest.clock';
910
import {expandExpect} from './jest.expect';
1011
import {mappingSpy} from './jest.spy';
1112

@@ -16,6 +17,7 @@ Zone.__load_patch('jest2mocha', (global: any) => {
1617
return;
1718
}
1819
// TODO: @JiaLiPassion, now we only support jest in Mocha runner
20+
// support jasmine later.
1921
if (global.Mocha['__zone_symbol__isBridge']) {
2022
return;
2123
}
@@ -26,4 +28,12 @@ Zone.__load_patch('jest2mocha', (global: any) => {
2628
mappingBDD(jest, global.Mocha, global);
2729
expandExpect(global);
2830
mappingSpy(jest, jasmine, global);
31+
addJestTimer(jest, global);
32+
33+
jest.setTimeout = function(timeout: number) {
34+
const ctx = global.Mocha.__zone_symbol__current_ctx;
35+
if (ctx && typeof ctx.timeout === 'function') {
36+
ctx.timeout(timeout);
37+
}
38+
};
2939
});

test/spec/mocha/jest-bridge.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ describe('expect', () => {
147147
});
148148

149149
test('rejects to octopus', async () => {
150-
await (expect(Promise.reject(new Error('octopus'))) as any).rejects.toThrow('octopus');
150+
return await (expect(Promise.reject(new Error('octopus'))) as any).rejects.toThrow('octopus');
151151
});
152152
});
153153

0 commit comments

Comments
 (0)