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

Commit

Permalink
feat(test): add jest support to Mocha runner
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion committed May 9, 2018
1 parent e7d43fe commit 9478a81
Show file tree
Hide file tree
Showing 13 changed files with 1,136 additions and 201 deletions.
483 changes: 285 additions & 198 deletions lib/mocha/jasmine-bridge/jasmine.expect.ts

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions lib/mocha/jasmine-bridge/jasmine.spy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
};
}

updateArgs(newArgs: SpyStrategyOptions) {
if (newArgs.identity) {
this.args.identity = newArgs.identity;
this.baseStrategy.identity = newArgs.identity;
this.and.identity = newArgs.identity;
}
if (newArgs.originalFn) {
this.args.originalFn = newArgs.originalFn;
this.baseStrategy.originalFn = newArgs.originalFn;
this.and.originalFn = newArgs.originalFn;
}
}

exec(spy: any, args: any) {
let strategy = this.strategyDict.get(args);
if (!strategy) {
Expand Down Expand Up @@ -234,6 +247,9 @@ export function addJasmineSpy(jasmine: any, Mocha: any, global: any) {
wrapper.withArgs = function() {
return spyStrategyDispatcher.withArgs.apply(spyStrategyDispatcher, arguments);
};
wrapper.updateArgs = function(newArgs: SpyStrategyOptions) {
spyStrategyDispatcher.updateArgs(newArgs);
};
return wrapper;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/mocha/jasmine-bridge/jasmine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ Zone.__load_patch('jasmine2mocha', (global: any) => {
configurable: true,
enumerable: true,
get: function() {
return global.Mocha.__zone_symbol__TIMEOUT;
return jasmine.__zone_symbol__TIMEOUT || 2000;
},
set: function(newValue: number) {
jasmine.__zone_symbol__TIMEOUT = newValue;
global.Mocha.__zone_symbol__TIMEOUT = newValue;
}
});
Expand Down
5 changes: 5 additions & 0 deletions lib/mocha/jasmine-bridge/jasmine.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,8 @@ export function eq(a: any, b: any) {

return false;
}

export function toMatch(actual: any, expected: any) {
const regExp = actual instanceof RegExp ? actual : new RegExp(actual);
return regExp.test(expected);
}
9 changes: 9 additions & 0 deletions lib/mocha/jest-bridge/jest-bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @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 './jest';
25 changes: 25 additions & 0 deletions lib/mocha/jest-bridge/jest.bdd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @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
*/

export function mappingBDD(jest: any, Mocha: any, global: any) {
const mappings: {jest: string, Mocha: string}[] = [
// other Jest APIs has already mapping in jasmine2mocha patch
{jest: 'test', Mocha: 'it'}
];
mappings.forEach(map => {
if (!global[map.jest]) {
const mocha = map.Mocha;
const chains = mocha.split('.');
let mochaMethod: any = null;
for (let i = 0; i < chains.length; i++) {
mochaMethod = mochaMethod ? mochaMethod[chains[i]] : global[chains[i]];
}
global[map.jest] = jest[map.jest] = mochaMethod;
}
});
}
257 changes: 257 additions & 0 deletions lib/mocha/jest-bridge/jest.expect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/**
* @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 { Any, eq, toMatch } from '../jasmine-bridge/jasmine.util';
export function expandExpect(global: any) {
const jasmine = global.jasmine;
const expect: any = global.expect;

class Anything { }

expect.anything = function () {
return new Anything();
};

expect.any = function (obj: any) {
return new Any(obj);
};

class ArrayContaining {
constructor(public expectedArray: any[]) { }
}

expect.arrayContaining = function (expectedArray: string[]) {
return new ArrayContaining(expectedArray);
}

class ObjectContaining {
constructor(public expectedObject: any) { }
}

expect.objectContaining = function (expectedObject: any) {
return new ObjectContaining(expectedObject);
}

class StringContaining {
constructor(public expectedString: string) { }
}

expect.stringContaining = function (expectedString: string) {
return new StringContaining(expectedString);
}

class StringMatching {
constructor(public expectedMatcher: RegExp | string) { }
}

expect.stringMatching = function (expectedMatcher: RegExp | string) {
return new StringMatching(expectedMatcher);
}

const assertions: { test: any, numbers: number }[] = expect.__zone_symbol__assertionsMap = [];

jasmine.addCustomEqualityTester((a: any, b: any) => {
if (b instanceof Anything) {
if (a === null || a === undefined) {
return false;
}
return true;
}
if (b instanceof Any) {
return b.eq(a);
}
if (b instanceof ArrayContaining && Array.isArray(a)) {
for (let i = 0; i < b.expectedArray.length; i++) {
let found = false;
const bitem = b.expectedArray[i];
for (let j = 0; j < a.length; j++) {
if (eq(a[j], bitem)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
if (b instanceof ObjectContaining) {
Object.keys(b.expectedObject).forEach(key => {
if (b.expectedObject.hasOwnProperty(key)) {
if (!eq(a[key], b.expectedObject[key]) || !toMatch(b.expectedObject[key], a[key])) {
return false;
}
}
});
return true;
}
if (b instanceof StringContaining) {
let astr = a;
if (typeof a !== 'string') {
astr = Object.prototype.toString.call(a);
}
if (!astr) {
return false;
}
return astr.indexOf(b.expectedString) !== -1;
}
if (b instanceof StringMatching) {
let astr = a;
if (typeof a !== 'string') {
astr = Object.prototype.toString.call(a);
}
return toMatch(b.expectedMatcher, astr);
}
});

expect.extend = function (extendedMatchers: any) {
const jasmineMatchers: any = {};
Object.keys(extendedMatchers).forEach(key => {
if (extendedMatchers.hasOwnProperty(key)) {
const matcher = extendedMatchers[key];
jasmineMatchers[key] = function (util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
const result = matcher(actual, expected);
return result.pass;
}
};
}
}
});
jasmine.addMatchers(jasmineMatchers);
};

jasmine.addMatchers({
toHaveBeenCalledTimes: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
return expected.calls.count() === actual;
}
};
},
lastCalledWith: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
return util.equals(actual, expected.calls.last().args);
}
};
},
toHaveBeenLastCalledWith: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
return util.equals(actual, expected.calls.last().args);
}
};
},
toBeInstanceOf: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
return actual instanceof expected;
}
};
},
toContainEqual: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
if (!Array.isArray(actual)) {
return false;
}
return actual.filter(a => util.equals(a, expected)).length > 0;
}
};
},
toHaveLength: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any) {
return actual.length === expected;
}
};
},
toHaveProperty: function(util: any, customEqualityTester: any) {
return {
compare: function (actual: any, expected: any, expectedValue: any) {
const split: string[] = Array.isArray(expected) ? expected : expected.split('.');
let value = null;
let hasKey = false;
for (let i = 0; i < split.length; i++) {
const prop = split[i];
const isIndex = typeof prop === 'number';
if (value) {
hasKey = isIndex ? Array.isArray(value) && (value as any).length > prop :
Object.keys(value).filter(a => util.equals(a, prop)).length > 0;
value = value[prop];
} else {
hasKey = isIndex ? Array.isArray(actual) && (actual as any).length > prop :
Object.keys(actual).filter(a => util.equals(a, prop)).length > 0;
value = actual[prop];
}
if (!hasKey) {
return false;
}
}

if (expectedValue !== undefined) {
return util.equals(expectedValue, value);
} else {
return true;
}
}
};
},
toMatchObject: function(util: any, customEqualityTester: any) {
return {
compare: function(actual: any, expected: any) {
Object.keys(expected).forEach(key => {
if (expected.hasOwnProperty(key)) {
if (!util.equals(actual[key], expected[key]) &&
!util.toMatch(actual[key], expected[key])) {
return false;
}
}
});
return true;
}
};
},
});

expect.assertions = function (numbers: number) {
if (typeof numbers !== 'number') {
return;
}
const currentTest = global.Mocha.__zone_symbol__test;
assertions.push({ test: currentTest, numbers });
}

expect.hasAssertions = function () {
const currentTest = global.Mocha.__zone_symbol__test;
assertions.push({ test: currentTest, numbers: 1 });
}

if (!global.Mocha.__zone_symbol__afterEach) {
global.Mocha.__zone_symbol__afterEach = [];
}

global.Mocha.__zone_symbol__afterEach.push((test: any) => {
// check assertions
for (let i = 0; i < assertions.length; i++) {
const ass = assertions[i];
if (ass.test === test) {
assertions.splice(i, 1);
const actual = jasmine.__zone_symbol__expect_assertions;
jasmine.__zone_symbol__expect_assertions = 0;
if (ass.numbers != actual) {
throw new Error(`Assertions failed, expect should be called ${
ass.numbers} times, it was actual called ${actual} times.`);
}
return;
}
}
});
}
Loading

0 comments on commit 9478a81

Please sign in to comment.