Skip to content

Commit

Permalink
feat(fakeSchedulers): Support fake timers.
Browse files Browse the repository at this point in the history
In Jasmine - with Angular's zone.js - and in Jest.
  • Loading branch information
cartant committed Jun 9, 2018
1 parent bc06f8c commit 01cfbb3
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 96 deletions.
34 changes: 32 additions & 2 deletions fixtures/jasmine/passing-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@
*/
/*tslint:disable:object-literal-sort-keys*/

import { of } from "rxjs";
import { map, tap } from "rxjs/operators";
import "zone.js";
import "zone.js/dist/long-stack-trace-zone";
import "zone.js/dist/proxy.js";
import "zone.js/dist/sync-test";
import "zone.js/dist/jasmine-patch";
import "zone.js/dist/async-test";
import "zone.js/dist/fake-async-test";

import { of, timer } from "rxjs";
import { delay, map, tap } from "rxjs/operators";
import { cases, DoneFunction, marbles, observe } from "../../dist/jasmine";
import { fakeSchedulers } from "../../dist/jasmine/angular";

interface TestContext {
myVariable: number;
Expand Down Expand Up @@ -107,3 +116,24 @@ describe("observe", () => {
tap(value => expect(value).toEqual("pass"))
)));
});

describe("fakeSchedulers", () => {

it("should support a timer", fakeSchedulers(tick => {
let received: number | undefined;
timer(100).subscribe(value => received = value);
tick(50);
expect(received).not.toBeDefined();
tick(50);
expect(received).toBe(0);
}));

it("should support delay", fakeSchedulers(tick => {
let received: number | undefined;
of(1).pipe(delay(100)).subscribe(value => received = value);
tick(50);
expect(received).not.toBeDefined();
tick(50);
expect(received).toBe(1);
}));
});
214 changes: 121 additions & 93 deletions fixtures/jest/passing-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,100 +4,128 @@
*/
/*tslint:disable:object-literal-sort-keys*/

import { of } from "rxjs";
import { map, tap } from "rxjs/operators";
import { cases, DoneFunction, marbles, observe } from "../../dist/jest";

test("it should handle white space in marble diagrams correctly", marbles((m) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.cold(" --a-b-c-|", values);
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
}));

test("it should support marble tests", marbles((m) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot("--^-a-b-c-|", values);
const subs = "^-------!";
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
m.expect(source).toHaveSubscriptions(subs);
}));

test("it should support a done callback", marbles<DoneFunction>((m, done) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot("--^-a-b-c-|", values);
const subs = "^-------!";
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
m.expect(source).toHaveSubscriptions(subs);
m.flush();

expect(done).toBeInstanceOf(Function);
expect(done.fail).toBeInstanceOf(Function);
setTimeout(done, 0);
}));

cases("should support cases", (m, c) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot(c.s, values);
const expected = m.cold(c.e, values);
const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
}, {
"non-empty": {
s: "-a-b-c-|",
e: "-b-c-d-|"
},
"empty": {
s: "-|",
e: "-|"
}
import { of, timer } from "rxjs";
import { delay, map, tap } from "rxjs/operators";
import { cases, DoneFunction, fakeSchedulers, marbles, observe } from "../../dist/jest";

describe("marbles", () => {

test("it should handle white space in marble diagrams correctly", marbles((m) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.cold(" --a-b-c-|", values);
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
}));

test("it should support marble tests", marbles((m) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot("--^-a-b-c-|", values);
const subs = "^-------!";
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
m.expect(source).toHaveSubscriptions(subs);
}));

test("it should support a done callback", marbles<DoneFunction>((m, done) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot("--^-a-b-c-|", values);
const subs = "^-------!";
const expected = m.cold("--b-c-d-|", values);

const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
m.expect(source).toHaveSubscriptions(subs);
m.flush();

expect(done).toBeInstanceOf(Function);
expect(done.fail).toBeInstanceOf(Function);
setTimeout(done, 0);
}));

cases("should support cases", (m, c) => {

const values = {
a: 1,
b: 2,
c: 3,
d: 4
};

const source = m.hot(c.s, values);
const expected = m.cold(c.e, values);
const destination = source.pipe(map((value) => value + 1));

m.expect(destination).toBeObservable(expected);
}, {
"non-empty": {
s: "-a-b-c-|",
e: "-b-c-d-|"
},
"empty": {
s: "-|",
e: "-|"
}
});

test("it should support promises", marbles((m) => {
return Promise.resolve("pass").then((value) => expect(value).toEqual("pass"));
}));
});

test("it should support promises", marbles((m) => {
describe("observe", () => {

return Promise.resolve("pass").then((value) => expect(value).toEqual("pass"));
}));
test("it should support observe", observe(() => of("pass").pipe(
tap(value => expect(value).toEqual("pass"))
)));
});

test("it should support observe", observe(() => of("pass").pipe(
tap(value => expect(value).toEqual("pass"))
)));
describe("fakeSchedulers", () => {

beforeEach(() => jest.useFakeTimers());

test("it should support a timer", fakeSchedulers(advance => {
let received: number | undefined;
timer(100).subscribe(value => received = value);
advance(50);
expect(received).not.toBeDefined();
advance(50);
expect(received).toBe(0);
}));

test("it should support delay", fakeSchedulers(advance => {
let received: number | undefined;
of(1).pipe(delay(100)).subscribe(value => received = value);
advance(50);
expect(received).not.toBeDefined();
advance(50);
expect(received).toBe(1);
}));
});
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"description": "An RxJS marble testing library for any test framework",
"devDependencies": {
"@angular/core": "^6.0.4",
"@types/chai": "^4.1.2",
"@types/jasmine": "^2.5.53",
"@types/jest": "^23.0.0",
Expand All @@ -31,7 +32,8 @@
"typescript": "~2.9.1",
"webpack": "^4.0.0",
"webpack-cli": "^3.0.0",
"webpack-rxjs-externals": "^2.0.0"
"webpack-rxjs-externals": "^2.0.0",
"zone.js": "~0.8.26"
},
"es2015": "./dist/esm2015/index.js",
"homepage": "https://github.com/cartant/rxjs-marbles",
Expand Down
24 changes: 24 additions & 0 deletions source/jasmine/angular.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @license Use of this source code is governed by an MIT-style license that
* can be found in the LICENSE file at https://github.com/cartant/rxjs-marbles
*/

import { fakeAsync, tick } from "@angular/core/testing";
import { asyncScheduler } from "rxjs";

export function fakeSchedulers(
fakeTest: (tick: (milliseconds: number) => void) => void
): () => void {
return fakeAsync(() => {
try {
let fakeTime = 0;
asyncScheduler.now = () => fakeTime;
fakeTest(milliseconds => {
fakeTime += milliseconds;
tick(milliseconds);
});
} finally {
delete asyncScheduler.now;
}
});
}
25 changes: 25 additions & 0 deletions source/jest/fake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @license Use of this source code is governed by an MIT-style license that
* can be found in the LICENSE file at https://github.com/cartant/rxjs-marbles
*/

import { asyncScheduler } from "rxjs";

declare const jest: any;

export function fakeSchedulers(
fakeTest: (advance: (milliseconds: number) => void) => void
): () => void {
return () => {
try {
let fakeTime = 0;
asyncScheduler.now = () => fakeTime;
fakeTest(milliseconds => {
fakeTime += milliseconds;
jest.advanceTimersByTime(milliseconds);
});
} finally {
delete asyncScheduler.now;
}
};
}
1 change: 1 addition & 0 deletions source/jest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from "../configuration";
export * from "../context";
export * from "../expect";
export { MarblesFunction } from "../marbles";
export * from "./fake";
export * from "./observe";

declare const describe: Function;
Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
# yarn lockfile v1


"@angular/core@^6.0.4":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-6.0.4.tgz#80b19624493126b6c7e3a180a33dee92c84b4bd6"
dependencies:
tslib "^1.9.0"

"@ava/babel-plugin-throws-helper@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@ava/babel-plugin-throws-helper/-/babel-plugin-throws-helper-2.0.0.tgz#2fc1fe3c211a71071a4eca7b8f7af5842cd1ae7c"
Expand Down Expand Up @@ -6020,3 +6026,7 @@ yargs@~3.10.0:
cliui "^2.1.0"
decamelize "^1.0.0"
window-size "0.1.0"

zone.js@~0.8.26:
version "0.8.26"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.26.tgz#7bdd72f7668c5a7ad6b118148b4ea39c59d08d2d"

0 comments on commit 01cfbb3

Please sign in to comment.