Skip to content

Commit 1c856ba

Browse files
committed
proof of concept for making all page-level tests into integration tests
1 parent e34afcb commit 1c856ba

File tree

4 files changed

+52
-25
lines changed

4 files changed

+52
-25
lines changed

app/pages/__tests__/InstanceCreateForm.spec.tsx renamed to app/pages/__tests__/InstanceCreatePage.spec.tsx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import React from 'react'
21
import {
32
fireEvent,
43
lastBody,
5-
renderWithRouter,
4+
renderAppAt,
65
screen,
76
waitFor,
87
} from '../../test-utils'
98
import fetchMock from 'fetch-mock'
109

1110
import { org, project, instance } from '@oxide/api-mocks'
1211

13-
import { InstanceCreateForm } from '../project/instances/create/InstancesCreatePage'
14-
1512
const submitButton = () =>
1613
screen.getByRole('button', { name: 'Create instance' })
1714

@@ -20,21 +17,14 @@ const instancesUrl = `${projectUrl}/instances`
2017
const disksUrl = `${projectUrl}/disks`
2118
const vpcsUrl = `${projectUrl}/vpcs`
2219

23-
let successSpy: jest.Mock
20+
const renderPage = () =>
21+
renderAppAt(`/orgs/${org.name}/projects/${project.name}/instances/new`)
2422

25-
describe('InstanceCreateForm', () => {
23+
describe('InstanceCreatePage', () => {
2624
beforeEach(() => {
2725
// existing disk modal fetches disks on render even if it's not visible
2826
fetchMock.get(disksUrl, 200)
2927
fetchMock.get(vpcsUrl, 200)
30-
successSpy = jest.fn()
31-
renderWithRouter(
32-
<InstanceCreateForm
33-
orgName={org.name}
34-
projectName={project.name}
35-
onSuccess={successSpy}
36-
/>
37-
)
3828
})
3929

4030
afterEach(() => {
@@ -43,6 +33,7 @@ describe('InstanceCreateForm', () => {
4333

4434
it('disables submit button on submit and enables on response', async () => {
4535
const mock = fetchMock.post(instancesUrl, 201)
36+
renderPage()
4637

4738
const submit = submitButton()
4839
expect(submit).not.toBeDisabled()
@@ -52,14 +43,14 @@ describe('InstanceCreateForm', () => {
5243
expect(mock.called(instancesUrl)).toBeFalsy()
5344
await waitFor(() => expect(submit).toBeDisabled())
5445
expect(mock.done()).toBeTruthy()
55-
expect(submit).not.toBeDisabled()
5646
})
5747

5848
it('shows specific message for known server error code', async () => {
5949
fetchMock.post(instancesUrl, {
6050
status: 400,
6151
body: { error_code: 'ObjectAlreadyExists' },
6252
})
53+
renderPage()
6354

6455
fireEvent.click(submitButton())
6556

@@ -73,6 +64,7 @@ describe('InstanceCreateForm', () => {
7364
status: 400,
7465
body: { error_code: 'UnknownCode' },
7566
})
67+
renderPage()
7668

7769
fireEvent.click(submitButton())
7870

@@ -81,6 +73,7 @@ describe('InstanceCreateForm', () => {
8173

8274
it('posts form on submit', async () => {
8375
const mock = fetchMock.post(instancesUrl, 201)
76+
renderPage()
8477

8578
fireEvent.change(screen.getByLabelText('Choose a name'), {
8679
target: { value: 'new-instance' },
@@ -99,15 +92,19 @@ describe('InstanceCreateForm', () => {
9992
)
10093
})
10194

102-
it('calls onSuccess on success', async () => {
95+
it('navigates to project instances page on success', async () => {
10396
const mock = fetchMock.post(instancesUrl, { status: 201, body: instance })
97+
const { history } = renderPage()
10498

105-
expect(successSpy).not.toHaveBeenCalled()
99+
const instancesPage = `/orgs/${org.name}/projects/${project.name}/instances`
100+
expect(history.location.pathname).not.toEqual(instancesPage)
106101

107102
fireEvent.click(submitButton())
108103

109104
await waitFor(() => expect(mock.called(instancesUrl)).toBeTruthy())
110105
await waitFor(() => expect(mock.done()).toBeTruthy())
111-
await waitFor(() => expect(successSpy).toHaveBeenCalled())
106+
await waitFor(() =>
107+
expect(history.location.pathname).toEqual(instancesPage)
108+
)
112109
})
113110
})

app/test-utils.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import React from 'react'
2-
import { BrowserRouter as Router } from 'react-router-dom'
2+
import {
3+
BrowserRouter,
4+
unstable_HistoryRouter as HistoryRouter,
5+
} from 'react-router-dom'
6+
import { createMemoryHistory } from 'history'
37
import { render } from '@testing-library/react'
48
import { QueryClient, QueryClientProvider } from 'react-query'
59
import type { FetchMockStatic } from 'fetch-mock'
10+
import { routes } from './routes'
611

712
const queryClient = new QueryClient({
813
defaultOptions: {
@@ -22,14 +27,24 @@ const customRender = (ui: React.ReactElement) =>
2227
export const renderWithRouter = (ui: React.ReactElement) =>
2328
render(ui, {
2429
wrapper: ({ children }) => (
25-
<Router>
30+
<BrowserRouter>
2631
<QueryClientProvider client={queryClient}>
2732
{children}
2833
</QueryClientProvider>
29-
</Router>
34+
</BrowserRouter>
3035
),
3136
})
3237

38+
export function renderAppAt(location: string) {
39+
const history = createMemoryHistory({ initialEntries: [location] })
40+
const rendered = render(
41+
<HistoryRouter history={history}>
42+
<QueryClientProvider client={queryClient}>{routes}</QueryClientProvider>
43+
</HistoryRouter>
44+
)
45+
return { history, rendered }
46+
}
47+
3348
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3449
export const lastBody = (mock: FetchMockStatic): any =>
3550
JSON.parse(mock.lastOptions()?.body as unknown as string)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
"react-is": "^17.0.2",
4242
"react-popper": "^2.2.5",
4343
"react-query": "^3.13.12",
44-
"react-router": "^6.0.0",
45-
"react-router-dom": "^6.0.0",
44+
"react-router": "^6.1.1",
45+
"react-router-dom": "^6.1.1",
4646
"react-table": "^7.7.0",
4747
"react-transition-group": "^4.4.1",
4848
"recharts": "^2.1.6",

yarn.lock

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12175,21 +12175,36 @@ react-resize-detector@^6.6.3:
1217512175
lodash.throttle "^4.1.1"
1217612176
resize-observer-polyfill "^1.5.1"
1217712177

12178-
react-router-dom@^6.0.0, react-router-dom@^6.0.0-beta.8:
12178+
react-router-dom@^6.0.0-beta.8:
1217912179
version "6.0.2"
1218012180
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.2.tgz"
1218112181
integrity sha512-cOpJ4B6raFutr0EG8O/M2fEoyQmwvZWomf1c6W2YXBZuFBx8oTk/zqjXghwScyhfrtnt0lANXV2182NQblRxFA==
1218212182
dependencies:
1218312183
history "^5.1.0"
1218412184
react-router "6.0.2"
1218512185

12186-
[email protected], react-router@^6.0.0, react-router@^6.0.0-beta.8:
12186+
react-router-dom@^6.1.1:
12187+
version "6.1.1"
12188+
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.1.1.tgz#ed59376ff9115bc49227e87982a32e91e9530ca3"
12189+
integrity sha512-O3UH89DI4o+swd2q6lF4dSmpuNCxwkUXcj0zAFcVc1H+YoPE6T7uwoFMX0ws1pUvCY8lYDucFpOqCCdal6VFzg==
12190+
dependencies:
12191+
history "^5.1.0"
12192+
react-router "6.1.1"
12193+
12194+
[email protected], react-router@^6.0.0-beta.8:
1218712195
version "6.0.2"
1218812196
resolved "https://registry.npmjs.org/react-router/-/react-router-6.0.2.tgz"
1218912197
integrity sha512-8/Wm3Ed8t7TuedXjAvV39+c8j0vwrI5qVsYqjFr5WkJjsJpEvNSoLRUbtqSEYzqaTUj1IV+sbPJxvO+accvU0Q==
1219012198
dependencies:
1219112199
history "^5.1.0"
1219212200

12201+
[email protected], react-router@^6.1.1:
12202+
version "6.1.1"
12203+
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.1.1.tgz#16f41bf54e87d995bcd4d447720a693f77d8fcb9"
12204+
integrity sha512-55o96RiDZmC0uD17DPqVmzzfdNd2Dc+EjkYvMAmHl43du/GItaTdFr5WwjTryNWPXZ+OOVQxQhwAX25UwxpHtw==
12205+
dependencies:
12206+
history "^5.1.0"
12207+
1219312208
react-sizeme@^3.0.1:
1219412209
version "3.0.2"
1219512210
resolved "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz"

0 commit comments

Comments
 (0)