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

Commit 9478a81

Browse files
committed
feat(test): add jest support to Mocha runner
1 parent e7d43fe commit 9478a81

13 files changed

+1136
-201
lines changed

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

+285-198
Large diffs are not rendered by default.

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

+16
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,19 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
170170
};
171171
}
172172

173+
updateArgs(newArgs: SpyStrategyOptions) {
174+
if (newArgs.identity) {
175+
this.args.identity = newArgs.identity;
176+
this.baseStrategy.identity = newArgs.identity;
177+
this.and.identity = newArgs.identity;
178+
}
179+
if (newArgs.originalFn) {
180+
this.args.originalFn = newArgs.originalFn;
181+
this.baseStrategy.originalFn = newArgs.originalFn;
182+
this.and.originalFn = newArgs.originalFn;
183+
}
184+
}
185+
173186
exec(spy: any, args: any) {
174187
let strategy = this.strategyDict.get(args);
175188
if (!strategy) {
@@ -234,6 +247,9 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
234247
wrapper.withArgs = function() {
235248
return spyStrategyDispatcher.withArgs.apply(spyStrategyDispatcher, arguments);
236249
};
250+
wrapper.updateArgs = function(newArgs: SpyStrategyOptions) {
251+
spyStrategyDispatcher.updateArgs(newArgs);
252+
};
237253
return wrapper;
238254
}
239255

lib/mocha/jasmine-bridge/jasmine.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ Zone.__load_patch('jasmine2mocha', (global: any) => {
4141
configurable: true,
4242
enumerable: true,
4343
get: function() {
44-
return global.Mocha.__zone_symbol__TIMEOUT;
44+
return jasmine.__zone_symbol__TIMEOUT || 2000;
4545
},
4646
set: function(newValue: number) {
47+
jasmine.__zone_symbol__TIMEOUT = newValue;
4748
global.Mocha.__zone_symbol__TIMEOUT = newValue;
4849
}
4950
});

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

+5
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,8 @@ export function eq(a: any, b: any) {
156156

157157
return false;
158158
}
159+
160+
export function toMatch(actual: any, expected: any) {
161+
const regExp = actual instanceof RegExp ? actual : new RegExp(actual);
162+
return regExp.test(expected);
163+
}

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

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
9+
import './jest';

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

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
9+
export function mappingBDD(jest: any, Mocha: any, global: any) {
10+
const mappings: {jest: string, Mocha: string}[] = [
11+
// other Jest APIs has already mapping in jasmine2mocha patch
12+
{jest: 'test', Mocha: 'it'}
13+
];
14+
mappings.forEach(map => {
15+
if (!global[map.jest]) {
16+
const mocha = map.Mocha;
17+
const chains = mocha.split('.');
18+
let mochaMethod: any = null;
19+
for (let i = 0; i < chains.length; i++) {
20+
mochaMethod = mochaMethod ? mochaMethod[chains[i]] : global[chains[i]];
21+
}
22+
global[map.jest] = jest[map.jest] = mochaMethod;
23+
}
24+
});
25+
}

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

+257
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
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+
import { Any, eq, toMatch } from '../jasmine-bridge/jasmine.util';
9+
export function expandExpect(global: any) {
10+
const jasmine = global.jasmine;
11+
const expect: any = global.expect;
12+
13+
class Anything { }
14+
15+
expect.anything = function () {
16+
return new Anything();
17+
};
18+
19+
expect.any = function (obj: any) {
20+
return new Any(obj);
21+
};
22+
23+
class ArrayContaining {
24+
constructor(public expectedArray: any[]) { }
25+
}
26+
27+
expect.arrayContaining = function (expectedArray: string[]) {
28+
return new ArrayContaining(expectedArray);
29+
}
30+
31+
class ObjectContaining {
32+
constructor(public expectedObject: any) { }
33+
}
34+
35+
expect.objectContaining = function (expectedObject: any) {
36+
return new ObjectContaining(expectedObject);
37+
}
38+
39+
class StringContaining {
40+
constructor(public expectedString: string) { }
41+
}
42+
43+
expect.stringContaining = function (expectedString: string) {
44+
return new StringContaining(expectedString);
45+
}
46+
47+
class StringMatching {
48+
constructor(public expectedMatcher: RegExp | string) { }
49+
}
50+
51+
expect.stringMatching = function (expectedMatcher: RegExp | string) {
52+
return new StringMatching(expectedMatcher);
53+
}
54+
55+
const assertions: { test: any, numbers: number }[] = expect.__zone_symbol__assertionsMap = [];
56+
57+
jasmine.addCustomEqualityTester((a: any, b: any) => {
58+
if (b instanceof Anything) {
59+
if (a === null || a === undefined) {
60+
return false;
61+
}
62+
return true;
63+
}
64+
if (b instanceof Any) {
65+
return b.eq(a);
66+
}
67+
if (b instanceof ArrayContaining && Array.isArray(a)) {
68+
for (let i = 0; i < b.expectedArray.length; i++) {
69+
let found = false;
70+
const bitem = b.expectedArray[i];
71+
for (let j = 0; j < a.length; j++) {
72+
if (eq(a[j], bitem)) {
73+
found = true;
74+
break;
75+
}
76+
}
77+
if (!found) {
78+
return false;
79+
}
80+
}
81+
return true;
82+
}
83+
if (b instanceof ObjectContaining) {
84+
Object.keys(b.expectedObject).forEach(key => {
85+
if (b.expectedObject.hasOwnProperty(key)) {
86+
if (!eq(a[key], b.expectedObject[key]) || !toMatch(b.expectedObject[key], a[key])) {
87+
return false;
88+
}
89+
}
90+
});
91+
return true;
92+
}
93+
if (b instanceof StringContaining) {
94+
let astr = a;
95+
if (typeof a !== 'string') {
96+
astr = Object.prototype.toString.call(a);
97+
}
98+
if (!astr) {
99+
return false;
100+
}
101+
return astr.indexOf(b.expectedString) !== -1;
102+
}
103+
if (b instanceof StringMatching) {
104+
let astr = a;
105+
if (typeof a !== 'string') {
106+
astr = Object.prototype.toString.call(a);
107+
}
108+
return toMatch(b.expectedMatcher, astr);
109+
}
110+
});
111+
112+
expect.extend = function (extendedMatchers: any) {
113+
const jasmineMatchers: any = {};
114+
Object.keys(extendedMatchers).forEach(key => {
115+
if (extendedMatchers.hasOwnProperty(key)) {
116+
const matcher = extendedMatchers[key];
117+
jasmineMatchers[key] = function (util: any, customEqualityTester: any) {
118+
return {
119+
compare: function (actual: any, expected: any) {
120+
const result = matcher(actual, expected);
121+
return result.pass;
122+
}
123+
};
124+
}
125+
}
126+
});
127+
jasmine.addMatchers(jasmineMatchers);
128+
};
129+
130+
jasmine.addMatchers({
131+
toHaveBeenCalledTimes: function(util: any, customEqualityTester: any) {
132+
return {
133+
compare: function (actual: any, expected: any) {
134+
return expected.calls.count() === actual;
135+
}
136+
};
137+
},
138+
lastCalledWith: function(util: any, customEqualityTester: any) {
139+
return {
140+
compare: function (actual: any, expected: any) {
141+
return util.equals(actual, expected.calls.last().args);
142+
}
143+
};
144+
},
145+
toHaveBeenLastCalledWith: function(util: any, customEqualityTester: any) {
146+
return {
147+
compare: function (actual: any, expected: any) {
148+
return util.equals(actual, expected.calls.last().args);
149+
}
150+
};
151+
},
152+
toBeInstanceOf: function(util: any, customEqualityTester: any) {
153+
return {
154+
compare: function (actual: any, expected: any) {
155+
return actual instanceof expected;
156+
}
157+
};
158+
},
159+
toContainEqual: function(util: any, customEqualityTester: any) {
160+
return {
161+
compare: function (actual: any, expected: any) {
162+
if (!Array.isArray(actual)) {
163+
return false;
164+
}
165+
return actual.filter(a => util.equals(a, expected)).length > 0;
166+
}
167+
};
168+
},
169+
toHaveLength: function(util: any, customEqualityTester: any) {
170+
return {
171+
compare: function (actual: any, expected: any) {
172+
return actual.length === expected;
173+
}
174+
};
175+
},
176+
toHaveProperty: function(util: any, customEqualityTester: any) {
177+
return {
178+
compare: function (actual: any, expected: any, expectedValue: any) {
179+
const split: string[] = Array.isArray(expected) ? expected : expected.split('.');
180+
let value = null;
181+
let hasKey = false;
182+
for (let i = 0; i < split.length; i++) {
183+
const prop = split[i];
184+
const isIndex = typeof prop === 'number';
185+
if (value) {
186+
hasKey = isIndex ? Array.isArray(value) && (value as any).length > prop :
187+
Object.keys(value).filter(a => util.equals(a, prop)).length > 0;
188+
value = value[prop];
189+
} else {
190+
hasKey = isIndex ? Array.isArray(actual) && (actual as any).length > prop :
191+
Object.keys(actual).filter(a => util.equals(a, prop)).length > 0;
192+
value = actual[prop];
193+
}
194+
if (!hasKey) {
195+
return false;
196+
}
197+
}
198+
199+
if (expectedValue !== undefined) {
200+
return util.equals(expectedValue, value);
201+
} else {
202+
return true;
203+
}
204+
}
205+
};
206+
},
207+
toMatchObject: function(util: any, customEqualityTester: any) {
208+
return {
209+
compare: function(actual: any, expected: any) {
210+
Object.keys(expected).forEach(key => {
211+
if (expected.hasOwnProperty(key)) {
212+
if (!util.equals(actual[key], expected[key]) &&
213+
!util.toMatch(actual[key], expected[key])) {
214+
return false;
215+
}
216+
}
217+
});
218+
return true;
219+
}
220+
};
221+
},
222+
});
223+
224+
expect.assertions = function (numbers: number) {
225+
if (typeof numbers !== 'number') {
226+
return;
227+
}
228+
const currentTest = global.Mocha.__zone_symbol__test;
229+
assertions.push({ test: currentTest, numbers });
230+
}
231+
232+
expect.hasAssertions = function () {
233+
const currentTest = global.Mocha.__zone_symbol__test;
234+
assertions.push({ test: currentTest, numbers: 1 });
235+
}
236+
237+
if (!global.Mocha.__zone_symbol__afterEach) {
238+
global.Mocha.__zone_symbol__afterEach = [];
239+
}
240+
241+
global.Mocha.__zone_symbol__afterEach.push((test: any) => {
242+
// check assertions
243+
for (let i = 0; i < assertions.length; i++) {
244+
const ass = assertions[i];
245+
if (ass.test === test) {
246+
assertions.splice(i, 1);
247+
const actual = jasmine.__zone_symbol__expect_assertions;
248+
jasmine.__zone_symbol__expect_assertions = 0;
249+
if (ass.numbers != actual) {
250+
throw new Error(`Assertions failed, expect should be called ${
251+
ass.numbers} times, it was actual called ${actual} times.`);
252+
}
253+
return;
254+
}
255+
}
256+
});
257+
}

0 commit comments

Comments
 (0)