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

WIP(test): unify mocha/jasmine/jest test #1084

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -8,3 +8,4 @@ parsed-idl/
.vscode
npm-debug.log
build-esm/
yarn-error.log
17 changes: 9 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -35,18 +35,19 @@ script:
- node_modules/.bin/gulp promisetest
- yarn promisefinallytest
- yarn test:phantomjs-single
- node_modules/.bin/karma start karma-dist-sauce-jasmine.conf.js --single-run
- node_modules/.bin/karma start karma-build-sauce-mocha.conf.js --single-run
- node_modules/.bin/karma start karma-dist-sauce-selenium3-jasmine.conf.js --single-run
- node_modules/.bin/karma start karma-build-sauce-selenium3-mocha.conf.js --single-run
- node_modules/.bin/karma start ./test/karma/ci/dist/karma-dist-sauce-jasmine.conf.js --single-run
- node_modules/.bin/karma start ./test/karma/ci/build/karma-build-sauce-mocha.conf.js --single-run
- node_modules/.bin/karma start ./test/karma/ci/dist/karma-dist-sauce-selenium3-jasmine.conf.js --single-run
- node_modules/.bin/karma start ./test/karma/ci/build/karma-build-sauce-selenium3-mocha.conf.js --single-run
- node_modules/.bin/gulp test/node
- node_modules/.bin/gulp test/node -no-patch-clock
- node_modules/.bin/gulp test/bluebird
- node simple-server.js 2>&1> server.log&
- node ./test/webdriver/test.sauce.js
- node ./test/spec/webdriver/simple-server.js 2>&1> server.log&
- node ./test/spec/webdriver/test.sauce.js
- yarn add [email protected] [email protected] [email protected]
- yarn test:phantomjs-single
- node_modules/.bin/karma start karma-dist-sauce-jasmine3.conf.js --single-run
- node_modules/.bin/karma start karma-build-sauce-selenium3-mocha.conf.js --single-run
- yarn test-mocha-jasmine-bridge-node
- node_modules/.bin/karma start ./test/karma/ci/dist/karma-dist-sauce-jasmine3.conf.js --single-run
- node_modules/.bin/karma start ./test/karma/ci/build/karma-build-sauce-selenium3-mocha.conf.js --single-run
- node_modules/.bin/gulp test/node
- node_modules/.bin/gulp test/node -no-patch-clock
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
Implements _Zones_ for JavaScript, inspired by [Dart](https://www.dartlang.org/articles/zones/).

> If you're using zone.js via unpkg (i.e. using `https://unpkg.com/zone.js`)
> and you're using any of the following libraries, make sure you import them first
> and you're using any of the following libraries, make sure you import them first
> * 'newrelic' as it patches global.Promise before zone.js does
> * 'async-listener' as it patches global.setTimeout, global.setInterval before zone.js does
@@ -36,19 +36,19 @@ See this video from ng-conf 2014 for a detailed explanation:
## Standard API support

zone.js patched most standard web APIs (such as DOM events, `XMLHttpRequest`, ...) and nodejs APIs
(`EventEmitter`, `fs`, ...), for more details, please see [STANDARD-APIS.md](STANDARD-APIS.md).
(`EventEmitter`, `fs`, ...), for more details, please see [STANDARD-APIS.md](./doc/design/STANDARD-APIS.md).

## Nonstandard API support

We are adding support to some nonstandard APIs, such as MediaQuery and
Notification. Please see [NON-STANDARD-APIS.md](NON-STANDARD-APIS.md) for more details.
Notification. Please see [NON-STANDARD-APIS.md](./doc/design/NON-STANDARD-APIS.md) for more details.

## Modules

zone.js patches the async APIs described above, but those patches will have some overhead.
Starting from zone.js v0.8.9, you can choose which web API module you want to patch.
For more details, please
see [MODULE.md](MODULE.md).
see [MODULE.md](./doc/design/MODULE.md).

## Promise A+ test passed
[![Promises/A+ 1.1 compliant](https://promisesaplus.com/assets/logo-small.png)](https://promisesaplus.com/)
File renamed without changes.
File renamed without changes.
File renamed without changes.
261 changes: 261 additions & 0 deletions doc/design/TEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
# Test Runner

`zone-testing` monkey-patch `jasmine` and `mocha` runner to provide the following functionalities.

1. All `describe/xdescribe/fdescribe` are guaranteed to run in `SyncTestZone`, so no `async` code are allowed under `describe` directly.

```javascript
describe('test', () => {
// setTimeout(() => {}, 100); // not allowed
});
```

2. Support `fakeAsync` in `test`.

```javascript
import 'zone.js`;
import 'zone.js/dist/zone-testing`;
// support simulate timer function.
describe('fakeAsync', () => {
it('setTimeout', fakeAsync(() => {
let called = false;
setTimeout(() => {called = true}, 100);
expect(called).toBe(false);
tick(100);
expect(called).toBe(true);
}));
it ('Promise', fakeAsync(() => {
let thenRan = false;
Promise.resolve(null).then((_) => {
thenRan = true;
});
expect(thenRan).toEqual(false);
flushMicrotasks();
expect(thenRan).toEqual(true);
}));
});
```

Will add more examples later.

3. support `asyncTest`.

```javascript
import 'zone.js`;
import 'zone.js/dist/zone-testing`;
// TODO: this is too complex to load asyncTest, should expose as global function.
const asyncTest = (Zone as any)[Zone.__symbol__('asyncTest')];
describe('async', () => {
it('setTimeout and Promise', asyncTest(() => { // don't need to provide doneFn here.
let timeoutCalled = false;
setTimeout(() => {
timeoutCalled = true;
}, 100);
let thenRan = false;
Promise.resolve(null).then((_) => {
thenRan = true;
});
setTimeout(() => {
expect(timeoutCalled).toBe(true);
expect(thenRan).toBe(true);
}, 200);
}));
});
```

Will add more examples later.

And `asyncTest/fakeAsyncTest/flush/tick/...` those APIs have been exposed as `global APIs`, you can check this type definition file for the full list. [zone-testing.typing.ts](../../lib/testing/zone-testing.typing.ts).

4. Date.now/jasmine.clock()/rxjs.Scheduler support in fakeAsync.

```javascript
describe('fakeAsync', () => {
it('setTimeout', fakeAsync(() => {
const start = Date.now();
testZoneSpec.tick(100);
const end = Date.now();
expect(end - start).toBe(100);
}));
});

// NOTE: automatically fall into fakeAsync need to set this flag to true before loading zone-testing
// (window as any).__zone_symbol__fakeAsyncPatchLock = true;
describe('jasmine.clock', () => {
beforeEach(() => {
jasmine.clock().install();
});

afterEach(() => {
jasmine.clock().uninstall();
});

it('should get date diff correctly', () => { // we don't need fakeAsync here.
// automatically run into fake async zone, because jasmine.clock() is installed.
const start = Date.now();
jasmine.clock().tick(100);
const end = Date.now();
expect(end - start).toBe(100);
});
});

// import the following files to patch Date.now of rxjs.Scheduler/rxjs.asap/rxjs.async
import 'zone.js/dist/zone-patch-rxjs-fakeAsync';

describe('fakeAsync', () => {
it('should get date diff correctly', fakeAsync(() => {
let result = null;
const observable = new Observable((subscribe: any) => {
subscribe.next('hello');
});
observable.delay(1000).subscribe(v => {
result = v;
});
expect(result).toBeNull();
testZoneSpec.tick(1000);
expect(result).toBe('hello');
});
});
```
5. Unify `jasmine/mocha/jest` test cases in `Mocha` runner.
You can write `jasmine`, `mocha`, `jest` style test cases when you use `Mocha` runner.
You can use `jasmine spy` inside `mocha` cases, you can use `jest style expect and mock`, and you can use `jasmine clock` and `jest TimerMock`.
for the full list of supported APIs, please check this type definition file. [zone-testing.typing.ts](../../lib/testing/zone-testing.typing.ts).
```javascript
describe('mixed', () => {
// jasmine style beforeAll
beforeAll(() => {});

// mocha style setup
setup(() => {});

it('jasmine style', () => {
expect(true).toBe(true);
});

// mocha specify
specify('mocha style', () => {
foo = {
setBar: function(value: any) {
bar = value;
}
};

spyOn(foo, 'setBar').and.callThrough();
foo.setBar(123);
expect(bar).toEqual(123);
});

test('jest style', () => {
// TODO: will add type definition later.
(expect([1, 2, 3]) as any).toHaveLength(3);
// can handle promise with jest expect.resolves
return (expect(Promise.resolve('lemon')) as any).resolves.toBe('lemon');
});

test('jest mock', () => {
// can support jest.mock
const myMockFn = jest.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');

// 'first call', 'second call', 'default', 'default'
logs.push(myMockFn(), myMockFn(), myMockFn(), myMockFn());
expect(logs).toEqual(['first call', 'second call', 'default', 'default']);
});
});
```
For full examples, you can find in [jasmine](../../test/spec/mocha/jasmine-bridge.spec.ts) and [jest](../../test/spec/jest-bridge.spec.ts)
And here is the mapping about which `jasmine/jest` functionalities are supported inside `Mocha` runner.
1. BDD/TDD interface.
| jasmine | mocha | jest |
| --- | --- | --- |
| beforeAll | before | beforeAll |
| afterAll | after | beforeAll |
| xdescribe | describe.skip | describe.skip |
| fdescribe | describe.only | describe.only |
| xit | it.skip | it.skip |
| fit | it.only | it.only |
And of course you can use `setup/suiteSetup/tearDown/suiteTearDown` in Mocha.
2. jasmine.clock
You can use `jasmine.clock` inside `Mocha` runner. And you can also use the `auto fakeAsync` feature.
3. jest TimerMock
You can use `jest.useFakeTimers()` to setup jest Timer Mock, and you can use `jest.runAllTimers()/jest.runOnlyPendingTimers()/jest.advanceTimersByTime()/...` to do the time control.
3. jasmine.spy
In Mocha, no built-in spy lib is provided, you can still use 3rd party spy library, with `zone-testing`, you can use `jasmine spy`, you can use `jasmine.createSpy, jasmine.createSpyObj, spyOn, spyOnProperty` to create `jasmine style spy`. And you can also use all `spy strategy` such as `callThough/callFake/...`
4. jest mock
Not only `jasmine spy`, you can also use `jest mock`, You can use `jest.fn` to create `jest style mock`. And you can also use the `jest mockFn method` such as `mochFn.mockReturnValue`.
5. jasmine expect
In Mocha, there is no built in `expect` lib, you can still use 3rd party `assert/expect` lib, with `zone-testing`, you can use `jasmine expect mathers`.
- nothing
- toBe
- toBeCloseTo
- toEqual
- toBeGreaterThan
- toBeGreaterThanOrEqual
- toBeLessThan
- toBeLessThanOrEqual
- toBeDefined
- toBeNaN
- toBeNegativeInfinity
- toBeNull
- toBePositiveInfinity
- toBeUndefined
- toThrow
- toThrowError
- toBeTruthy
- toBeFalsy
- toContain
- toHaveBeenCalled
- toHaveBeenCalledWith
- toMatch
- not
You can also add customMatchers and customEqualityTesters.
6. Jest expect
And you can also get `jest expect matchers`.
- toBeCalled
- toBeCalledWith
- toHaveBeenCalledTimes
- lastCalledWith
- toHaveBeenLastCalledWith
- toBeInstanceOf
- toContainEqual
- toHaveLength
- toHaveProperty
- toMatchObject
And `expect util method`.
- expect.anything
- expect.any
- expect.arrayContaining
- expect.objectContaining
- expect.stringContaining
- expect.stringMatching
- expect.extend
- expect.assertions
- expect.hasAssertions
- expect.resolves (Promise)
- expect.rejects (Promise)
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
19 changes: 12 additions & 7 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -106,6 +106,12 @@ gulp.task('build/zone.js.d.ts', ['compile-esm'], function() {
.pipe(gulp.dest('./dist'));
});

gulp.task('build/zone-testing.d.ts', ['compile-esm'], function() {
return gulp.src('./build-esm/lib/testing/zone-testing.typing.d.ts')
.pipe(rename('zone-testing.d.ts'))
.pipe(gulp.dest('./dist'));
});

// Zone for Node.js environment.
gulp.task('build/zone-node.js', ['compile-esm-node'], function(cb) {
return generateScript('./lib/node/rollup-main.ts', 'zone-node.js', false, cb);
@@ -404,9 +410,8 @@ function nodeTest(specFiles, cb) {
require('./build/lib/node/rollup-main');
var args = process.argv;
if (args.length > 3) {
require('./build/test/test-env-setup-jasmine' + args[3]);
require('./build/test/env/config/test-env-setup-jasmine' + args[3]);
}
var JasmineRunner = require('jasmine');
var JasmineRunner = require('jasmine');
var jrunner = new JasmineRunner();

@@ -433,12 +438,12 @@ function nodeTest(specFiles, cb) {
}

gulp.task('test/node', ['compile-node'], function(cb) {
var specFiles = ['build/test/node_entry_point.js'];
var specFiles = ['build/test/env/node/node_entry_point.js'];
nodeTest(specFiles, cb);
});

gulp.task('test/bluebird', ['compile-node'], function(cb) {
var specFiles = ['build/test/node_bluebird_entry_point.js'];
var specFiles = ['build/test/env/node/node_bluebird_entry_point.js'];
nodeTest(specFiles, cb);
});

@@ -498,7 +503,7 @@ gulp.task('changelog', () => {
// run promise aplus test
gulp.task('promisetest', ['build/zone-node.js'], (cb) => {
const promisesAplusTests = require('promises-aplus-tests');
const adapter = require('./promise-adapter');
const adapter = require('./test/spec/promise/promise-adapter');
promisesAplusTests(adapter, {reporter: 'dot'}, function(err) {
if (err) {
cb(err);
@@ -510,8 +515,8 @@ gulp.task('promisetest', ['build/zone-node.js'], (cb) => {

// check dist file size limitation
gulp.task('filesize', ['build'], (cb) => {
const checker = require('./check-file-size');
const result = checker(require('./file-size-limit.json'));
const checker = require('./scripts/size/check-file-size');
const result = checker(require('./scripts/size/file-size-limit.json'));
if (result) {
cb();
} else {
12 changes: 0 additions & 12 deletions karma-dist-sauce-jasmine3.conf.js

This file was deleted.

Loading