Skip to content

Commit eab5583

Browse files
authored
Merge pull request #100 from kreatemore/add-jest-support
Shallow-render should now support both Jest & Jasmine
2 parents a3613db + d79f947 commit eab5583

11 files changed

+163
-24
lines changed

lib/models/mock-of-provider.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Provider } from '@angular/core';
2+
import { testFramework } from '../test-framework';
23

34
export class MockOfProvider {
45
constructor(public mockOf: Provider, stubs: any = {}) {
56
Object.assign(this, stubs);
67
Object.keys(stubs).forEach(key => {
7-
if (typeof (this as any)[key] === 'function' && !(jasmine as any).isSpy((this as any)[key])) {
8-
spyOn(this as any, key).and.callThrough();
8+
if (typeof (this as any)[key] === 'function' && !testFramework.isSpy((this as any)[key])) {
9+
testFramework.spyOn(this as any, key);
910
}
1011
});
1112
}

lib/models/renderer.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Directive, EventEmitter, Type } from '@angular/core';
22
import { ComponentFixture, TestBed } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
4+
import { testFramework } from '../test-framework';
45
import { copyTestModule } from '../tools/copy-test-module';
56
import { createContainer } from '../tools/create-container';
67
import { mockProvider } from '../tools/mock-provider';
@@ -45,12 +46,12 @@ export class Renderer<TComponent> {
4546
.checkMockForStaticProperties(stubs);
4647
Object.keys(stubs).forEach(key => {
4748
const stub = stubs[key];
48-
if (!(jasmine as any).isSpy(obj[key])) {
49-
spyOn(obj, key).and.callFake(stub);
49+
if (!testFramework.isSpy(obj[key])) {
50+
testFramework.spyOn(obj, key, stub);
5051
} else {
51-
const spy = obj[key] as jasmine.Spy;
52-
spy.calls.reset();
53-
spy.and.callFake(stub);
52+
const spy = obj[key];
53+
testFramework.resetSpy(spy);
54+
testFramework.mockImplementation(spy, stub);
5455
}
5556
});
5657
});

lib/shallow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RecursivePartial } from './models/recursive-partial';
55
import { InvalidStaticPropertyMockError, Renderer } from './models/renderer';
66
import { Rendering, RenderOptions } from './models/rendering';
77
import { TestSetup } from './models/test-setup';
8-
import './tools/jasmine-matchers';
8+
import './test-frameworks/matchers';
99

1010
export class Shallow<TTestComponent> {
1111
readonly setup: TestSetup<TTestComponent>;

lib/test-framework.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TestFramework } from './test-frameworks';
2+
import { JasmineFramework } from './test-frameworks/jasmine.framework';
3+
import { JestFramework } from './test-frameworks/jest.framework';
4+
5+
declare var jest: any;
6+
7+
export const testFramework: TestFramework = typeof jest === 'undefined'
8+
? new JasmineFramework()
9+
: new JestFramework();

lib/test-frameworks/index.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
export interface TestFramework {
2+
isSpy(mockFunction: AnyFunction): boolean;
3+
4+
spyOn<T>(object: T, method: keyof T, mockImplementation?: AnyFunction): any;
5+
6+
resetSpy<T>(spy: T): void;
7+
8+
mockImplementation<T>(spy: T, mockImplementation: AnyFunction): void;
9+
10+
addMatchers(matcherFactories: CustomMatcherFactories): void;
11+
}
12+
13+
export type AnyFunction = (args?: any) => any;
14+
15+
export interface CustomMatcher {
16+
compare<T>(actual: T, expected: T, ...args: any[]): CustomMatcherResult;
17+
18+
compare(actual: any, ...expected: any[]): CustomMatcherResult;
19+
}
20+
21+
export interface CustomMatcherFactories {
22+
[index: string]: CustomMatcherFactory;
23+
}
24+
25+
export type CustomMatcherFactory = (
26+
util: MatchersUtil,
27+
customEqualityTesters: CustomEqualityTester[],
28+
) => CustomMatcher;
29+
30+
export interface MatchersUtil {
31+
equals(a: any, b: any, customTesters?: CustomEqualityTester[]): boolean;
32+
33+
contains<T>(
34+
haystack: ArrayLike<T> | string,
35+
needle: any,
36+
customTesters?: CustomEqualityTester[],
37+
): boolean;
38+
39+
buildFailureMessage(
40+
matcherName: string,
41+
isNot: boolean,
42+
actual: any,
43+
...expected: any[]
44+
): string;
45+
}
46+
47+
export type CustomEqualityTester = (first: any, second: any) => boolean | void;
48+
49+
export interface CustomMatcherResult {
50+
pass: boolean;
51+
message?: string;
52+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { AnyFunction, CustomMatcherFactories, TestFramework } from './index';
2+
import { BaseArrayLikeMatchers } from './matchers';
3+
4+
declare var jasmine: any;
5+
6+
export class JasmineFramework implements TestFramework {
7+
isSpy = (mockFunction: AnyFunction): boolean => jasmine.isSpy(mockFunction);
8+
9+
spyOn<T>(object: T, method: keyof T, mockImplementation?: AnyFunction) {
10+
const spy = spyOn(object, method);
11+
12+
if (!mockImplementation) {
13+
return spy.and.callThrough();
14+
}
15+
16+
this.mockImplementation(spy, mockImplementation);
17+
18+
return spy;
19+
}
20+
21+
mockImplementation(spy: any, mockImplementation: (args?: any) => any): void {
22+
spy.and.callFake(mockImplementation);
23+
}
24+
25+
resetSpy(spy: any): void {
26+
spy.calls.reset();
27+
}
28+
29+
addMatchers(matcherFactories: CustomMatcherFactories): void {
30+
jasmine.addMatchers(matcherFactories);
31+
}
32+
}
33+
34+
declare global {
35+
namespace jasmine {
36+
export interface ArrayLikeMatchers<T> extends BaseArrayLikeMatchers<T> {}
37+
}
38+
}

lib/test-frameworks/jest.framework.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { AnyFunction, CustomMatcherFactories, TestFramework } from './index';
2+
import { BaseArrayLikeMatchers } from './matchers';
3+
4+
declare var jest: any;
5+
declare var expect: any;
6+
7+
export class JestFramework implements TestFramework {
8+
isSpy = (mockFunction: any): boolean => jest.isMockFunction(mockFunction);
9+
10+
spyOn<T>(object: T, method: keyof T, mockImplementation?: AnyFunction) {
11+
const spy = jest.spyOn(object, method);
12+
13+
if (!!mockImplementation) {
14+
this.mockImplementation(spy, mockImplementation);
15+
}
16+
17+
return spy;
18+
}
19+
20+
mockImplementation(spy: any, mockImplementation: AnyFunction): void {
21+
spy.mockImplementation(mockImplementation);
22+
}
23+
24+
resetSpy(spy: any): void {
25+
spy.mockReset();
26+
}
27+
28+
addMatchers(matcherFactories: CustomMatcherFactories): void {
29+
expect.extend(matcherFactories);
30+
}
31+
}
32+
33+
declare global {
34+
namespace jest {
35+
export interface ArrayLikeMatchers<T> extends BaseArrayLikeMatchers<T> {}
36+
}
37+
}

lib/tools/jasmine-matchers.ts renamed to lib/test-frameworks/matchers.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { QueryMatch } from '../models/query-match';
2-
// This import ensures JasmineMatchers are available any time
3-
// shallow is imported
4-
import './jasmine-matchers-namespace';
2+
import { testFramework } from '../test-framework';
3+
import { CustomMatcherFactories } from './index';
54

6-
const jasmineMatchers: jasmine.CustomMatcherFactories = {
5+
export interface BaseArrayLikeMatchers<T> {
6+
toHaveFoundOne(): void;
7+
8+
toHaveFoundMoreThan(count: number): void;
9+
10+
toHaveFoundLessThan(count: number): void;
11+
12+
toHaveFound(count: number): void;
13+
}
14+
15+
const matcherFactories: CustomMatcherFactories = {
716
toHaveFound: () => ({
817
compare: (actual: QueryMatch<any>, expected: number) => ({
918
pass: actual.length === expected,
@@ -34,5 +43,5 @@ const jasmineMatchers: jasmine.CustomMatcherFactories = {
3443
};
3544

3645
beforeAll(() => {
37-
jasmine.addMatchers(jasmineMatchers);
46+
testFramework.addMatchers(matcherFactories);
3847
});

lib/tools/create-container.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Component, Type } from '@angular/core';
2+
import { testFramework } from '../test-framework';
23

34
const spyOnBindings = (bindings: any) => {
45
Object.keys(bindings).forEach(key => {
5-
if (typeof bindings[key] === 'function' && !(jasmine as any).isSpy(bindings[key])) {
6-
spyOn(bindings, key).and.callThrough();
6+
if (typeof bindings[key] === 'function' && !testFramework.isSpy(bindings[key])) {
7+
testFramework.spyOn(bindings, key);
78
}
89
});
910
return bindings;

lib/tools/jasmine-matchers-namespace.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)