Skip to content

Commit b14967f

Browse files
author
FrancescoMauto
committed
[DSC-2887] edit: updated route reducer
1 parent 2b16910 commit b14967f

4 files changed

Lines changed: 208 additions & 17 deletions

File tree

src/app/app.metareducers.spec.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { AppState } from './app.reducer';
2+
import { universalMetaReducer } from './app.metareducers';
3+
import { StoreActionTypes } from './store.actions';
4+
5+
describe('universalMetaReducer', () => {
6+
const mockReducer = (state: any, action: any) => state;
7+
8+
it('should pass state through for unknown action', () => {
9+
const state = { core: { route: { queryParams: { a: '1' }, params: {} } } };
10+
const result = universalMetaReducer(mockReducer)(state, { type: 'UNKNOWN' });
11+
expect(result).toEqual(state);
12+
});
13+
14+
describe('REHYDRATE', () => {
15+
it('should merge payload into state', () => {
16+
const state = { core: { existing: true } };
17+
const payload = { core: { route: { queryParams: { f: ['x'] }, params: {} } } };
18+
const result = universalMetaReducer(mockReducer)(state, {
19+
type: StoreActionTypes.REHYDRATE,
20+
payload,
21+
});
22+
expect(result.core.existing).toBe(true);
23+
});
24+
25+
it('should reset core.route to empty after rehydration', () => {
26+
const state = { core: { route: { queryParams: { f: ['x'] }, params: { id: '123' } } } };
27+
const payload = { core: { route: { queryParams: { stale: ['val'] }, params: { stale: 'val' } } } };
28+
const result = universalMetaReducer(mockReducer)(state, {
29+
type: StoreActionTypes.REHYDRATE,
30+
payload,
31+
});
32+
expect(result.core.route).toEqual({ queryParams: {}, params: {} });
33+
});
34+
35+
it('should not crash when state.core is undefined', () => {
36+
const state = {};
37+
const payload = { core: { route: { queryParams: { a: '1' }, params: {} } } };
38+
const result = universalMetaReducer(mockReducer)(state, {
39+
type: StoreActionTypes.REHYDRATE,
40+
payload,
41+
});
42+
expect(result).toEqual(payload);
43+
});
44+
45+
it('should preserve other core properties when resetting route', () => {
46+
const state = { core: { route: {}, otherProp: 'keep-me' } };
47+
const payload = { core: { route: { queryParams: { x: '1' }, params: {} } } };
48+
const result = universalMetaReducer(mockReducer)(state, {
49+
type: StoreActionTypes.REHYDRATE,
50+
payload,
51+
});
52+
expect(result.core.otherProp).toBe('keep-me');
53+
expect(result.core.route).toEqual({ queryParams: {}, params: {} });
54+
});
55+
56+
it('should handle REPLAY action (no-op)', () => {
57+
const state = { core: { route: { queryParams: { a: '1' }, params: {} } } };
58+
const result = universalMetaReducer(mockReducer)(state, {
59+
type: StoreActionTypes.REPLAY,
60+
});
61+
expect(result).toEqual(state);
62+
});
63+
});
64+
});

src/app/app.metareducers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export function universalMetaReducer(reducer) {
1919
switch (action.type) {
2020
case StoreActionTypes.REHYDRATE:
2121
state = Object.assign({}, state, action.payload);
22+
if (state.core) {
23+
state.core = Object.assign({}, state.core, {
24+
route: { queryParams: {}, params: {} }
25+
});
26+
}
2227
break;
2328
case StoreActionTypes.REPLAY:
2429
default:
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { Params } from '@angular/router';
2+
3+
import {
4+
AddParameterAction,
5+
AddQueryParameterAction,
6+
ResetRouteStateAction,
7+
SetParameterAction,
8+
SetParametersAction,
9+
SetQueryParameterAction,
10+
SetQueryParametersAction,
11+
} from './route.actions';
12+
import { routeReducer, RouteState } from './route.reducer';
13+
14+
describe('routeReducer', () => {
15+
const initialState: RouteState = {
16+
queryParams: {},
17+
params: {},
18+
};
19+
20+
it('should return initial state for unknown action', () => {
21+
const state = routeReducer(undefined, { type: 'UNKNOWN' } as any);
22+
expect(state).toEqual(initialState);
23+
});
24+
25+
describe('RESET', () => {
26+
it('should reset state to initialState', () => {
27+
const state: RouteState = {
28+
queryParams: { f: { author: ['Smith'] } },
29+
params: { id: '123', configuration: 'default' },
30+
};
31+
const result = routeReducer(state, new ResetRouteStateAction());
32+
expect(result).toEqual(initialState);
33+
});
34+
});
35+
36+
describe('SET_PARAMETERS', () => {
37+
it('should replace all params with action payload', () => {
38+
const state: RouteState = { ...initialState };
39+
const payload: Params = { id: '123', configuration: 'default' };
40+
const result = routeReducer(state, new SetParametersAction(payload));
41+
expect(result.params).toEqual(payload);
42+
expect(result.queryParams).toEqual(state.queryParams);
43+
});
44+
45+
it('should clear params when payload is empty', () => {
46+
const state: RouteState = {
47+
queryParams: {},
48+
params: { id: '123' },
49+
};
50+
const result = routeReducer(state, new SetParametersAction({}));
51+
expect(result.params).toEqual({});
52+
expect(result.queryParams).toEqual(state.queryParams);
53+
});
54+
});
55+
56+
describe('SET_QUERY_PARAMETERS', () => {
57+
it('should replace all queryParams with action payload', () => {
58+
const state: RouteState = { ...initialState };
59+
const payload: Params = { 'f.author': ['Smith'], 'spc.page': '1' };
60+
const result = routeReducer(state, new SetQueryParametersAction(payload));
61+
expect(result.queryParams).toEqual(payload);
62+
expect(result.params).toEqual(state.params);
63+
});
64+
65+
it('should clear queryParams when payload is empty', () => {
66+
const state: RouteState = {
67+
queryParams: { 'f.author': ['Smith'] },
68+
params: {},
69+
};
70+
const result = routeReducer(state, new SetQueryParametersAction({}));
71+
expect(result.queryParams).toEqual({});
72+
expect(result.params).toEqual(state.params);
73+
});
74+
});
75+
76+
describe('SET_PARAMETER', () => {
77+
it('should set a single param key-value pair', () => {
78+
const state: RouteState = {
79+
queryParams: {},
80+
params: { id: '123' },
81+
};
82+
const result = routeReducer(state, new SetParameterAction('configuration', 'default'));
83+
expect(result.params).toEqual({ id: '123', configuration: 'default' });
84+
});
85+
});
86+
87+
describe('SET_QUERY_PARAMETER', () => {
88+
it('should set a single query param key-value pair', () => {
89+
const state: RouteState = {
90+
queryParams: { 'f.author': ['Smith'] },
91+
params: {},
92+
};
93+
const result = routeReducer(state, new SetQueryParameterAction('tab', 'publications'));
94+
expect(result.queryParams).toEqual({ 'f.author': ['Smith'], tab: 'publications' });
95+
});
96+
});
97+
98+
describe('ADD_PARAMETER', () => {
99+
it('should append value to existing param key', () => {
100+
const state: RouteState = {
101+
queryParams: {},
102+
params: { tag: ['a'] },
103+
};
104+
const result = routeReducer(state, new AddParameterAction('tag', 'b'));
105+
expect(result.params.tag).toEqual(['a', 'b']);
106+
});
107+
108+
it('should create new param key with value array when key does not exist', () => {
109+
const state: RouteState = { ...initialState };
110+
const result = routeReducer(state, new AddParameterAction('tag', 'new'));
111+
expect(result.params.tag).toEqual(['new']);
112+
});
113+
});
114+
115+
describe('ADD_QUERY_PARAMETER', () => {
116+
it('should append value to existing query param key', () => {
117+
const state: RouteState = {
118+
queryParams: { 'f.author': ['Smith'] },
119+
params: {},
120+
};
121+
const result = routeReducer(state, new AddQueryParameterAction('f.author', 'Jones'));
122+
expect(result.queryParams['f.author']).toEqual(['Smith', 'Jones']);
123+
});
124+
125+
it('should create new query param key with value array when key does not exist', () => {
126+
const state: RouteState = { ...initialState };
127+
const result = routeReducer(state, new AddQueryParameterAction('tag', 'new'));
128+
expect(result.queryParams.tag).toEqual(['new']);
129+
});
130+
});
131+
});

src/app/core/services/route.reducer.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {
77
RouteActions,
88
RouteActionTypes,
99
SetParameterAction,
10-
SetParametersAction,
1110
SetQueryParameterAction,
12-
SetQueryParametersAction,
1311
} from './route.actions';
1412

1513
/**
@@ -39,10 +37,14 @@ export function routeReducer(state = initialState, action: RouteActions): RouteS
3937
return initialState;
4038
}
4139
case RouteActionTypes.SET_PARAMETERS: {
42-
return setParameters(state, action as SetParametersAction, 'params');
40+
return Object.assign({}, state, {
41+
params: isNotEmpty(action.payload) ? action.payload : {},
42+
});
4343
}
4444
case RouteActionTypes.SET_QUERY_PARAMETERS: {
45-
return setParameters(state, action as SetQueryParametersAction, 'queryParams');
45+
return Object.assign({}, state, {
46+
queryParams: isNotEmpty(action.payload) ? action.payload : {},
47+
});
4648
}
4749
case RouteActionTypes.ADD_PARAMETER: {
4850
return addParameter(state, action as AddParameterAction, 'params');
@@ -68,7 +70,7 @@ export function routeReducer(state = initialState, action: RouteActions): RouteS
6870
* @param action The add action to perform on the current state
6971
* @param paramType The type of parameter to add: route or query parameter
7072
*/
71-
function addParameter(state: RouteState, action: AddParameterAction | AddQueryParameterAction, paramType: string): RouteState {
73+
function addParameter(state: RouteState, action: AddParameterAction | AddQueryParameterAction, paramType: 'params' | 'queryParams'): RouteState {
7274
const subState = state[paramType];
7375
const existingValues = subState[action.payload.key] || [];
7476
const newValues = [...existingValues, action.payload.value];
@@ -82,18 +84,7 @@ function addParameter(state: RouteState, action: AddParameterAction | AddQueryPa
8284
* @param action The set action to perform on the current state
8385
* @param paramType The type of parameter to set: route or query parameter
8486
*/
85-
function setParameters(state: RouteState, action: SetParametersAction | SetQueryParametersAction, paramType: string): RouteState {
86-
const param = isNotEmpty(action.payload) ? { [paramType]: { [action.payload.key]: action.payload.value } } : {};
87-
return Object.assign({}, state, param);
88-
}
89-
90-
/**
91-
* Set a route or query parameter in the store
92-
* @param state The current state
93-
* @param action The set action to perform on the current state
94-
* @param paramType The type of parameter to set: route or query parameter
95-
*/
96-
function setParameter(state: RouteState, action: SetParameterAction | SetQueryParameterAction, paramType: string): RouteState {
87+
function setParameter(state: RouteState, action: SetParameterAction | SetQueryParameterAction, paramType: 'params' | 'queryParams'): RouteState {
9788
const subState = state[paramType];
9889
const newSubstate = Object.assign({}, subState, { [action.payload.key]: action.payload.value });
9990
return Object.assign({}, state, { [paramType]: newSubstate });

0 commit comments

Comments
 (0)