go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/build/components/build_table/summary_column/summary_column.test.tsx (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import { act, cleanup, render, screen } from '@testing-library/react';
    16  
    17  import { OutputBuild } from '@/build/types';
    18  import { Status } from '@/proto/go.chromium.org/luci/buildbucket/proto/common.pb';
    19  import { FakeContextProvider } from '@/testing_tools/fakes/fake_context_provider';
    20  
    21  import { BuildTable } from '../build_table';
    22  import { BuildTableBody } from '../build_table_body';
    23  import { BuildTableHead } from '../build_table_head';
    24  import { BuildTableRow } from '../build_table_row';
    25  import { useBuild } from '../context';
    26  
    27  import { SummaryContentCell, SummaryHeadCell } from './summary_column';
    28  
    29  jest.mock('../context', () =>
    30    self.createSelectiveSpiesFromModule<typeof import('../context')>(
    31      '../context',
    32      ['useBuild'],
    33    ),
    34  );
    35  
    36  const buildWithSummary = {
    37    id: '1234',
    38    status: Status.SUCCESS,
    39    summaryMarkdown: '<strong>build is fine</strong>',
    40  } as OutputBuild;
    41  
    42  const buildWithNoSummary = {
    43    id: '2345',
    44    status: Status.SUCCESS,
    45  } as OutputBuild;
    46  
    47  describe('SummaryContentCell', () => {
    48    beforeEach(() => {
    49      jest.useFakeTimers();
    50    });
    51  
    52    afterEach(() => {
    53      jest.useRealTimers();
    54    });
    55  
    56    describe('when there is summary', () => {
    57      let useBuildSpy: jest.MockedFunctionDeep<typeof useBuild>;
    58  
    59      beforeEach(() => {
    60        useBuildSpy = jest.mocked(useBuild);
    61        render(
    62          <FakeContextProvider>
    63            <BuildTable>
    64              <BuildTableHead>
    65                <SummaryHeadCell />
    66              </BuildTableHead>
    67              <BuildTableBody>
    68                <BuildTableRow build={buildWithSummary}>
    69                  <SummaryContentCell />
    70                </BuildTableRow>
    71              </BuildTableBody>
    72            </BuildTable>
    73          </FakeContextProvider>,
    74        );
    75      });
    76  
    77      afterEach(() => {
    78        useBuildSpy.mockClear();
    79        cleanup();
    80      });
    81  
    82      it('should expand/collapse correctly', async () => {
    83        const toggleRowButton = screen.getByLabelText('toggle-row');
    84        const toggleAllRowsButton = screen.getByLabelText('toggle-all-rows');
    85  
    86        expect(toggleRowButton).not.toBeDisabled();
    87        expect(toggleRowButton).not.toHaveStyleRule('visibility', 'hidden');
    88  
    89        expect(
    90          toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"),
    91        ).not.toHaveStyle({ display: 'none' });
    92        expect(
    93          toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"),
    94        ).toHaveStyle({ display: 'none' });
    95  
    96        // Expand by clicking on toggle button.
    97        act(() => toggleRowButton.click());
    98        expect(
    99          toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"),
   100        ).toHaveStyle({ display: 'none' });
   101        expect(
   102          toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"),
   103        ).not.toHaveStyle({ display: 'none' });
   104  
   105        // Collapse by clicking on toggle button.
   106        act(() => toggleRowButton.click());
   107        expect(
   108          toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"),
   109        ).not.toHaveStyle({ display: 'none' });
   110        expect(
   111          toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"),
   112        ).toHaveStyle({ display: 'none' });
   113  
   114        // Expand again by changing the default state.
   115        act(() => toggleAllRowsButton.click());
   116        await act(() => jest.runAllTimersAsync());
   117        expect(
   118          toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"),
   119        ).toHaveStyle({ display: 'none' });
   120        expect(
   121          toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"),
   122        ).not.toHaveStyle({ display: 'none' });
   123  
   124        // Collapse again by changing the default state.
   125        act(() => toggleAllRowsButton.click());
   126        await act(() => jest.runAllTimersAsync());
   127        expect(
   128          toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"),
   129        ).not.toHaveStyle({ display: 'none' });
   130        expect(
   131          toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"),
   132        ).toHaveStyle({ display: 'none' });
   133      });
   134  
   135      it('should avoid unnecessary rerendering', async () => {
   136        const toggleRowButton = screen.getByLabelText('toggle-row');
   137        const toggleAllRowsButton = screen.getByLabelText('toggle-all-rows');
   138        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   139  
   140        // Expand by clicking on toggle button.
   141        act(() => toggleRowButton.click());
   142        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   143  
   144        // Expand by changing the default state.
   145        act(() => toggleAllRowsButton.click());
   146        await act(() => jest.runAllTimersAsync());
   147        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   148  
   149        // Collapse by clicking on toggle button.
   150        act(() => toggleRowButton.click());
   151        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   152  
   153        // Collapse by changing the default state.
   154        act(() => toggleAllRowsButton.click());
   155        await act(() => jest.runAllTimersAsync());
   156        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   157      });
   158    });
   159  
   160    describe('when there is no summary', () => {
   161      let useBuildSpy: jest.MockedFunctionDeep<typeof useBuild>;
   162  
   163      beforeEach(() => {
   164        useBuildSpy = jest.mocked(useBuild);
   165        render(
   166          <FakeContextProvider>
   167            <BuildTable>
   168              <BuildTableHead>
   169                <SummaryHeadCell />
   170              </BuildTableHead>
   171              <BuildTableBody>
   172                <BuildTableRow build={buildWithNoSummary}>
   173                  <SummaryContentCell />
   174                </BuildTableRow>
   175              </BuildTableBody>
   176            </BuildTable>
   177          </FakeContextProvider>,
   178        );
   179      });
   180  
   181      afterEach(() => {
   182        useBuildSpy.mockClear();
   183        cleanup();
   184      });
   185  
   186      it('should disable toggle button', async () => {
   187        const toggleButton = screen.getByLabelText('toggle-row');
   188  
   189        expect(toggleButton).toBeDisabled();
   190        expect(toggleButton).toHaveStyleRule('visibility', 'hidden');
   191      });
   192  
   193      it('should avoid unnecessary rerendering', async () => {
   194        const toggleAllRowsButton = screen.getByLabelText('toggle-all-rows');
   195        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   196  
   197        // Expand by changing the default state.
   198        act(() => toggleAllRowsButton.click());
   199        await act(() => jest.runAllTimersAsync());
   200        // Not rerendered. There is no summary anyway.
   201        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   202  
   203        // Collapse by changing the default state.
   204        act(() => toggleAllRowsButton.click());
   205        await act(() => jest.runAllTimersAsync());
   206        // Not rerendered. There is no summary anyway.
   207        expect(useBuildSpy).toHaveBeenCalledTimes(1);
   208      });
   209    });
   210  });