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  });