Skip to content

Instantly share code, notes, and snippets.

@TCotton
Created May 17, 2021 10:49
Show Gist options
  • Save TCotton/3c0f5b549bd919aa34d9274053c1cf3b to your computer and use it in GitHub Desktop.
Save TCotton/3c0f5b549bd919aa34d9274053c1cf3b to your computer and use it in GitHub Desktop.
Redux saga example
// customer
import { put, call } from 'redux-saga/effects';
const fetch = (url, data) =>
window.fetch(url, {
body: JSON.stringify(data),
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' }
});
export function* addCustomer({ customer }) {
yield put({ type: 'ADD_CUSTOMER_SUBMITTING' });
const result = yield call(fetch, '/customers', customer);
if (result.ok) {
const customerWithId = yield call([result, 'json']);
yield put({
type: 'ADD_CUSTOMER_SUCCESSFUL',
customer: customerWithId
});
} else if (result.status === 422) {
const response = yield call([result, 'json']);
yield put({
type: 'ADD_CUSTOMER_VALIDATION_FAILED',
validationErrors: response.errors
});
} else {
yield put({ type: 'ADD_CUSTOMER_FAILED' });
}
}
const defaultState = {
customer: {},
status: undefined,
validationErrors: {},
error: false
};
export const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD_CUSTOMER_SUBMITTING':
return { status: 'SUBMITTING' };
default:
return state;
}
};
// store
import { put, call } from 'redux-saga/effects';
const fetch = (url, data) =>
window.fetch(url, {
body: JSON.stringify(data),
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' }
});
export function* addCustomer({ customer }) {
yield put({ type: 'ADD_CUSTOMER_SUBMITTING' });
const result = yield call(fetch, '/customers', customer);
if (result.ok) {
const customerWithId = yield call([result, 'json']);
yield put({
type: 'ADD_CUSTOMER_SUCCESSFUL',
customer: customerWithId
});
} else if (result.status === 422) {
const response = yield call([result, 'json']);
yield put({
type: 'ADD_CUSTOMER_VALIDATION_FAILED',
validationErrors: response.errors
});
} else {
yield put({ type: 'ADD_CUSTOMER_FAILED' });
}
}
const defaultState = {
customer: {},
status: undefined,
validationErrors: {},
error: false
};
export const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD_CUSTOMER_SUBMITTING':
return { status: 'SUBMITTING' };
default:
return state;
}
};
// customer test
import { storeSpy, expectRedux } from 'expect-redux';
import 'whatwg-fetch';
import {
fetchResponseOk,
fetchResponseError
} from '../spyHelpers';
import { configureStore } from '../../src/store';
import { reducer } from '../../src/sagas/customer';
describe('addCustomer', () => {
const customer = { id: 123 };
let store;
beforeEach(() => {
jest
.spyOn(window, 'fetch')
.mockReturnValue(fetchResponseOk(customer));
store = configureStore([storeSpy]);
});
const dispatchRequest = customer =>
store.dispatch({
type: 'ADD_CUSTOMER_REQUEST',
customer: customer
});
it('sets current status to submitting', () => {
dispatchRequest();
return expectRedux(store)
.toDispatchAnAction()
.matching({ type: 'ADD_CUSTOMER_SUBMITTING' });
});
it('submits request to the fetch api', async () => {
const inputCustomer = { firstName: 'Ashley' };
dispatchRequest(inputCustomer);
expect(window.fetch).toHaveBeenCalledWith('/customers', {
body: JSON.stringify(inputCustomer),
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' }
});
});
it('dispatches ADD_CUSTOMER_SUCCESSFUL on success', () => {
dispatchRequest();
return expectRedux(store)
.toDispatchAnAction()
.matching({ type: 'ADD_CUSTOMER_SUCCESSFUL', customer });
});
it('dispatches ADD_CUSTOMER_FAILED on non-specific error', () => {
window.fetch.mockReturnValue(fetchResponseError());
dispatchRequest();
return expectRedux(store)
.toDispatchAnAction()
.matching({ type: 'ADD_CUSTOMER_FAILED' });
});
it('dispatches ADD_CUSTOMER_VALIDATION_FAILED if validation errors were returned', () => {
const errors = { field: 'field', description: 'error text' };
window.fetch.mockReturnValue(
fetchResponseError(422, { errors })
);
dispatchRequest();
return expectRedux(store)
.toDispatchAnAction()
.matching({
type: 'ADD_CUSTOMER_VALIDATION_FAILED',
validationErrors: errors
});
});
});
describe('reducer', () => {
it('returns a default state for an undefined existing state', () => {
expect(reducer(undefined, {})).toEqual({
customer: {},
status: undefined,
validationErrors: {},
error: false
});
});
describe('ADD_CUSTOMER_SUBMITTING action', () => {
const action = { type: 'ADD_CUSTOMER_SUBMITTING' };
it('sets status to SUBMITTING', () => {
expect(reducer(undefined, action)).toMatchObject({
status: 'SUBMITTING'
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment