MM-61198: react-hooks-testing-library and renderHookWithContext (#28886)

Этот коммит содержится в:
Caleb Roseland
2024-10-29 08:56:22 -05:00
коммит произвёл GitHub
родитель da2a84e99d
Коммит 3ac1c9821b
4 изменённых файлов: 147 добавлений и 20 удалений

Просмотреть файл

@@ -110,6 +110,7 @@
"@stylistic/stylelint-plugin": "2.1.0",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "13.5.0",
"@types/bootstrap": "4.5.0",
"@types/country-list": "2.1.0",

Просмотреть файл

@@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {DeepPartial} from '@mattermost/types/utilities';
import {renderHookWithContext} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import type {GlobalState} from 'types/store';
import {useThreadRouting} from './hooks';
describe('components/threading/hooks', () => {
const mockUser = TestHelper.getUserMock();
const mockTeam = TestHelper.getTeamMock();
const mockState: DeepPartial<GlobalState> = {
entities: {
users: {
currentUserId: mockUser.id,
},
teams: {
currentTeamId: mockTeam.id,
},
},
};
describe('useThreadRouting', () => {
test('should indicate current team and user', () => {
const {result} = renderHookWithContext(() => useThreadRouting(), mockState);
expect(result.current.currentUserId).toBe(mockUser.id);
expect(result.current.currentTeamId).toBe(mockTeam.id);
});
});
});

Просмотреть файл

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {render} from '@testing-library/react';
import {renderHook} from '@testing-library/react-hooks';
import userEvent from '@testing-library/user-event';
import type {History} from 'history';
import {createBrowserHistory} from 'history';
@@ -54,28 +55,14 @@ export const renderWithContext = (
store: testStore,
};
// This should wrap the component in roughly the same providers used in App and RootProvider
function WrapComponent(props: {children: React.ReactElement}) {
// Every time this is called, these values should be updated from `renderState`
return (
<Provider store={renderState.store}>
<Router history={renderState.history}>
<IntlProvider
locale={renderState.options.locale}
messages={renderState.options.intlMessages}
>
<WebSocketContext.Provider value={WebSocketClient}>
{props.children}
</WebSocketContext.Provider>
</IntlProvider>
</Router>
</Provider>
);
}
replaceGlobalStore(() => renderState.store);
const results = render(component, {wrapper: WrapComponent});
const results = render(component, {
wrapper: ({children}) => {
// Every time this is called, these values should be updated from `renderState`
return <Providers {...renderState}>{children}</Providers>;
},
});
return {
...results,
@@ -106,6 +93,36 @@ export const renderWithContext = (
};
};
export const renderHookWithContext = <TProps, TResult>(
callback: (props: TProps) => TResult,
initialState: DeepPartial<GlobalState> = {},
partialOptions?: FullContextOptions,
) => {
const options = {
intlMessages: partialOptions?.intlMessages,
locale: partialOptions?.locale ?? 'en',
useMockedStore: partialOptions?.useMockedStore ?? false,
};
const testStore = configureOrMockStore(initialState, options.useMockedStore, partialOptions?.pluginReducers);
// Store these in an object so that they can be maintained through rerenders
const renderState = {
callback,
history: partialOptions?.history ?? createBrowserHistory(),
options,
store: testStore,
};
replaceGlobalStore(() => renderState.store);
return renderHook(callback, {
wrapper: ({children}) => {
// Every time this is called, these values should be updated from `renderState`
return <Providers {...renderState}>{children}</Providers>;
},
});
};
function configureOrMockStore<T>(initialState: DeepPartial<T>, useMockedStore: boolean, extraReducersKeys?: string[]) {
let testReducers;
if (extraReducersKeys) {
@@ -133,3 +150,29 @@ function replaceGlobalStore(getStore: () => any) {
// This may stop working if getStore starts to return new results
jest.spyOn(globalStore, 'subscribe').mockImplementation((...args) => getStore().subscribe(...args));
}
type Opts = {
intlMessages: Record<string, string> | undefined;
locale: string;
useMockedStore: boolean;
}
type RenderStateProps = {children: React.ReactNode; store: any; history: History<unknown>; options: Opts}
// This should wrap the component in roughly the same providers used in App and RootProvider
const Providers = ({children, store, history, options}: RenderStateProps) => {
return (
<Provider store={store}>
<Router history={history}>
<IntlProvider
locale={options.locale}
messages={options.intlMessages}
>
<WebSocketContext.Provider value={WebSocketClient}>
{children}
</WebSocketContext.Provider>
</IntlProvider>
</Router>
</Provider>
);
};

47
webapp/package-lock.json сгенерированный
Просмотреть файл

@@ -159,6 +159,7 @@
"@stylistic/stylelint-plugin": "2.1.0",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "13.5.0",
"@types/bootstrap": "4.5.0",
"@types/country-list": "2.1.0",
@@ -5169,6 +5170,36 @@
"react-dom": "*"
}
},
"node_modules/@testing-library/react-hooks": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz",
"integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"react-error-boundary": "^3.1.0"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"@types/react": "^16.9.0 || ^17.0.0",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0",
"react-test-renderer": "^16.9.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-test-renderer": {
"optional": true
}
}
},
"node_modules/@testing-library/user-event": {
"version": "13.5.0",
"dev": true,
@@ -19236,6 +19267,22 @@
"react": "17.0.2"
}
},
"node_modules/react-error-boundary": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"engines": {
"node": ">=10",
"npm": ">=6"
},
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"license": "MIT"