vitess.io/vitess@v0.16.2/web/vtadmin/src/components/routes/createKeyspace/CreateKeyspace.test.tsx (about) 1 /** 2 * Copyright 2022 The Vitess Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 import { rest } from 'msw'; 17 import { setupServer } from 'msw/node'; 18 import { render, screen, waitFor } from '@testing-library/react'; 19 import userEvent from '@testing-library/user-event'; 20 import { createMemoryHistory } from 'history'; 21 import { QueryClient, QueryClientProvider } from 'react-query'; 22 import { Router } from 'react-router-dom'; 23 24 import { CreateKeyspace } from './CreateKeyspace'; 25 import { vtadmin } from '../../../proto/vtadmin'; 26 import * as Snackbar from '../../Snackbar'; 27 28 const ORIGINAL_PROCESS_ENV = process.env; 29 const TEST_PROCESS_ENV = { 30 ...process.env, 31 REACT_APP_VTADMIN_API_ADDRESS: '', 32 }; 33 34 // This integration test verifies the behaviour from the form UI 35 // all the way down to the network level (which we mock with msw). 36 // It's a very comprehensive test (good!), but does make some assumptions 37 // about UI structure (boo!), which means this test is rather brittle 38 // to UI changes (e.g., like how the Select works, adding new form fields, etc.) 39 describe('CreateKeyspace integration test', () => { 40 const server = setupServer(); 41 42 beforeAll(() => { 43 process.env = { ...TEST_PROCESS_ENV } as NodeJS.ProcessEnv; 44 }); 45 46 afterEach(() => { 47 process.env = { ...TEST_PROCESS_ENV } as NodeJS.ProcessEnv; 48 }); 49 50 afterAll(() => { 51 process.env = { ...ORIGINAL_PROCESS_ENV }; 52 server.close(); 53 }); 54 55 it('successfully creates a keyspace', async () => { 56 jest.spyOn(global, 'fetch'); 57 jest.spyOn(Snackbar, 'success'); 58 59 const cluster = { id: 'local', name: 'local' }; 60 61 server.use( 62 rest.get('/api/clusters', (req, res, ctx) => { 63 return res(ctx.json({ result: { clusters: [cluster] }, ok: true })); 64 }), 65 rest.post('/api/keyspace/:clusterID', (req, res, ctx) => { 66 const data: vtadmin.ICreateKeyspaceResponse = { 67 keyspace: { 68 cluster: { id: cluster.id, name: cluster.name }, 69 keyspace: { name: 'some-keyspace' }, 70 }, 71 }; 72 return res(ctx.json({ result: data, ok: true })); 73 }) 74 ); 75 server.listen(); 76 77 const history = createMemoryHistory(); 78 jest.spyOn(history, 'push'); 79 80 const queryClient = new QueryClient({ 81 defaultOptions: { queries: { retry: false } }, 82 }); 83 84 // Finally, render the view 85 render( 86 <Router history={history}> 87 <QueryClientProvider client={queryClient}> 88 <CreateKeyspace /> 89 </QueryClientProvider> 90 </Router> 91 ); 92 93 // Wait for initial queries to load. Given that the "initial queries" for this 94 // page are presently only the call to GET /api/clusters, checking that the 95 // Select is populated with the clusters from the response defined above is 96 // sufficient, albeit clumsy. This will need to be reworked in a future where 97 // we add more queries that fire on form load (e.g., to fetch all keyspaces.) 98 await waitFor(() => { 99 expect(screen.queryByTestId('select-empty')).toBeNull(); 100 }); 101 102 // Reset the fetch mock after the initial queries have completed so that 103 // form submission assertions are easier. 104 (global.fetch as any).mockClear(); 105 106 // From here on we can proceed with filling out the form fields. 107 const user = userEvent.setup(); 108 await user.click(screen.getByText('local (local)')); 109 await user.type(screen.getByLabelText('Keyspace Name'), 'some-keyspace'); 110 111 // Submit the form 112 const submitButton = screen.getByText('Create Keyspace', { 113 selector: 'button[type="submit"]', 114 }); 115 await user.click(submitButton); 116 117 // Assert that the client sent the correct API request 118 expect(global.fetch).toHaveBeenCalledTimes(1); 119 expect(global.fetch).toHaveBeenCalledWith('/api/keyspace/local', { 120 credentials: undefined, 121 body: JSON.stringify({ 122 name: 'some-keyspace', 123 }), 124 method: 'post', 125 }); 126 127 // Validate form UI loading state, while the API request is "in flight" 128 expect(submitButton).toHaveTextContent('Creating Keyspace...'); 129 expect(submitButton).toHaveAttribute('disabled'); 130 131 // Wait for the API request to complete 132 await waitFor(() => { 133 expect(submitButton).toHaveTextContent('Create Keyspace'); 134 }); 135 136 // Validate redirect to the new keyspace's detail page 137 expect(history.push).toHaveBeenCalledTimes(1); 138 expect(history.push).toHaveBeenCalledWith('/keyspace/local/some-keyspace'); 139 140 // Validate that snackbar was triggered 141 expect(Snackbar.success).toHaveBeenCalledTimes(1); 142 expect(Snackbar.success).toHaveBeenCalledWith('Created keyspace some-keyspace', { autoClose: 1600 }); 143 }); 144 });