Permalink
Browse files

feat(async interop): can dispatch functions that return promises, obs…

…ervable-like objects, and iterables such as generators
  • Loading branch information...
benlesh committed Apr 29, 2016
1 parent 2fdfbda commit d20c411244c63c359fb9692b09dc8f8a7dd4ea6f
Showing with 96 additions and 9 deletions.
  1. +4 −1 package.json
  2. +2 −1 src/rxDucksMiddleware.js
  3. +90 −7 test/rxDucksMiddleware-spec.js
@@ -37,12 +37,15 @@
"babel-plugin-transform-es2015-modules-commonjs": "^6.7.4",
"babel-plugin-transform-function-bind": "^6.5.2",
"babel-plugin-transform-object-rest-spread": "^6.6.5",
"babel-polyfill": "^6.7.4",
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.7.2",
"chai": "^3.5.0",
"eslint": "^2.8.0",
"mocha": "^2.4.5",
"promise": "^7.1.1",
"redux": "^3.5.1",
"rxjs": "^5.0.0-beta.6"
"rxjs": "^5.0.0-beta.6",
"symbol-observable": "^0.2.4"
}
}
@@ -1,12 +1,13 @@
import { Subject } from 'rxjs/Subject';
import { from } from 'rxjs/observable/from';
export function rxDucksMiddleware() {
let actions = new Subject();
let middleware = (store) => (next) => {
return (action) => {
if (typeof action === 'function') {
let obs = action(actions, store);
let obs = from(action(actions, store));
let sub = obs.subscribe(next);
actions.next(action);
return sub;
@@ -3,32 +3,115 @@ import { expect } from 'chai';
import { createStore, applyMiddleware } from 'redux';
import { rxDucksMiddleware } from '../';
import * as Rx from 'rxjs';
import Promise from 'promise';
import 'babel-polyfill';
import $$observable from 'symbol-observable';
const { Observable } = Rx;
describe('rxDucksMiddleware', () => {
it('should exist', () => {
expect(rxDucksMiddleware).to.be.a('function');
it('should intercept and process actions', (done) => {
const reducer = (state = [], action) => state.concat(action);
const middleware = rxDucksMiddleware();
const store = createStore(reducer, applyMiddleware(middleware));
store.dispatch(() => Observable.of({ type: 'ASYNC_ACTION_1' }).delay(10));
store.dispatch(() => Observable.of({ type: 'ASYNC_ACTION_2' }).delay(20));
// HACKY: but should work until we use TestScheduler.
setTimeout(() => {
expect(store.getState()).to.deep.equal([
{ type: '@@redux/INIT' },
{ type: 'ASYNC_ACTION_1' },
{ type: 'ASYNC_ACTION_2' }
]);
done();
}, 100);
});
it('should intercept and process actions', (done) => {
it('should work dispatched functions that return a promise', (done) => {
const reducer = (state = [], action) => state.concat(action);
const middleware = rxDucksMiddleware();
const store = createStore(reducer, applyMiddleware(middleware));
store.dispatch(() => Observable.of({ type: 'TEST1_HANDLED' }).delay(10));
store.dispatch(() => Observable.of({ type: 'TEST2_HANDLED' }).delay(20));
store.dispatch(() => Promise.resolve({ type: 'ASYNC_ACTION_1' }));
store.dispatch(() => Promise.resolve({ type: 'ASYNC_ACTION_2' }));
// HACKY: but should work until we use TestScheduler.
setTimeout(() => {
expect(store.getState()).to.deep.equal([
{ type: '@@redux/INIT' },
{ type: 'TEST1_HANDLED' },
{ type: 'TEST2_HANDLED' }
{ type: 'ASYNC_ACTION_1' },
{ type: 'ASYNC_ACTION_2' }
]);
done();
}, 100);
});
it('should work with iterators/generators', (done) => {
const reducer = (state = [], action) => state.concat(action);
const middleware = rxDucksMiddleware();
const store = createStore(reducer, applyMiddleware(middleware));
store.dispatch(() => (function *() {
yield { type: 'ASYNC_ACTION_1' };
yield { type: 'ASYNC_ACTION_2' };
})());
// HACKY: but should work until we use TestScheduler.
setTimeout(() => {
expect(store.getState()).to.deep.equal([
{ type: '@@redux/INIT' },
{ type: 'ASYNC_ACTION_1' },
{ type: 'ASYNC_ACTION_2' }
]);
done();
}, 100);
});
it('should work with observablesque arguments', (done) => {
const reducer = (state = [], action) => state.concat(action);
const middleware = rxDucksMiddleware();
const store = createStore(reducer, applyMiddleware(middleware));
let finalized = false;
store.dispatch(() => ({
[$$observable]() {
return {
subscribe(observer) {
observer.next({ type: 'ASYNC_ACTION_1' });
observer.next({ type: 'ASYNC_ACTION_2' });
observer.complete();
return {
unsubscribe() {
finalized = true;
}
};
}
};
}
}));
// HACKY: but should work until we use TestScheduler.
setTimeout(() => {
expect(store.getState()).to.deep.equal([
{ type: '@@redux/INIT' },
{ type: 'ASYNC_ACTION_1' },
{ type: 'ASYNC_ACTION_2' }
]);
expect(finalized).to.equal(true);
done();
}, 100);
});
});

0 comments on commit d20c411

Please sign in to comment.