Skip to content

Commit

Permalink
[refactored] tests to use mock-raf
Browse files Browse the repository at this point in the history
Reviewers: O3 Material JavaScript platform reviewers, O2 Material Motion, #material_motion, featherless

Reviewed By: O2 Material Motion, #material_motion, featherless

Tags: #material_motion

Differential Revision: http://codereview.cc/D2405
  • Loading branch information
appsforartists committed Jan 6, 2017
1 parent 01b088a commit 32eb483
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 123 deletions.
115 changes: 53 additions & 62 deletions packages/streams/src/__tests__/MotionObservable-debounce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import { expect } from 'chai';

import {
after,
before,
beforeEach,
describe,
it,
Expand All @@ -35,71 +37,60 @@ require('chai').use(

import {
createMockObserver,
useMockedRAF,
} from 'material-motion-testing-utils';

import MotionObservable from '../MotionObservable';

export function waitOneFrame() {
return new Promise(
resolve => requestAnimationFrame(resolve)
);
}

describe('MotionObservable._debounce',
() => {
let stream;
let mockObserver;
let listener1;

beforeEach(
() => {
mockObserver = createMockObserver();
stream = new MotionObservable(mockObserver.connect);
listener1 = stub();
}
);

it('should send the most recent value to the observer at the beginning of each frame',
() => {
stream._debounce().subscribe(listener1);

mockObserver.next(1);
mockObserver.next(2);
mockObserver.next(3);

return waitOneFrame().then(
() => {
expect(listener1).to.have.been.calledOnce;
expect(listener1).to.have.been.calledWith(3);
}
);
}
);

it('should dispatch exactly once per frame',
() => {
stream._debounce().subscribe(listener1);

mockObserver.next(1);
mockObserver.next(2);
mockObserver.next(3);

return waitOneFrame().then(
() => {
mockObserver.next(4);
mockObserver.next(5);
mockObserver.next(6);

return waitOneFrame();
}
).then(
() => {
expect(listener1).to.have.been.calledTwice;
expect(listener1).to.have.been.calledWith(6);
}
);
}
);
}
useMockedRAF(
(mockRAF) => {
let stream;
let mockObserver;
let listener1;

beforeEach(
() => {
mockObserver = createMockObserver();
stream = new MotionObservable(mockObserver.connect);
listener1 = stub();
}
);

it('should send the most recent value to the observer at the beginning of each frame',
() => {
stream._debounce().subscribe(listener1);

mockObserver.next(1);
mockObserver.next(2);
mockObserver.next(3);
mockRAF.step();

expect(listener1).to.have.been.calledOnce;
expect(listener1).to.have.been.calledWith(3);
}
);

it('should dispatch exactly once per frame',
() => {
stream._debounce().subscribe(listener1);

mockObserver.next(1);
mockObserver.next(2);
mockObserver.next(3);

mockRAF.step();

mockObserver.next(4);
mockObserver.next(5);
mockObserver.next(6);

mockRAF.step();

expect(listener1).to.have.been.calledTwice;
expect(listener1).to.have.been.calledWith(6);
}
);
}
)
);

102 changes: 47 additions & 55 deletions packages/streams/src/sources/__tests__/scrollSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import { expect } from 'chai';

import {
after,
before,
beforeEach,
describe,
it,
Expand All @@ -26,6 +28,10 @@ import {
stub,
} from 'sinon';

import {
useMockedRAF,
} from 'material-motion-testing-utils';

declare function require(name: string);

// chai really doesn't like being imported as an ES2015 module; will be fixed in v4
Expand All @@ -36,62 +42,48 @@ require('chai').use(
import MotionObservable from '../../MotionObservable';
import scrollSource from '../scrollSource';

// TODO: put this in a shared place.
import {
waitOneFrame,
} from '../../__tests__/MotionObservable-debounce.test';

describe('scrollSource',
() => {
let mockMotionElement;
let listener;

beforeEach(
() => {
mockMotionElement = new MockMotionElement();
listener = stub();
}
);

it('should return the current scrollPosition as a Point2D.',
() => {
const scrollPosition$ = scrollSource(mockMotionElement);
scrollPosition$.subscribe(listener);

const expectedScrollPositions = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
{ x: 20, y: 0 },
];

// scrollSource is debounced, so we must wait a frame between each
// scroll
mockMotionElement.scrollTo(expectedScrollPositions[0]);

return waitOneFrame().then(
() => {
mockMotionElement.scrollTo(expectedScrollPositions[1]);

return waitOneFrame();
}
).then(
() => {
mockMotionElement.scrollTo(expectedScrollPositions[2]);

return waitOneFrame();
}
).then(
() => {
const results = listener.args.map(
args => args[0]
);

expect(results).to.deep.equal(expectedScrollPositions);
}
);
}
);
}
useMockedRAF(
(mockRAF) => {
let mockMotionElement;
let listener;

beforeEach(
() => {
mockMotionElement = new MockMotionElement();
listener = stub();
}
);

it('should return the current scrollPosition as a Point2D.',
() => {
const scrollPosition$ = scrollSource(mockMotionElement);
scrollPosition$.subscribe(listener);

const expectedScrollPositions = [
{ x: 0, y: 0 },
{ x: 10, y: 0 },
{ x: 20, y: 0 },
];

// scrollSource is debounced, so we must wait a frame between each
// scroll
expectedScrollPositions.forEach(
position => {
mockMotionElement.scrollTo(position);
mockRAF.step();
}
);

const results = listener.args.map(
args => args[0]
);

expect(results).to.deep.equal(expectedScrollPositions);
}
);
}
)
);

class MockMotionElement {
Expand Down
23 changes: 18 additions & 5 deletions packages/testing-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@ Utilities for testing the Material Motion framework
## Usage ##

```javascript

import {
createMockObserver,
useMockedRAF,
} from 'material-motion-testing-utils';

const mockObserver = createMockObserver();
declare('TestableThing',
useMockedRAF(
(mockRAF) => {
it('should do something with requestAnimationFrame',
() => {
const mockObserver = createMockObserver();

const someStream = new MotionObservable(mockObserver).debounce();
someStream.subscribe(someListener);

mockObserver.next(4);
mockObserver.next(5);
mockObserver.next(6);

mockRAF.step();

const someStream = new MotionObservable(mockObserver);
someStream.subscribe(someListener);
mockObserver.next(5);
expect(someListener).to.have.been.calledWith(5);
expect(someListener).to.have.been.calledWith(6);
```
## Installation ##
Expand Down
9 changes: 8 additions & 1 deletion packages/testing-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
"clean": "rm -rf ./dist/*; mkdir -p ./dist/",
"build": "yarn run clean; ../../node_modules/.bin/tsc"
},
"dependencies": {
"mock-raf": "^1.0.0"
},
"peerDependencies": {
"mocha-sugar-free": "1.3.1",
"sinon": "2.0.0-pre.3"
},
"repository": {
Expand All @@ -35,5 +39,8 @@
"bugs": {
"url": "https://github.com/material-motion/material-motion-js/issues/"
},
"homepage": "https://github.com/material-motion/material-motion-js/tree/develop/packages/testing-utils/#readme"
"homepage": "https://github.com/material-motion/material-motion-js/tree/develop/packages/testing-utils/#readme",
"devDependencies": {
"@types/sinon": "^1.16.34"
}
}
3 changes: 3 additions & 0 deletions packages/testing-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@

export * from './createMockObserver';
export { default as createMockObserver } from './createMockObserver';

export * from './useMockedRAF';
export { default as useMockedRAF } from './useMockedRAF';
51 changes: 51 additions & 0 deletions packages/testing-utils/src/useMockedRAF.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/** @license
* Copyright 2016 - present The Material Motion Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

import {
after,
before,
} from 'mocha-sugar-free';

import {
SinonStub,
stub,
} from 'sinon';

import * as createMockRAF from 'mock-raf';

/**
* Replaces window.requestAnimationFrame with a mock for the duration of a mocha
* testing suite.
*/
export default function useMockedRAF(closure) {
return () => {
const mockRAF = createMockRAF();

before(
() => {
stub(window, 'requestAnimationFrame', mockRAF.raf);
}
);

after(
() => {
(window.requestAnimationFrame as SinonStub).restore();
}
);

return closure(mockRAF);
};
};
Loading

0 comments on commit 32eb483

Please sign in to comment.