Skip to content
This repository has been archived by the owner on Jan 18, 2021. It is now read-only.

Commit

Permalink
feat: 🎸 Able to pass generator to handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Pham Tuan Manh authored and manhhailua committed Sep 28, 2019
1 parent e1ecde7 commit c9899f4
Show file tree
Hide file tree
Showing 6 changed files with 481 additions and 230 deletions.
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"flow": "flow check",
"flowbuild": "flow-copy-source src dist",
"lint": "eslint . --ignore-path .gitignore",
"postcoverage": "opn coverage/lcov-report/index.html",
"postcoverage": "open coverage/lcov-report/index.html",
"postdocs": "git add README.md",
"prebuild": "npm run docs && npm run clean && npm run flowbuild",
"semantic-release": "semantic-release",
Expand Down Expand Up @@ -67,32 +67,32 @@
},
"dependencies": {
"redux": "^4.0.4",
"redux-saga": "^1.0.5"
"redux-saga": "^1.1.1"
},
"devDependencies": {
"@babel/cli": "7.5.5",
"@babel/core": "7.5.5",
"@babel/cli": "7.6.2",
"@babel/core": "7.6.2",
"@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/preset-env": "7.5.5",
"@babel/preset-env": "7.6.2",
"@babel/preset-flow": "7.0.0",
"@semantic-release/changelog": "^3.0.4",
"@semantic-release/git": "^7.0.16",
"babel-eslint": "10.0.3",
"babel-jest": "24.9.0",
"documentation": "12.1.1",
"eslint": "6.2.2",
"documentation": "12.1.2",
"eslint": "6.4.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-config-prettier": "6.1.0",
"eslint-plugin-flowtype": "4.2.0",
"eslint-config-prettier": "6.3.0",
"eslint-plugin-flowtype": "4.3.0",
"eslint-plugin-flowtype-errors": "4.1.0",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-prettier": "3.1.0",
"flow-bin": "0.106.2",
"eslint-plugin-prettier": "3.1.1",
"flow-bin": "0.108.0",
"flow-copy-source": "2.0.8",
"husky": "3.0.4",
"husky": "3.0.7",
"jest": "24.9.0",
"lint-staged": "9.2.5",
"opn-cli": "5.0.0",
"lint-staged": "9.4.0",
"open-cli": "5.0.0",
"prettier": "1.18.2",
"rimraf": "3.0.0",
"semantic-release": "^15.13.24"
Expand Down
2 changes: 1 addition & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface UnfoldSagaCallbacksType {
}

export interface UnfoldSagaHandlerType {
handler: Function;
handler: Function | GeneratorFunction;
key: string;
}

Expand Down
7 changes: 6 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ export function* unfoldSaga(
try {
yield put({ type: createActionTypeOnBeginning(key) });
yield call(onBeginning);
const data = yield call(handler);
let data;
if (['GeneratorFunction', 'AsyncGeneratorFunction'].includes(handler.constructor.name)) {
data = yield* handler();
} else {
data = yield call(handler);
}
yield put({ type: createActionTypeOnSuccess(key), payload: data });
yield call(onSuccess, data);
} catch (error) {
Expand Down
69 changes: 43 additions & 26 deletions test/unfoldSaga.test.js → test/unfoldSagaAsync.test.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,75 @@
import { put } from 'redux-saga/effects';
import { call, put } from 'redux-saga/effects';
import {
createActionTypeOnBeginning,
createActionTypeOnFailure,
createActionTypeOnFinish,
createActionTypeOnSuccess,
unfoldSaga,
} from '../src';
import { noop } from '../src/helpers';

describe('unfoldSaga', () => {
describe('unfoldSaga for async handler', () => {
let generator;
let key;
let result;

const callbacksMock = {
onBeginning: jest.fn(),
onSuccess: jest.fn(),
onFailure: jest.fn(),
onFinish: jest.fn(),
};

describe('on happy flow', () => {
const fakeResult = 'handler-result';
const handler = function handler() {
return fakeResult;
};

beforeAll(() => {
key = 'TEST';
generator = unfoldSaga({
handler: noop,
key,
});
});

beforeEach(() => {
result = generator.next();
generator = unfoldSaga({ handler, key }, callbacksMock);
});

test('should PUT onBeginning action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnBeginning(key) }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnBeginning(key) }));
});

test('should CALL onBeginning callback', () => {
result = generator.next();
expect(result.done).toBe(false);
// expect(result.value).toEqual(
// put({ type: createActionTypeOnBeginning(key) })
// );
expect(result.value).toEqual(call(callbacksMock.onBeginning));
});

test('should CALL handler', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(call(handler));
});

test('should PUT onSuccess action', () => {
result = generator.next(fakeResult);
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnSuccess(key) }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnSuccess(key), payload: fakeResult }));
});

test('should CALL onSuccess callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onSuccess, fakeResult));
});

test('should PUT onFinish action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnFinish(key) }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFinish(key) }));
});

test('should CALL onFinish callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onFinish));
});

test('should end', () => {
Expand All @@ -71,23 +83,27 @@ describe('unfoldSaga', () => {

beforeAll(() => {
key = 'TEST';
generator = unfoldSaga({
handler: () => {
throw fakeError;
generator = unfoldSaga(
{
handler: () => {
throw fakeError;
},
key,
},
key,
});
callbacksMock,
);
});

test('should PUT onBeginning action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnBeginning(key) }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnBeginning(key) }));
});

test('should CALL onBeginning callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onBeginning));
});

test('should CALL handler', () => {
Expand All @@ -98,24 +114,25 @@ describe('unfoldSaga', () => {
test('should PUT onFailure action', () => {
result = generator.throw(fakeError);
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnFailure(key), payload: fakeError }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFailure(key), payload: fakeError }));
});

test('should CALL onFailure callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onFailure, fakeError));
});

test('should PUT onFinish action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(put({ type: createActionTypeOnFinish(key) }));
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFinish(key) }));
});

test('should CALL onFinish callback', () => {
result = generator.next();
expect(result.done).toBe(false);
// mock
expect(result.value).toEqual(call(callbacksMock.onFinish));
});

test('should end', () => {
Expand Down
154 changes: 154 additions & 0 deletions test/unfoldSagaGenerator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { call, put } from 'redux-saga/effects';
import {
createActionTypeOnBeginning,
createActionTypeOnFailure,
createActionTypeOnFinish,
createActionTypeOnSuccess,
unfoldSaga,
} from '../src';

describe('unfoldSaga for generator handler', () => {
let generator;
let key;
let result;

const callbacksMock = {
onBeginning: jest.fn(),
onSuccess: jest.fn(),
onFailure: jest.fn(),
onFinish: jest.fn(),
};

describe('on happy flow', () => {
const fakeResult = 'handler-result';
const handler = function* handler() {
yield 1;
yield 2;
yield 3;
return fakeResult;
};

beforeAll(() => {
key = 'TEST';
generator = unfoldSaga({ handler, key }, callbacksMock);
});

test('should PUT onBeginning action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnBeginning(key) }));
});

test('should CALL onBeginning callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onBeginning));
});

test('should yield handler', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toBe(1);

result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toBe(2);

result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toBe(3);
});

test('should PUT onSuccess action', () => {
result = generator.next(fakeResult);
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnSuccess(key), payload: fakeResult }));
});

test('should CALL onSuccess callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onSuccess, fakeResult));
});

test('should PUT onFinish action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFinish(key) }));
});

test('should CALL onFinish callback', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toEqual(call(callbacksMock.onFinish));
});

test('should end', () => {
result = generator.next();
expect(result.done).toBe(true);
});
});

describe('on error flow', () => {
const fakeError = new Error('test');

beforeAll(() => {
key = 'TEST';
generator = unfoldSaga(
{
handler: () => {
throw fakeError;
},
key,
},
// callbacksMock,
);
});

test('should PUT onBeginning action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnBeginning(key) }));
});

test('should CALL onBeginning callback', () => {
result = generator.next();
expect(result.done).toBe(false);
// expect(result.value).toEqual(call(callbacksMock.onBeginning));
});

test('should CALL handler', () => {
result = generator.next();
expect(result.done).toBe(false);
});

test('should PUT onFailure action', () => {
result = generator.throw(fakeError);
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFailure(key), payload: fakeError }));
});

test('should CALL onFailure callback', () => {
result = generator.next();
expect(result.done).toBe(false);
// expect(result.value).toEqual(call(callbacksMock.onFailure, fakeError));
});

test('should PUT onFinish action', () => {
result = generator.next();
expect(result.done).toBe(false);
expect(result.value).toStrictEqual(put({ type: createActionTypeOnFinish(key) }));
});

test('should CALL onFinish callback', () => {
result = generator.next();
expect(result.done).toBe(false);
// expect(result.value).toEqual(call(callbacksMock.onFinish));
});

test('should end', () => {
result = generator.next();
expect(result.done).toBe(true);
});
});
});
Loading

0 comments on commit c9899f4

Please sign in to comment.