MM-61198: react-hooks-testing-library and renderHookWithContext (#28886)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
da2a84e99d
Коммит
3ac1c9821b
@@ -110,6 +110,7 @@
|
|||||||
"@stylistic/stylelint-plugin": "2.1.0",
|
"@stylistic/stylelint-plugin": "2.1.0",
|
||||||
"@testing-library/jest-dom": "5.16.4",
|
"@testing-library/jest-dom": "5.16.4",
|
||||||
"@testing-library/react": "12.1.4",
|
"@testing-library/react": "12.1.4",
|
||||||
|
"@testing-library/react-hooks": "8.0.1",
|
||||||
"@testing-library/user-event": "13.5.0",
|
"@testing-library/user-event": "13.5.0",
|
||||||
"@types/bootstrap": "4.5.0",
|
"@types/bootstrap": "4.5.0",
|
||||||
"@types/country-list": "2.1.0",
|
"@types/country-list": "2.1.0",
|
||||||
|
|||||||
36
webapp/channels/src/components/threading/hooks.test.ts
Обычный файл
36
webapp/channels/src/components/threading/hooks.test.ts
Обычный файл
@@ -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.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {render} from '@testing-library/react';
|
import {render} from '@testing-library/react';
|
||||||
|
import {renderHook} from '@testing-library/react-hooks';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import type {History} from 'history';
|
import type {History} from 'history';
|
||||||
import {createBrowserHistory} from 'history';
|
import {createBrowserHistory} from 'history';
|
||||||
@@ -54,28 +55,14 @@ export const renderWithContext = (
|
|||||||
store: testStore,
|
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);
|
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 {
|
return {
|
||||||
...results,
|
...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[]) {
|
function configureOrMockStore<T>(initialState: DeepPartial<T>, useMockedStore: boolean, extraReducersKeys?: string[]) {
|
||||||
let testReducers;
|
let testReducers;
|
||||||
if (extraReducersKeys) {
|
if (extraReducersKeys) {
|
||||||
@@ -133,3 +150,29 @@ function replaceGlobalStore(getStore: () => any) {
|
|||||||
// This may stop working if getStore starts to return new results
|
// This may stop working if getStore starts to return new results
|
||||||
jest.spyOn(globalStore, 'subscribe').mockImplementation((...args) => getStore().subscribe(...args));
|
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
сгенерированный
47
webapp/package-lock.json
сгенерированный
@@ -159,6 +159,7 @@
|
|||||||
"@stylistic/stylelint-plugin": "2.1.0",
|
"@stylistic/stylelint-plugin": "2.1.0",
|
||||||
"@testing-library/jest-dom": "5.16.4",
|
"@testing-library/jest-dom": "5.16.4",
|
||||||
"@testing-library/react": "12.1.4",
|
"@testing-library/react": "12.1.4",
|
||||||
|
"@testing-library/react-hooks": "8.0.1",
|
||||||
"@testing-library/user-event": "13.5.0",
|
"@testing-library/user-event": "13.5.0",
|
||||||
"@types/bootstrap": "4.5.0",
|
"@types/bootstrap": "4.5.0",
|
||||||
"@types/country-list": "2.1.0",
|
"@types/country-list": "2.1.0",
|
||||||
@@ -5169,6 +5170,36 @@
|
|||||||
"react-dom": "*"
|
"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": {
|
"node_modules/@testing-library/user-event": {
|
||||||
"version": "13.5.0",
|
"version": "13.5.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -19236,6 +19267,22 @@
|
|||||||
"react": "17.0.2"
|
"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": {
|
"node_modules/react-fast-compare": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user