mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #15158 from grafana/hugoh/redux-poc
WIP: Reducing boilerplate code for Redux
This commit is contained in:
56
public/test/core/redux/reducerTester.test.ts
Normal file
56
public/test/core/redux/reducerTester.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { reducerFactory, actionCreatorFactory } from 'app/core/redux';
|
||||
import { reducerTester } from './reducerTester';
|
||||
|
||||
interface DummyState {
|
||||
data: string[];
|
||||
}
|
||||
|
||||
const initialState: DummyState = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const dummyAction = actionCreatorFactory<string>('dummyAction').create();
|
||||
|
||||
const mutatingReducer = reducerFactory(initialState)
|
||||
.addMapper({
|
||||
filter: dummyAction,
|
||||
mapper: (state, action) => {
|
||||
state.data.push(action.payload);
|
||||
return state;
|
||||
},
|
||||
})
|
||||
.create();
|
||||
|
||||
const okReducer = reducerFactory(initialState)
|
||||
.addMapper({
|
||||
filter: dummyAction,
|
||||
mapper: (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
data: state.data.concat(action.payload),
|
||||
};
|
||||
},
|
||||
})
|
||||
.create();
|
||||
|
||||
describe('reducerTester', () => {
|
||||
describe('when reducer mutates state', () => {
|
||||
it('then it should throw', () => {
|
||||
expect(() => {
|
||||
reducerTester()
|
||||
.givenReducer(mutatingReducer, initialState)
|
||||
.whenActionIsDispatched(dummyAction('some string'));
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when reducer does not mutate state', () => {
|
||||
it('then it should not throw', () => {
|
||||
expect(() => {
|
||||
reducerTester()
|
||||
.givenReducer(okReducer, initialState)
|
||||
.whenActionIsDispatched(dummyAction('some string'));
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
79
public/test/core/redux/reducerTester.ts
Normal file
79
public/test/core/redux/reducerTester.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Reducer } from 'redux';
|
||||
|
||||
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
|
||||
export interface Given<State> {
|
||||
givenReducer: (reducer: Reducer<State, ActionOf<any>>, state: State) => When<State>;
|
||||
}
|
||||
|
||||
export interface When<State> {
|
||||
whenActionIsDispatched: (action: ActionOf<any>) => Then<State>;
|
||||
}
|
||||
|
||||
export interface Then<State> {
|
||||
thenStateShouldEqual: (state: State) => Then<State>;
|
||||
}
|
||||
|
||||
interface ObjectType extends Object {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const deepFreeze = <T>(obj: T): T => {
|
||||
Object.freeze(obj);
|
||||
|
||||
const isNotException = (object: any, propertyName: any) =>
|
||||
typeof object === 'function'
|
||||
? propertyName !== 'caller' && propertyName !== 'callee' && propertyName !== 'arguments'
|
||||
: true;
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
if (obj && obj instanceof Object) {
|
||||
const object: ObjectType = obj;
|
||||
Object.getOwnPropertyNames(object).forEach(propertyName => {
|
||||
const objectProperty: any = object[propertyName];
|
||||
if (
|
||||
hasOwnProp.call(object, propertyName) &&
|
||||
isNotException(object, propertyName) &&
|
||||
objectProperty &&
|
||||
(typeof objectProperty === 'object' || typeof objectProperty === 'function') &&
|
||||
Object.isFrozen(objectProperty) === false
|
||||
) {
|
||||
deepFreeze(objectProperty);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
interface ReducerTester<State> extends Given<State>, When<State>, Then<State> {}
|
||||
|
||||
export const reducerTester = <State>(): Given<State> => {
|
||||
let reducerUnderTest: Reducer<State, ActionOf<any>> = null;
|
||||
let resultingState: State = null;
|
||||
let initialState: State = null;
|
||||
|
||||
const givenReducer = (reducer: Reducer<State, ActionOf<any>>, state: State): When<State> => {
|
||||
reducerUnderTest = reducer;
|
||||
initialState = { ...state };
|
||||
initialState = deepFreeze(initialState);
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const whenActionIsDispatched = (action: ActionOf<any>): Then<State> => {
|
||||
resultingState = reducerUnderTest(initialState, action);
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const thenStateShouldEqual = (state: State): Then<State> => {
|
||||
expect(state).toEqual(resultingState);
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const instance: ReducerTester<State> = { thenStateShouldEqual, givenReducer, whenActionIsDispatched };
|
||||
|
||||
return instance;
|
||||
};
|
||||
Reference in New Issue
Block a user