Skip to content

Commit 377dac8

Browse files
committed
DiffScene testing bug, no default state set immediately
1 parent d92df04 commit 377dac8

File tree

5 files changed

+160
-94
lines changed

5 files changed

+160
-94
lines changed

packages/api-explorer/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@
8888
"react": "^16.13.1",
8989
"react-diff-viewer": "^3.1.1",
9090
"react-dom": "^16.13.1",
91+
"react-helmet-async": "^1.3.0",
9192
"react-is": "^16.13.1",
93+
"react-query": "^3.39.2",
9294
"react-redux": "^7.2.3",
9395
"react-router": "^5.1.2",
9496
"react-router-dom": "^5.1.2",

packages/api-explorer/src/scenes/DiffScene/DiffScene.spec.tsx

Lines changed: 76 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -24,50 +24,43 @@
2424
2525
*/
2626
import React from 'react'
27-
import { screen, waitFor } from '@testing-library/react'
27+
import { screen, waitFor, render } from '@testing-library/react'
2828
import userEvent from '@testing-library/user-event'
2929

3030
import type { SpecItem, SpecList } from '@looker/sdk-codegen'
31-
import { useHistory } from 'react-router-dom'
32-
import * as routerLocation from 'react-router-dom'
33-
import type { Location } from 'history'
34-
import * as reactRedux from 'react-redux'
31+
import { useLocation } from 'react-router-dom'
32+
import { useDispatch } from 'react-redux'
3533
import { getLoadedSpecs } from '../../test-data'
3634
import {
3735
createTestStore,
36+
renderWithRouter,
3837
renderWithRouterAndReduxProvider,
3938
} from '../../test-utils'
40-
import { getApixAdaptor } from '../../utils'
39+
import { getApixAdaptor, MockedProvider, mockHistory } from '../../utils'
4140
import { DiffScene } from './DiffScene'
4241

43-
// jest.mock('react-router', () => {
44-
// const ReactRouter = jest.requireActual('react-router')
45-
// return {
46-
// ...ReactRouter,
47-
// useHistory: jest.fn().mockReturnValue({ push: jest.fn(), location }),
48-
// useLocation: jest.fn().mockReturnValue({ pathname: '/', search: '' }),
49-
// }
50-
// })
51-
52-
jest.mock('react-router', () => {
53-
const mockLocation = {
54-
pathname: '/',
55-
search: '',
42+
jest.mock('react-redux', () => {
43+
const reactRedux = jest.requireActual('react-redux')
44+
return {
45+
__esModule: true,
46+
...reactRedux,
47+
useDispatch: jest.fn(reactRedux.useDispatch),
5648
}
57-
const ReactRouter = jest.requireActual('react-router')
49+
})
50+
51+
jest.mock('react-router-dom', () => {
52+
const ReactRouter = jest.requireActual('react-router-dom')
53+
5854
return {
55+
__esModule: true,
5956
...ReactRouter,
6057
useHistory: jest.fn().mockReturnValue({
61-
push: jest.fn((location) => {
62-
mockLocation.pathname = location.pathname
63-
mockLocation.search = location.search
64-
}),
58+
push: jest.fn(),
6559
location,
60+
useLocation: jest
61+
.fn()
62+
.mockReturnValue({ pathname: '/4.0/diff/3.1', search: '' }),
6663
}),
67-
useLocation: jest.fn(() => ({
68-
pathname: jest.fn().mockReturnValue(mockLocation.pathname),
69-
search: jest.fn().mockReturnValue(mockLocation.search),
70-
})),
7164
}
7265
})
7366

@@ -82,6 +75,7 @@ const mockApixAdaptor = new MockApixAdaptor()
8275
jest.mock('../../utils/apixAdaptor', () => {
8376
const apixAdaptor = jest.requireActual('../../utils/apixAdaptor')
8477
return {
78+
__esModule: true,
8579
...apixAdaptor,
8680
getApixAdaptor: jest.fn(),
8781
}
@@ -98,30 +92,24 @@ describe('DiffScene', () => {
9892
Element.prototype.scrollIntoView = jest.fn()
9993

10094
const toggleNavigation = () => false
101-
test('toggling comparison option pushes param to url', async () => {
102-
const { push } = useHistory()
103-
jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(mockDispatch)
95+
test.only('updating url dispatches store action', () => {
96+
// skipping test due to an issue with rerender callback returned from @looker/redux
97+
;(useDispatch as jest.Mock).mockReturnValue(mockDispatch)
10498
const store = createTestStore({
105-
specs: { specs, currentSpecKey: '3.1' },
106-
settings: { initialized: true, diffOptions: [] },
99+
specs: { specs, currentSpecKey: '4.0' },
100+
settings: { initialized: true },
107101
})
108-
renderWithRouterAndReduxProvider(
109-
<DiffScene specs={specs} toggleNavigation={toggleNavigation} />,
110-
['/diff/3.1'],
111-
store
112-
)
113-
userEvent.click(screen.getByPlaceholderText('Comparison options'))
114-
userEvent.click(
115-
screen.getByRole('option', {
116-
name: 'Missing',
117-
})
118-
)
119-
await waitFor(() => {
120-
expect(push).toHaveBeenLastCalledWith({
121-
pathname: '/',
122-
search: 'opts=missing',
123-
})
102+
const history = mockHistory({
103+
initialEntries: ['/4.0/diff/3.1'],
124104
})
105+
const MockScene = (
106+
<MockedProvider history={history} store={store}>
107+
<DiffScene toggleNavigation={toggleNavigation} />
108+
</MockedProvider>
109+
)
110+
const { rerender } = renderWithRouterAndReduxProvider(MockScene)
111+
history.push('/4.0/diff/3.1?opts=missing')
112+
rerender(MockScene)
125113
expect(mockDispatch).toHaveBeenLastCalledWith({
126114
payload: { diffOptions: ['missing'] },
127115
type: 'settings/setDiffOptionsAction',
@@ -130,45 +118,45 @@ describe('DiffScene', () => {
130118
// TODO: test that toggling another will push both options to store/url
131119
})
132120

133-
test.todo(
134-
'rendering scene with opts param in url sets selected options in selector',
135-
async () => {
136-
jest.spyOn(routerLocation, 'useLocation').mockReturnValue({
137-
pathname: `/`,
138-
search: `opts=missing%2Ctype%2Cresponse`,
139-
} as unknown as Location)
140-
const store = createTestStore({
141-
specs: { specs, currentSpecKey: '3.1' },
142-
settings: { initialized: true, diffOptions: [] },
143-
})
144-
jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(mockDispatch)
145-
renderWithRouterAndReduxProvider(
146-
<DiffScene specs={specs} toggleNavigation={toggleNavigation} />,
147-
['/diff/3.1'],
148-
store
149-
)
150-
expect(mockDispatch).toHaveBeenLastCalledWith({
151-
payload: { diffOptions: ['missing', 'type', 'response'] },
152-
type: 'settings/setDiffOptionsAction',
153-
})
154-
expect(
155-
screen.getByRole('option', {
156-
name: 'Missing',
157-
})
158-
).toBeInTheDocument()
159-
expect(
160-
screen.getByRole('option', {
161-
name: 'Type',
162-
})
163-
).toBeInTheDocument()
164-
expect(
165-
screen.getByRole('option', {
166-
name: 'Response',
167-
})
168-
).toBeInTheDocument()
169-
}
170-
)
171-
121+
// test.todo(
122+
// 'rendering scene with opts param in url sets selected options in selector',
123+
// async () => {
124+
// jest.spyOn(routerLocation, 'useLocation').mockReturnValue({
125+
// pathname: `/`,
126+
// search: `opts=missing%2Ctype%2Cresponse`,
127+
// } as unknown as Location)
128+
// const store = createTestStore({
129+
// specs: { specs, currentSpecKey: '3.1' },
130+
// settings: { initialized: true, diffOptions: [] },
131+
// })
132+
// jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(mockDispatch)
133+
// renderWithRouterAndReduxProvider(
134+
// <DiffScene specs={specs} toggleNavigation={toggleNavigation} />,
135+
// ['/diff/3.1'],
136+
// store
137+
// )
138+
// expect(mockDispatch).toHaveBeenLastCalledWith({
139+
// payload: { diffOptions: ['missing', 'type', 'response'] },
140+
// type: 'settings/setDiffOptionsAction',
141+
// })
142+
// expect(
143+
// screen.getByRole('option', {
144+
// name: 'Missing',
145+
// })
146+
// ).toBeInTheDocument()
147+
// expect(
148+
// screen.getByRole('option', {
149+
// name: 'Type',
150+
// })
151+
// ).toBeInTheDocument()
152+
// expect(
153+
// screen.getByRole('option', {
154+
// name: 'Response',
155+
// })
156+
// ).toBeInTheDocument()
157+
// }
158+
// )
159+
test.todo('toggling comparison option pushes value to url opts param')
172160
test.todo('unselecting comparison option will remove it from url opts param')
173161
test.todo('selecting clear option will remove all params from url opts param')
174162
})

packages/api-explorer/src/test-utils/redux.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ import {
4646
import { specState } from '../test-data'
4747
import { renderWithRouter } from './router'
4848

49+
export const preloadedState: RootState = {
50+
settings: defaultSettingsState,
51+
lodes: defaultLodesState,
52+
specs: defaultSpecsState,
53+
}
54+
4955
export const withReduxProvider = (
5056
consumers: ReactElement<any>,
5157
store: Store<RootState> = createTestStore()
@@ -68,12 +74,6 @@ export const renderWithRouterAndReduxProvider = (
6874
) =>
6975
renderWithRouter(withReduxProvider(consumers, store), initialEntries, options)
7076

71-
export const preloadedState: RootState = {
72-
settings: defaultSettingsState,
73-
lodes: defaultLodesState,
74-
specs: defaultSpecsState,
75-
}
76-
7777
type DeepPartial<T> = {
7878
[P in keyof T]?: DeepPartial<T[P]>
7979
}

packages/api-explorer/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ export { getLoded } from './lodeUtils'
3030
export { useWindowSize } from './useWindowSize'
3131
export * from './apixAdaptor'
3232
export * from './adaptorUtils'
33+
export { MockedProvider, mockHistory } from './testReduxUtils'
3334
export { useNavigation, useGlobalStoreSync } from './hooks'
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2022 Looker Data Sciences, Inc.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
import { ComponentsProvider } from '@looker/components'
27+
import type { Store } from '@looker/redux'
28+
import React from 'react'
29+
import { QueryClient, QueryClientProvider } from 'react-query'
30+
import { Provider } from 'react-redux'
31+
import { Router } from 'react-router'
32+
import { HelmetProvider } from 'react-helmet-async'
33+
import type { MemoryHistoryBuildOptions, History } from 'history'
34+
import { createMemoryHistory } from 'history'
35+
import type { RootState } from '../state'
36+
import { createTestStore } from '../test-utils'
37+
38+
export interface MockedProviderProps {
39+
history?: History
40+
store?: Store<RootState>
41+
}
42+
43+
/**
44+
* Mocks all providers needed to render any component or scene
45+
*/
46+
export const MockedProvider: React.FC<MockedProviderProps> = ({
47+
children,
48+
history = mockHistory(),
49+
store = createTestStore(),
50+
}) => {
51+
return (
52+
<QueryClientProvider client={new QueryClient()}>
53+
<Router history={history}>
54+
<HelmetProvider>
55+
<Provider store={store}>
56+
<ComponentsProvider disableStyleDefender>
57+
{children}
58+
</ComponentsProvider>
59+
</Provider>
60+
</HelmetProvider>
61+
</Router>
62+
</QueryClientProvider>
63+
)
64+
}
65+
66+
export const mockHistory = (
67+
/**
68+
* Set the current route by passing in a string or to mock the entire
69+
* history stack pass in MemoryHistoryBuildOptions
70+
*/
71+
route?: string | MemoryHistoryBuildOptions
72+
) =>
73+
createMemoryHistory(
74+
typeof route === 'string' ? { initialEntries: [route] } : route
75+
)

0 commit comments

Comments
 (0)