vitess.io/vitess@v0.16.2/web/vtadmin/src/components/ActionPanel.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  
    17  import { render, screen, waitFor } from '@testing-library/react';
    18  import userEvent from '@testing-library/user-event';
    19  import { rest } from 'msw';
    20  import { setupServer } from 'msw/node';
    21  import { QueryClient, QueryClientProvider, useMutation } from 'react-query';
    22  
    23  import ActionPanel, { ActionPanelProps } from './ActionPanel';
    24  
    25  const ORIGINAL_PROCESS_ENV = process.env;
    26  const TEST_PROCESS_ENV = {
    27      ...process.env,
    28      REACT_APP_VTADMIN_API_ADDRESS: '',
    29  };
    30  
    31  describe('ActionPanel', () => {
    32      const server = setupServer(
    33          rest.post('/api/test', (req, res, ctx) => {
    34              return res(ctx.json({ ok: true }));
    35          })
    36      );
    37  
    38      const queryClient = new QueryClient({
    39          defaultOptions: { queries: { retry: false } },
    40      });
    41  
    42      /**
    43       * The useMutation query hook must be defined in the body of a function
    44       * that is _within_ the context of a QueryClientProvider. This Wrapper component
    45       * provides such a function and should be `render`ed in the context QueryClientProvider.
    46       */
    47      const Wrapper: React.FC<Omit<ActionPanelProps, 'mutation'>> = (props) => {
    48          const mutation = useMutation(() => fetch('/api/test', { method: 'post' }));
    49          return <ActionPanel {...props} mutation={mutation as any} />;
    50      };
    51  
    52      beforeAll(() => {
    53          process.env = { ...TEST_PROCESS_ENV } as NodeJS.ProcessEnv;
    54          server.listen();
    55      });
    56  
    57      afterEach(() => {
    58          process.env = { ...TEST_PROCESS_ENV } as NodeJS.ProcessEnv;
    59          jest.clearAllMocks();
    60      });
    61  
    62      afterAll(() => {
    63          process.env = { ...ORIGINAL_PROCESS_ENV };
    64          server.close();
    65      });
    66  
    67      it('initiates the mutation', async () => {
    68          jest.spyOn(global, 'fetch');
    69  
    70          render(
    71              <QueryClientProvider client={queryClient}>
    72                  <Wrapper
    73                      confirmationValue="zone1-101"
    74                      description="Do an action."
    75                      documentationLink="https://test.com"
    76                      loadedText="Do Action"
    77                      loadingText="Doing Action..."
    78                      title="A Title"
    79                  />
    80              </QueryClientProvider>
    81          );
    82  
    83          const user = userEvent.setup();
    84  
    85          const button = screen.getByRole('button');
    86          const input = screen.getByRole('textbox');
    87  
    88          // Enter the confirmation text
    89          await user.type(input, 'zone1-101');
    90          expect(button).not.toHaveAttribute('disabled');
    91  
    92          await user.click(button);
    93  
    94          // Validate form while API request is in flight
    95          expect(button).toHaveTextContent('Doing Action...');
    96  
    97          expect(global.fetch).toHaveBeenCalledTimes(1);
    98          expect(global.fetch).toHaveBeenCalledWith('/api/test', { method: 'post' });
    99  
   100          // Wait for API request to complete
   101          await waitFor(() => expect(button).toHaveTextContent('Do Action'));
   102      });
   103  
   104      it('enables form submission if and only if input matches confirmation', async () => {
   105          render(
   106              <QueryClientProvider client={queryClient}>
   107                  <Wrapper
   108                      confirmationValue="zone1-101"
   109                      description={<>Hello world!</>}
   110                      documentationLink="https://test.com"
   111                      loadedText="Do Action"
   112                      loadingText="Doing Action..."
   113                      title="A Title"
   114                  />
   115              </QueryClientProvider>
   116          );
   117  
   118          const user = userEvent.setup();
   119  
   120          const button = screen.getByRole('button');
   121          const input = screen.getByRole('textbox');
   122  
   123          expect(button).toHaveAttribute('disabled');
   124  
   125          const invalidInputs = [' ', 'zone-100', 'zone1'];
   126          for (let i = 0; i < invalidInputs.length; i++) {
   127              await user.clear(input);
   128              await user.type(input, invalidInputs[i]);
   129              expect(button).toHaveAttribute('disabled');
   130          }
   131  
   132          await user.clear(input);
   133          await user.type(input, 'zone1-101');
   134          expect(button).not.toHaveAttribute('disabled');
   135      });
   136  
   137      it('does not render confirmation if "confirmationValue" not set', async () => {
   138          render(
   139              <QueryClientProvider client={queryClient}>
   140                  <Wrapper
   141                      description={<>Hello world!</>}
   142                      documentationLink="https://test.com"
   143                      loadedText="Do Action"
   144                      loadingText="Doing Action..."
   145                      title="A Title"
   146                  />
   147              </QueryClientProvider>
   148          );
   149  
   150          const button = screen.getByRole('button');
   151          const input = screen.queryByRole('textbox');
   152  
   153          expect(input).toBeNull();
   154          expect(button).not.toHaveAttribute('disabled');
   155      });
   156  
   157      it('disables interaction when "disabled" prop is set', () => {
   158          render(
   159              <QueryClientProvider client={queryClient}>
   160                  <Wrapper
   161                      confirmationValue="zone1-101"
   162                      description={<>Hello world!</>}
   163                      disabled
   164                      documentationLink="https://test.com"
   165                      loadedText="Do Action"
   166                      loadingText="Doing Action..."
   167                      title="A Title"
   168                  />
   169              </QueryClientProvider>
   170          );
   171  
   172          const button = screen.getByRole('button');
   173          const input = screen.queryByRole('textbox');
   174  
   175          expect(input).toBeNull();
   176          expect(button).toHaveAttribute('disabled');
   177      });
   178  });