diff --git a/generators/react/resources/package.json b/generators/react/resources/package.json index 754639419b01..f9a1ce8e9793 100644 --- a/generators/react/resources/package.json +++ b/generators/react/resources/package.json @@ -3,7 +3,7 @@ "@fortawesome/fontawesome-svg-core": "6.5.1", "@fortawesome/free-solid-svg-icons": "6.5.1", "@fortawesome/react-fontawesome": "0.2.0", - "@reduxjs/toolkit": "1.9.7", + "@reduxjs/toolkit": "2.0.1", "axios": "1.6.2", "bootstrap": "5.3.2", "bootswatch": "5.3.2", @@ -14,14 +14,13 @@ "react-hook-form": "7.49.2", "react-jhipster": "0.25.3", "react-loadable": "5.5.0", - "react-redux": "8.1.3", + "react-redux": "9.0.4", "react-redux-loading-bar": "5.0.5", "react-router-dom": "6.21.0", "react-toastify": "9.1.3", "react-transition-group": "4.4.5", "reactstrap": "9.2.1", - "redux": "4.2.1", - "redux-thunk": "2.4.2", + "redux": "5.0.0", "sonar-scanner": "3.1.0", "tslib": "2.6.2", "uuid": "9.0.1" @@ -34,7 +33,7 @@ "@types/node": "18.19.3", "@types/react": "18.2.45", "@types/react-dom": "18.2.17", - "@types/react-redux": "7.1.31", + "@types/react-redux": "7.1.33", "@types/redux": "3.6.31", "@types/webpack-env": "1.18.4", "@typescript-eslint/eslint-plugin": "6.14.0", diff --git a/generators/react/templates/package.json.ejs b/generators/react/templates/package.json.ejs index 144e5122f588..f413d8061218 100644 --- a/generators/react/templates/package.json.ejs +++ b/generators/react/templates/package.json.ejs @@ -51,7 +51,6 @@ "react-transition-group": "<%= nodeDependencies['react-transition-group'] %>", "reactstrap": "<%= nodeDependencies['reactstrap'] %>", "redux": "<%= nodeDependencies['redux'] %>", - "redux-thunk": "<%= nodeDependencies['redux-thunk'] %>", <%_ if (communicationSpringWebsocket) { _%> "rxjs": "6.6.3", "sockjs-client": "1.5.0", diff --git a/generators/react/templates/src/main/webapp/app/config/store.ts.ejs b/generators/react/templates/src/main/webapp/app/config/store.ts.ejs index 4443296f9796..b8af1dd3f711 100644 --- a/generators/react/templates/src/main/webapp/app/config/store.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/config/store.ts.ejs @@ -17,7 +17,7 @@ limitations under the License. -%> import { - AnyAction, + UnknownAction, configureStore, ThunkAction, <%_ if (microfrontend || applicationTypeGateway) { _%> @@ -60,7 +60,7 @@ const store = configureStore({ <%_ if (microfrontend || applicationTypeGateway) { _%> // Allow lazy loading of reducers https://github.com/reduxjs/redux/blob/master/docs/usage/CodeSplitting.md -interface InjectableStore extends Store { +interface InjectableStore extends Store { asyncReducers: ReducersMapObject; injectReducer(key: string, reducer: Reducer): void; } @@ -95,6 +95,6 @@ export type AppDispatch = typeof store.dispatch; export const useAppSelector: TypedUseSelectorHook = useSelector; export const useAppDispatch = () => useDispatch(); -export type AppThunk = ThunkAction; +export type AppThunk = ThunkAction; export default getStore; diff --git a/generators/react/templates/src/main/webapp/app/entities/_entityFolder_/_entityFile_-reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/entities/_entityFolder_/_entityFile_-reducer.spec.ts.ejs index 8e9a7330b352..1b21a67014bc 100644 --- a/generators/react/templates/src/main/webapp/app/entities/_entityFolder_/_entityFile_-reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/entities/_entityFolder_/_entityFile_-reducer.spec.ts.ejs @@ -22,8 +22,7 @@ let entityActionNamePlural = entityInstancePlural.toUpperCase(); _%> import axios from 'axios'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { configureStore } from '@reduxjs/toolkit'; import sinon from 'sinon'; <%_ if (paginationInfiniteScroll) { _%> import { parseHeaderForLinks } from 'react-jhipster'; @@ -167,7 +166,7 @@ describe('Entities reducer tests', () => { 'some message', state => { expect(state).toMatchObject({ - errorMessage: 'error message', + errorMessage: null, updateSuccess: false, updating: false }); @@ -270,9 +269,13 @@ describe('Entities reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.put = sinon.stub().returns(Promise.resolve(resolvedObject)); @@ -281,150 +284,81 @@ describe('Entities reducer tests', () => { }); it('dispatches FETCH_<%= entityActionName %>_LIST actions', async () => { - const expectedActions = [ - { - type: getEntities.pending.type - }, - { - type: getEntities.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getEntities({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = {}; + + const result = await getEntities(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getEntities.fulfilled.match(result)).toBe(true); }); <%_ if (searchEngineAny) { _%> it('dispatches SEARCH_<%= entityActionNamePlural %> actions', async () => { - const expectedActions = [ - { - type: searchEntities.pending.type - }, - { - type: searchEntities.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(searchEntities({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = {}; + + const result = await searchEntities(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(searchEntities.fulfilled.match(result)).toBe(true); }); <%_ } _%> it('dispatches FETCH_<%= entityActionName %> actions', async () => { - const expectedActions = [ - { - type: getEntity.pending.type - }, - { - type: getEntity.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getEntity(42666)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = 42666; + + const result = await getEntity(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getEntity.fulfilled.match(result)).toBe(true); }); <%_ if (!readOnly) { _%> it('dispatches CREATE_<%= entityActionName %> actions', async () => { - const expectedActions = [ - { - type: createEntity.pending.type - }, - <%_ if (!paginationInfiniteScroll) { _%> - { - type: getEntities.pending.type - }, - <%_ } _%> - { - type: createEntity.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(createEntity(<%- this.generateTestEntityPrimaryKey(primaryKey, 1) %>)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - <%_ if (!paginationInfiniteScroll) { _%> - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); - <%_ } _%> + const arg = <%- this.generateTestEntityPrimaryKey(primaryKey, 1) %>; + + const result = await createEntity(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(createEntity.fulfilled.match(result)).toBe(true); }); it('dispatches UPDATE_<%= entityActionName %> actions', async () => { - const expectedActions = [ - { - type: updateEntity.pending.type - }, - <%_ if (!paginationInfiniteScroll) { _%> - { - type: getEntities.pending.type - }, - <%_ } _%> - { - type: updateEntity.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(updateEntity(<%- this.generateTestEntityPrimaryKey(primaryKey, 1) %>)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - <%_ if (!paginationInfiniteScroll) { _%> - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); - <%_ } _%> + const arg = <%- this.generateTestEntityPrimaryKey(primaryKey, 1) %>; + + const result = await updateEntity(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(updateEntity.fulfilled.match(result)).toBe(true); }); it('dispatches PARTIAL_UPDATE_<%= entityActionName %> actions', async () => { - const expectedActions = [ - { - type: partialUpdateEntity.pending.type - }, - <%_ if (!paginationInfiniteScroll) { _%> - { - type: getEntities.pending.type - }, - <%_ } _%> - { - type: partialUpdateEntity.fulfilled.type, - payload: resolvedObject + const arg = { <%- primaryKey.name %>: <%- this.generateTestEntityId(primaryKey.type) %> }; - } - ]; - await store.dispatch(partialUpdateEntity({ <%- primaryKey.name %>: <%- this.generateTestEntityId(primaryKey.type) %> })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - <%_ if (!paginationInfiniteScroll) { _%> - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); - <%_ } _%> + const result = await partialUpdateEntity(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(partialUpdateEntity.fulfilled.match(result)).toBe(true); }); it('dispatches DELETE_<%= entityActionName %> actions', async () => { - const expectedActions = [ - { - type: deleteEntity.pending.type - }, - <%_ if (!paginationInfiniteScroll) { _%> - { - type: getEntities.pending.type - }, - <%_ } _%> - { - type: deleteEntity.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(deleteEntity(42666)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - <%_ if (!paginationInfiniteScroll) { _%> - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); - <%_ } _%> + const arg = 42666; + + const result = await deleteEntity(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(deleteEntity.fulfilled.match(result)).toBe(true); }); <%_ } _%> it('dispatches RESET actions', async () => { - const expectedActions = [reset()]; await store.dispatch(reset()); - expect(store.getActions()).toEqual(expectedActions); + expect(store.getState()).toEqual([expect.any(Object), expect.objectContaining(reset())]); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/modules/account/activate/activate.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/account/activate/activate.reducer.spec.ts.ejs index 06561a933386..a482c3a19cf6 100644 --- a/generators/react/templates/src/main/webapp/app/modules/account/activate/activate.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/account/activate/activate.reducer.spec.ts.ejs @@ -17,10 +17,9 @@ limitations under the License. -%> -import thunk from 'redux-thunk'; import axios from 'axios'; import sinon from 'sinon'; -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; import activate, { activateAction, reset } from './activate.reducer'; @@ -74,29 +73,32 @@ describe('Activate reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches ACTIVATE_ACCOUNT_PENDING and ACTIVATE_ACCOUNT_FULFILLED actions', async () => { - const expectedActions = [ - { - type: activateAction.pending.type, - }, - { - type: activateAction.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(activateAction('')); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = ''; + + const result = await activateAction(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(activateAction.fulfilled.match(result)).toBe(true); + expect(result.payload).toBe(resolvedObject); }); it('dispatches RESET actions', async () => { await store.dispatch(reset()); - expect(store.getActions()[0]).toMatchObject(reset()); + expect(store.getState()).toEqual([ + expect.any(Object), + expect.objectContaining(reset()), + ]); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/modules/account/password/password.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/account/password/password.reducer.spec.ts.ejs index 3f0c6b253bb5..91e4cdf929c5 100644 --- a/generators/react/templates/src/main/webapp/app/modules/account/password/password.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/account/password/password.reducer.spec.ts.ejs @@ -17,10 +17,9 @@ limitations under the License. -%> -import thunk from 'redux-thunk'; import axios from 'axios'; import sinon from 'sinon'; -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; <%_ if (enableTranslation) { _%> import { TranslatorContext } from 'react-jhipster'; <%_ } _%> @@ -90,30 +89,31 @@ describe('Password reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches UPDATE_PASSWORD_PENDING and UPDATE_PASSWORD_FULFILLED actions', async () => { + const arg = { currentPassword: '', newPassword: '' }; + + const result = await savePassword(arg)(dispatch, getState, extra); - const expectedActions = [ - { - type: savePassword.pending.type, - }, - { - type: savePassword.fulfilled.type, - payload: resolvedObject, - } - ]; - await store.dispatch(savePassword({ currentPassword: '', newPassword: '' })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(savePassword.fulfilled.match(result)).toBe(true); }); it('dispatches RESET actions', async () => { await store.dispatch(reset()); - expect(store.getActions()[0]).toMatchObject(reset()); + expect(store.getState()).toEqual([ + expect.any(Object), + expect.objectContaining(reset()), + ]); }); }); diff --git a/generators/react/templates/src/main/webapp/app/modules/account/register/register.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/account/register/register.reducer.spec.ts.ejs index 14f983b09ac0..20e46f4778fa 100644 --- a/generators/react/templates/src/main/webapp/app/modules/account/register/register.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/account/register/register.reducer.spec.ts.ejs @@ -17,10 +17,9 @@ limitations under the License. -%> -import thunk from 'redux-thunk'; import axios from 'axios'; import sinon from 'sinon'; -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; <%_ if (enableTranslation) { _%> import { TranslatorContext } from 'react-jhipster'; <%_ } _%> @@ -98,30 +97,32 @@ describe('Creating account tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches CREATE_ACCOUNT_PENDING and CREATE_ACCOUNT_FULFILLED actions', async () => { + const arg = { login: '', email: '', password: '' }; - const expectedActions = [ - { - type: handleRegister.pending.type, - }, - { - type: handleRegister.fulfilled.type, - payload: resolvedObject, - } - ]; - await store.dispatch(handleRegister({ login: '', email: '', password: '' })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await handleRegister(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(handleRegister.fulfilled.match(result)).toBe(true); + expect(result.payload).toBe(resolvedObject); }); it('dispatches RESET actions', async () => { await store.dispatch(reset()); - expect(store.getActions()[0]).toMatchObject(reset()); + expect(store.getState()).toEqual([ + expect.any(Object), + expect.objectContaining(reset()), + ]); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/modules/account/settings/settings.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/account/settings/settings.reducer.spec.ts.ejs index 5d9d72619eb0..0b41885af7e6 100644 --- a/generators/react/templates/src/main/webapp/app/modules/account/settings/settings.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/account/settings/settings.reducer.spec.ts.ejs @@ -16,8 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. -%> -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { configureStore } from '@reduxjs/toolkit'; import axios from 'axios'; import sinon from 'sinon'; <%_ if (enableTranslation) { _%> @@ -90,38 +89,33 @@ describe('Settings reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); -<%_ if (enableTranslation) { _%> - store = mockStore({ authentication: { account: { langKey: '<%= nativeLanguage %>' } } }); -<%_ } else { _%> - store = mockStore({}); -<%_ } _%> + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches UPDATE_ACCOUNT_PENDING and UPDATE_ACCOUNT_FULFILLED actions', async () => { - const expectedActions = [ - { - type: updateAccount.pending.type, - }, - { - type: updateAccount.fulfilled.type, - payload: resolvedObject, - }, - { - type: getAccount.pending.type - }, - ]; - await store.dispatch(saveAccountSettings({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + const arg = ''; + + const result = await updateAccount(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(updateAccount.fulfilled.match(result)).toBe(true); + expect(result.payload).toBe(resolvedObject); }); it('dispatches RESET actions', async () => { await store.dispatch(reset()); - expect(store.getActions()[0]).toMatchObject(reset()); + expect(store.getState()).toEqual([ + expect.any(Object), + expect.objectContaining(reset()), + ]); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/modules/administration/administration.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/administration/administration.reducer.spec.ts.ejs index f67ab5e22df7..ef45cbc50c93 100644 --- a/generators/react/templates/src/main/webapp/app/modules/administration/administration.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/administration/administration.reducer.spec.ts.ejs @@ -17,10 +17,9 @@ limitations under the License. -%> -import configureStore from 'redux-mock-store'; import axios from 'axios'; -import thunk from 'redux-thunk'; import sinon from 'sinon'; +import { configureStore } from '@reduxjs/toolkit'; import administration, { <%_ if (applicationTypeGateway && serviceDiscoveryAny) { _%> @@ -264,132 +263,78 @@ describe('Administration reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; <%_ if (applicationTypeGateway && serviceDiscoveryAny || withAdminUi) { _%> beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); }); <%_ } _%> <%_ if (applicationTypeGateway && serviceDiscoveryAny) { _%> it('dispatches FETCH_GATEWAY_ROUTE_PENDING and FETCH_GATEWAY_ROUTE_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getGatewayRoutes.pending.type - }, - { - type: getGatewayRoutes.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getGatewayRoutes()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getGatewayRoutes()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getGatewayRoutes.fulfilled.match(result)).toBe(true); + expect(result.payload).toBe(resolvedObject); }); <%_ } _%> <%_ if (withAdminUi) { _%> it('dispatches FETCH_HEALTH_PENDING and FETCH_HEALTH_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getSystemHealth.pending.type - }, - { - type: getSystemHealth.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getSystemHealth()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getSystemHealth()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getSystemHealth.fulfilled.match(result)).toBe(true); + expect(result.payload).toBe(resolvedObject); }); it('dispatches FETCH_METRICS_PENDING and FETCH_METRICS_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getSystemMetrics.pending.type, - }, - { - type: getSystemMetrics.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getSystemMetrics()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getSystemMetrics()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getSystemMetrics.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_THREAD_DUMP_PENDING and FETCH_THREAD_DUMP_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getSystemThreadDump.pending.type - }, - { - type: getSystemThreadDump.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getSystemThreadDump()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getSystemThreadDump()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getSystemThreadDump.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_LOGS_PENDING and FETCH_LOGS_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getLoggers.pending.type - }, - { - type: getLoggers.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getLoggers()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getLoggers()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getLoggers.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_LOGS_CHANGE_LEVEL_PENDING and FETCH_LOGS_CHANGE_LEVEL_FULFILLED actions', async () => { - const expectedActions = [ - { - type: setLoggers.pending.type - }, - { - type: setLoggers.fulfilled.type, - payload: resolvedObject - }, - { - type: getLoggers.pending.type - }, - ]; - await store.dispatch(changeLogLevel('ROOT', 'DEBUG')); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + const result = await setLoggers({ name: 'ROOT', configuredLevel: 'DEBUG' })(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(setLoggers.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_CONFIGURATIONS_PENDING and FETCH_CONFIGURATIONS_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getConfigurations.pending.type - }, - { - type: getConfigurations.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getConfigurations()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getConfigurations()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getConfigurations.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_ENV_PENDING and FETCH_ENV_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getEnv.pending.type - }, - { - type: getEnv.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getEnv()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getEnv()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getEnv.fulfilled.match(result)).toBe(true); }); <%_ } _%> }); diff --git a/generators/react/templates/src/main/webapp/app/modules/administration/user-management/user-management.reducer.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/modules/administration/user-management/user-management.reducer.spec.ts.ejs index 2168432fee5f..a52540523e43 100644 --- a/generators/react/templates/src/main/webapp/app/modules/administration/user-management/user-management.reducer.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/modules/administration/user-management/user-management.reducer.spec.ts.ejs @@ -17,9 +17,8 @@ limitations under the License. -%> -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; import axios from 'axios'; -import thunk from 'redux-thunk'; import sinon from 'sinon'; import userManagement, { @@ -219,9 +218,13 @@ describe('User management reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.put = sinon.stub().returns(Promise.resolve(resolvedObject)); axios.post = sinon.stub().returns(Promise.resolve(resolvedObject)); @@ -229,158 +232,95 @@ describe('User management reducer tests', () => { }); it('dispatches FETCH_USERS_AS_ADMIN_PENDING and FETCH_USERS_AS_ADMIN_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getUsersAsAdmin.pending.type, - }, - { - type: getUsersAsAdmin.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getUsersAsAdmin({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = {}; + + const result = await getUsersAsAdmin(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsersAsAdmin.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_USERS_AS_ADMIN_PENDING and FETCH_USERS_AS_ADMIN_FULFILLED actions with pagination options', async () => { - const expectedActions = [ - { - type: getUsersAsAdmin.pending.type, - }, - { - type: getUsersAsAdmin.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getUsersAsAdmin({ page: 1, size: 20, sort: 'id,desc' })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = { page: 1, size: 20, sort: 'id,desc' }; + + const result = await getUsersAsAdmin(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsersAsAdmin.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_USERS_PENDING and FETCH_USERS_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getUsers.pending.type, - }, - { - type: getUsers.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getUsers({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = {}; + + const result = await getUsers(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsers.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_USERS_PENDING and FETCH_USERS_FULFILLED actions with pagination options', async () => { - const expectedActions = [ - { - type: getUsers.pending.type, - }, - { - type: getUsers.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getUsers({ page: 1, size: 20, sort: 'id,desc' })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = { page: 1, size: 20, sort: 'id,desc' }; + + const result = await getUsers(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsers.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_ROLES_PENDING and FETCH_ROLES_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getRoles.pending.type, - }, - { - type: getRoles.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getRoles()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getRoles()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getRoles.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_USER_PENDING and FETCH_USER_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getUser.pending.type, - }, - { - type: getUser.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(getUser(username)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getUser(username)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUser.fulfilled.match(result)).toBe(true); }); it('dispatches CREATE_USER_PENDING and CREATE_USER_FULFILLED actions', async () => { - const expectedActions = [ - { - type: createUser.pending.type, - }, - { - type: getUsersAsAdmin.pending.type, - }, - { - type: createUser.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(createUser({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + const arg = {}; + + const result = await createUser(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(createUser.fulfilled.match(result)).toBe(true); }); it('dispatches UPDATE_USER_PENDING and UPDATE_USER_FULFILLED actions', async () => { - const expectedActions = [ - { - type: updateUser.pending.type, - }, - { - type: getUsersAsAdmin.pending.type, - }, - { - type: updateUser.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(updateUser({ login: username })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + const arg = { login: username }; + + const result = await updateUser(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(updateUser.fulfilled.match(result)).toBe(true); }); it('dispatches DELETE_USER_PENDING and DELETE_USER_FULFILLED actions', async () => { - const expectedActions = [ - { - type: deleteUser.pending.type, - }, - { - type: getUsersAsAdmin.pending.type, - }, - { - type: deleteUser.fulfilled.type, - payload: resolvedObject, - }, - ]; - await store.dispatch(deleteUser(username)); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + const result = await deleteUser(username)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(deleteUser.fulfilled.match(result)).toBe(true); }); it('dispatches RESET actions', async () => { - const expectedActions = [ - reset(), - ]; await store.dispatch(reset()); - expect(store.getActions()).toEqual(expectedActions); + expect(store.getState()).toEqual([ + expect.any(Object), + expect.objectContaining(reset()), + ]); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/shared/auth/private-route.spec.tsx.ejs b/generators/react/templates/src/main/webapp/app/shared/auth/private-route.spec.tsx.ejs index 44aacb190e0a..9f1c5d604783 100644 --- a/generators/react/templates/src/main/webapp/app/shared/auth/private-route.spec.tsx.ejs +++ b/generators/react/templates/src/main/webapp/app/shared/auth/private-route.spec.tsx.ejs @@ -5,7 +5,6 @@ import { render } from '@testing-library/react'; import { TranslatorContext } from 'react-jhipster'; <%_ } _%> import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import { AUTHORITIES } from 'app/config/constants'; @@ -22,7 +21,7 @@ describe('private-route component', () => { }); <%_ } _%> - const mockStore = configureStore([thunk]); + const mockStore = configureStore(); const wrapper = (Elem: JSX.Element, authentication) => { const store = mockStore({ authentication }); return render( diff --git a/generators/react/templates/src/main/webapp/app/shared/reducers/application-profile.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/shared/reducers/application-profile.spec.ts.ejs index 9fb3f4063686..99e4d6e54470 100644 --- a/generators/react/templates/src/main/webapp/app/shared/reducers/application-profile.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/shared/reducers/application-profile.spec.ts.ejs @@ -17,10 +17,9 @@ limitations under the License. -%> -import thunk from 'redux-thunk'; import axios from 'axios'; import sinon from 'sinon'; -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; import profile, { getProfile } from './application-profile'; @@ -71,25 +70,22 @@ describe('Profile reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches GET_SESSION_PENDING and GET_SESSION_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getProfile.pending.type - }, - { - type: getProfile.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getProfile()); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const result = await getProfile()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getProfile.fulfilled.match(result)).toBe(true); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/shared/reducers/authentication.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/shared/reducers/authentication.spec.ts.ejs index 4a14397d1eda..84cd5486f8cf 100644 --- a/generators/react/templates/src/main/webapp/app/shared/reducers/authentication.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/shared/reducers/authentication.spec.ts.ejs @@ -16,13 +16,12 @@ See the License for the specific language governing permissions and limitations under the License. -%> -import thunk from 'redux-thunk'; import axios from 'axios'; import sinon from 'sinon'; <%_ if (authenticationTypeJwt) { _%> import { Storage } from 'react-jhipster'; <%_ } _%> -import configureStore from 'redux-mock-store'; +import { configureStore, createReducer } from '@reduxjs/toolkit'; import authentication, { getSession, @@ -204,109 +203,70 @@ describe('Authentication reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); -<%_ if (enableTranslation) { _%> - store = mockStore({ authentication: { account: { langKey: '<%= nativeLanguage %>' } }, locale: { loadedLocales: ['<%= nativeLanguage %>'] } }); -<%_ } else { _%> - store = mockStore({ authentication: { account: { } } }); -<%_ } _%> + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches GET_SESSION_PENDING and GET_SESSION_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getAccount.pending.type - }, - <%_ if (enableTranslation) { _%> - { - type: getAccount.fulfilled.type, - payload: resolvedObject - }, - { - type: setLocale.pending.type, - }, - updateLocale('en'), - { - type: setLocale.fulfilled.type, - payload: 'en', - }, - <%_ } _%> - ]; - await store.dispatch(getSession()); - expect(store.getActions()).toMatchObject(expectedActions); + const result = await getAccount()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getAccount.fulfilled.match(result)).toBe(true); }); it('dispatches LOGOUT actions', async () => { -<%_ if (!authenticationTypeJwt) { _%> + <%_ if (!authenticationTypeJwt) { _%> axios.post = sinon.stub().returns(Promise.resolve({})); -<%_ } _%> - const expectedActions = [ -<%_ if (authenticationTypeJwt) { _%> - logoutSession(), -<%_ } else { _%> - { - type: logoutServer.pending.type - }, - { - type: logoutServer.fulfilled.type, - payload: {}, - }, - { - type: getAccount.pending.type, - }, -<%_ } _%> - ]; + + const result = await logoutServer()(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(logoutServer.fulfilled.match(result)).toBe(true); + <%_ } else { _%> await store.dispatch(logout()); -<%_ if (authenticationTypeJwt) { _%> - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); -<%_ } else { _%> - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); -<%_ } _%> + expect(store.getState()).toEqual([expect.any(Object), expect.objectContaining(logoutSession())]); + <%_ } _%> }); it('dispatches CLEAR_AUTH actions', async () => { - const expectedActions = [authError('message'), clearAuth()]; await store.dispatch(clearAuthentication('message')); - expect(store.getActions()).toEqual(expectedActions); + expect(store.getState()).toEqual([expect.any(Object), expect.objectContaining(authError('message')), clearAuth()]); }); <%_ if (!authenticationTypeOauth2) { _%> it('dispatches LOGIN, GET_SESSION and SET_LOCALE success and request actions', async () => { - <%_ if (authenticationTypeJwt) { _%> const loginResponse = { headers: { authorization: 'auth' } }; - <%_ } else { _%> - const loginResponse = { value: 'any' }; - <%_ } _%> axios.post = sinon.stub().returns(Promise.resolve(loginResponse)); - const expectedActions = [ - { - type: authenticate.pending.type - }, - { - type: authenticate.fulfilled.type, - payload: loginResponse - }, - { - type: getAccount.pending.type - }, - ]; - await store.dispatch(login('test', 'test')); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); - expect(store.getActions()[2]).toMatchObject(expectedActions[2]); + + const result = await authenticate('test', 'test')(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(authenticate.fulfilled.match(result)).toBe(true); }); <%_ } _%> }); <%_ if (authenticationTypeJwt) { _%> describe('clearAuthToken', () => { let store; + const reducer = createReducer( + { authentication: { account: { langKey: 'en' } } }, + (builder) => { + builder.addDefaultCase(() => {}) + } + ); beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({ authentication: { account: { langKey: 'en' } } }); + store = configureStore({ + reducer + }); }); it('clears the session token on clearAuthToken', async () => { const AUTH_TOKEN_KEY = '<%= jhiPrefixDashed %>-authenticationToken'; diff --git a/generators/react/templates/src/main/webapp/app/shared/reducers/locale.spec.ts.ejs b/generators/react/templates/src/main/webapp/app/shared/reducers/locale.spec.ts.ejs index 4ac12673a27a..df2c8b80e35b 100644 --- a/generators/react/templates/src/main/webapp/app/shared/reducers/locale.spec.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/shared/reducers/locale.spec.ts.ejs @@ -1,12 +1,13 @@ import axios from 'axios'; import sinon from 'sinon'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; +import { configureStore, createReducer } from '@reduxjs/toolkit'; import { TranslatorContext } from 'react-jhipster'; import locale, { setLocale, updateLocale, loaded, addTranslationSourcePrefix } from 'app/shared/reducers/locale'; const defaultLocale = '<%= nativeLanguage %>'; +const dispatch = jest.fn(); +const extra = {}; describe('Locale reducer tests', () => { it('should return the initial state', () => { @@ -45,8 +46,13 @@ describe('Locale reducer tests', () => { describe('setLocale reducer', () => { describe('with default language loaded', () => { let store; + const reducer = createReducer({ locale: { sourcePrefixes: '', loadedLocales: [defaultLocale], loadedKeys: [] } }, builder => { + builder.addDefaultCase(() => {}); + }); beforeEach(() => { - store = configureStore([thunk])({ locale: { loadedLocales: [defaultLocale], loadedKeys: [] } }); + store = configureStore({ + reducer, + }); axios.get = sinon.stub().returns(Promise.resolve({ key: 'value' })); }); @@ -54,26 +60,25 @@ describe('Locale reducer tests', () => { TranslatorContext.setDefaultLocale(defaultLocale); expect(Object.keys(TranslatorContext.context.translations)).not.toContainEqual(defaultLocale); - const expectedActions = [ - { - type: setLocale.pending.type, - }, - updateLocale(defaultLocale), - { - type: setLocale.fulfilled.type, - payload: defaultLocale, - }, - ]; - - await store.dispatch(setLocale(defaultLocale)); - expect(store.getActions()).toMatchObject(expectedActions); + const getState = jest.fn(() => ({ locale: { sourcePrefixes: '', loadedLocales: [defaultLocale], loadedKeys: [] } })); + + const result = await setLocale(defaultLocale)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(setLocale.fulfilled.match(result)).toBe(true); }); }); describe('with no language loaded', () => { let store; + const reducer = createReducer({ locale: { sourcePrefixes: [], loadedLocales: [], loadedKeys: [] } }, builder => { + builder.addDefaultCase(() => {}); + }); beforeEach(() => { - store = configureStore([thunk])({ locale: { sourcePrefixes: [], loadedLocales: [], loadedKeys: [] } }); + store = configureStore({ + reducer, + }); axios.get = sinon.stub().returns(Promise.resolve({ key: 'value' })); }); @@ -81,20 +86,13 @@ describe('Locale reducer tests', () => { TranslatorContext.setDefaultLocale(defaultLocale); expect(Object.keys(TranslatorContext.context.translations)).not.toContainEqual(defaultLocale); - const expectedActions = [ - { - type: setLocale.pending.type, - }, - loaded({ keys: [defaultLocale], locale: defaultLocale }), - updateLocale(defaultLocale), - { - type: setLocale.fulfilled.type, - payload: defaultLocale, - }, - ]; - - await store.dispatch(setLocale(defaultLocale)); - expect(store.getActions()).toMatchObject(expectedActions); + const getState = jest.fn(() => ({ locale: { sourcePrefixes: [], loadedLocales: [], loadedKeys: [] } })); + + const result = await setLocale(defaultLocale)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(setLocale.fulfilled.match(result)).toBe(true); }); }); }); @@ -104,77 +102,70 @@ describe('Locale reducer tests', () => { describe('with no prefixes and keys loaded', () => { let store; + const reducer = createReducer({ locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [] } }, builder => { + builder.addDefaultCase(() => {}); + }); beforeEach(() => { - store = configureStore([thunk])({ - locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [] }, + store = configureStore({ + reducer, }); axios.get = sinon.stub().returns(Promise.resolve({ key: 'value' })); }); it('dispatches loaded action with keys and sourcePrefix', async () => { - const expectedActions = [ - { - type: addTranslationSourcePrefix.pending.type, - }, - loaded({ keys: [`${sourcePrefix}${defaultLocale}`], sourcePrefix }), - { - type: addTranslationSourcePrefix.fulfilled.type, - payload: `${sourcePrefix}${defaultLocale}`, - }, - ]; - - await store.dispatch(addTranslationSourcePrefix(sourcePrefix)); - expect(store.getActions()).toMatchObject(expectedActions); + const getState = jest.fn(() => ({ locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [] } })); + + const result = await addTranslationSourcePrefix(sourcePrefix)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(addTranslationSourcePrefix.fulfilled.match(result)).toBe(true); }); }); describe('with prefix already added', () => { let store; + const reducer = createReducer({ locale: { currentLocale: defaultLocale, sourcePrefixes: [sourcePrefix], loadedLocales: [], loadedKeys: [] } }, builder => { + builder.addDefaultCase(() => {}); + }); beforeEach(() => { - store = configureStore([thunk])({ - locale: { currentLocale: defaultLocale, sourcePrefixes: [sourcePrefix], loadedLocales: [], loadedKeys: [] }, + store = configureStore({ + reducer, }); axios.get = sinon.stub().returns(Promise.resolve({ key: 'value' })); }); it("doesn't dispatches loaded action", async () => { - const expectedActions = [ - { - type: addTranslationSourcePrefix.pending.type, - }, - { - type: addTranslationSourcePrefix.fulfilled.type, - payload: `${sourcePrefix}${defaultLocale}`, - }, - ]; - - await store.dispatch(addTranslationSourcePrefix(sourcePrefix)); - expect(store.getActions()).toMatchObject(expectedActions); + const getState = jest.fn(() => ({ locale: { currentLocale: defaultLocale, sourcePrefixes: [sourcePrefix], loadedLocales: [], loadedKeys: [] } })); + + const result = await addTranslationSourcePrefix(sourcePrefix)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(addTranslationSourcePrefix.fulfilled.match(result)).toBe(true); }); }); describe('with key already loaded', () => { let store; + const reducer = createReducer({ locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [`${sourcePrefix}${defaultLocale}`] } }, builder => { + builder.addDefaultCase(() => {}); + }); beforeEach(() => { - store = configureStore([thunk])({ - locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [`${sourcePrefix}${defaultLocale}`] }, + store = configureStore({ + reducer, }); axios.get = sinon.stub().returns(Promise.resolve({ key: 'value' })); }); it("doesn't dispatches loaded action", async () => { - const expectedActions = [ - { - type: addTranslationSourcePrefix.pending.type, - }, - { - type: addTranslationSourcePrefix.fulfilled.type, - payload: `${sourcePrefix}${defaultLocale}`, - }, - ]; - - await store.dispatch(addTranslationSourcePrefix(sourcePrefix)); - expect(store.getActions()).toMatchObject(expectedActions); + const getState = jest.fn(() => ({ locale: { currentLocale: defaultLocale, sourcePrefixes: [], loadedLocales: [], loadedKeys: [`${sourcePrefix}${defaultLocale}`] } })); + + const result = await addTranslationSourcePrefix(sourcePrefix)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(addTranslationSourcePrefix.fulfilled.match(result)).toBe(true); }); }); }); diff --git a/generators/react/templates/src/main/webapp/app/shared/reducers/reducer.utils.ts.ejs b/generators/react/templates/src/main/webapp/app/shared/reducers/reducer.utils.ts.ejs index a10fdceed2b0..78a4fabaab60 100644 --- a/generators/react/templates/src/main/webapp/app/shared/reducers/reducer.utils.ts.ejs +++ b/generators/react/templates/src/main/webapp/app/shared/reducers/reducer.utils.ts.ejs @@ -17,7 +17,7 @@ limitations under the License. -%> import { - AnyAction, + UnknownAction, AsyncThunk, ActionReducerMapBuilder, createSlice, @@ -43,21 +43,21 @@ export type FulfilledAction = ReturnType; /** * Check if the async action type is rejected */ -export function isRejectedAction(action: AnyAction) { +export function isRejectedAction(action: UnknownAction) { return action.type.endsWith('/rejected'); } /** * Check if the async action type is pending */ -export function isPendingAction(action: AnyAction) { +export function isPendingAction(action: UnknownAction) { return action.type.endsWith('/pending'); } /** * Check if the async action type is completed */ -export function isFulfilledAction(action: AnyAction) { +export function isFulfilledAction(action: UnknownAction) { return action.type.endsWith('/fulfilled'); } @@ -137,7 +137,7 @@ export const createEntitySlice = -import configureStore from 'redux-mock-store'; +import { configureStore } from '@reduxjs/toolkit'; import axios from 'axios'; -import thunk from 'redux-thunk'; import sinon from 'sinon'; import userManagement, { @@ -67,40 +66,34 @@ describe('User management reducer tests', () => { let store; const resolvedObject = { value: 'whatever' }; + const getState = jest.fn(); + const dispatch = jest.fn(); + const extra = {}; beforeEach(() => { - const mockStore = configureStore([thunk]); - store = mockStore({}); + store = configureStore({ + reducer: (state = [], action) => [...state, action], + }); axios.get = sinon.stub().returns(Promise.resolve(resolvedObject)); }); it('dispatches FETCH_USERS_PENDING and FETCH_USERS_FULFILLED actions', async () => { - const expectedActions = [ - { - type: getUsers.pending.type - }, - { - type: getUsers.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getUsers({})); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = {}; + + const result = await getUsers(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsers.fulfilled.match(result)).toBe(true); }); it('dispatches FETCH_USERS_PENDING and FETCH_USERS_FULFILLED actions with pagination options', async () => { - const expectedActions = [ - { - type: getUsers.pending.type - }, - { - type: getUsers.fulfilled.type, - payload: resolvedObject - } - ]; - await store.dispatch(getUsers({ page: 1, size: 20, sort: 'id,desc' })); - expect(store.getActions()[0]).toMatchObject(expectedActions[0]); - expect(store.getActions()[1]).toMatchObject(expectedActions[1]); + const arg = { page: 1, size: 20, sort: 'id,desc' }; + + const result = await getUsers(arg)(dispatch, getState, extra); + + const pendingAction = dispatch.mock.calls[0][0]; + expect(pendingAction.meta.requestStatus).toBe('pending'); + expect(getUsers.fulfilled.match(result)).toBe(true); }); }); });