go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/gitiles/components/commit_table/commit_table.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 import userEvent from '@testing-library/user-event'; 17 18 import { OutputCommit } from '@/gitiles/types'; 19 import { Commit_TreeDiff_ChangeType } from '@/proto/go.chromium.org/luci/common/proto/git/commit.pb'; 20 import { FakeContextProvider } from '@/testing_tools/fakes/fake_context_provider'; 21 22 import { CommitTable } from './commit_table'; 23 import { CommitTableBody } from './commit_table_body'; 24 import { CommitTableHead } from './commit_table_head'; 25 import { CommitTableRow } from './commit_table_row'; 26 27 const commit: OutputCommit = { 28 id: '1234567890abcdef', 29 tree: '1234567890abcdef', 30 parents: ['1234567890abcdee'], 31 author: { 32 name: 'author', 33 email: 'author@email.com', 34 time: '2022-02-02T23:22:22Z', 35 }, 36 committer: { 37 name: 'committer', 38 email: 'committer@email.com', 39 time: '2022-02-02T23:22:22Z', 40 }, 41 message: 'this is a commit\ndescription\n', 42 treeDiff: [ 43 { 44 type: Commit_TreeDiff_ChangeType.MODIFY, 45 oldId: '1234567890abcdef', 46 oldMode: 33188, 47 oldPath: 'ash/style/combobox.cc', 48 newId: '1234567890abcdef', 49 newMode: 33188, 50 newPath: 'ash/style/combobox.cc', 51 }, 52 ], 53 }; 54 55 describe('CommitTable', () => { 56 beforeEach(() => { 57 jest.useFakeTimers(); 58 }); 59 60 afterEach(() => { 61 jest.useRealTimers(); 62 cleanup(); 63 }); 64 65 it('should expand/collapse correctly', async () => { 66 render( 67 <FakeContextProvider> 68 <CommitTable repoUrl="https://repo.url"> 69 <CommitTableHead> 70 <></> 71 </CommitTableHead> 72 <CommitTableBody> 73 <CommitTableRow commit={commit}> 74 <></> 75 </CommitTableRow> 76 </CommitTableBody> 77 </CommitTable> 78 </FakeContextProvider>, 79 ); 80 81 const toggleRowButton = screen.getByLabelText('toggle-row'); 82 const toggleAllRowsButton = screen.getByLabelText('toggle-all-rows'); 83 const contentRow = screen.getByTestId('content-row'); 84 85 expect( 86 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 87 ).toBeInTheDocument(); 88 expect( 89 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 90 ).not.toBeInTheDocument(); 91 expect(contentRow).toHaveStyle({ display: 'none' }); 92 93 // Expand by clicking on toggle button. 94 act(() => toggleRowButton.click()); 95 expect( 96 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 97 ).not.toBeInTheDocument(); 98 expect( 99 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 100 ).toBeInTheDocument(); 101 expect(contentRow).not.toHaveStyle({ display: 'none' }); 102 103 // Collapse by clicking on toggle button. 104 act(() => toggleRowButton.click()); 105 expect( 106 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 107 ).toBeInTheDocument(); 108 expect( 109 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 110 ).not.toBeInTheDocument(); 111 expect(contentRow).toHaveStyle({ display: 'none' }); 112 113 // Expand again by changing the default state. 114 act(() => toggleAllRowsButton.click()); 115 await act(() => jest.runAllTimersAsync()); 116 expect( 117 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 118 ).not.toBeInTheDocument(); 119 expect( 120 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 121 ).toBeInTheDocument(); 122 expect(contentRow).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 ).toBeInTheDocument(); 130 expect( 131 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 132 ).not.toBeInTheDocument(); 133 expect(contentRow).toHaveStyle({ display: 'none' }); 134 }); 135 136 it('should expand/collapse correctly with hotkey', async () => { 137 render( 138 <FakeContextProvider> 139 <CommitTable repoUrl="https://repo.url"> 140 <CommitTableHead toggleExpandHotkey="x"> 141 <></> 142 </CommitTableHead> 143 <CommitTableBody> 144 <CommitTableRow commit={commit}> 145 <></> 146 </CommitTableRow> 147 </CommitTableBody> 148 </CommitTable> 149 </FakeContextProvider>, 150 ); 151 152 const toggleRowButton = screen.getByLabelText('toggle-row'); 153 const contentRow = screen.getByTestId('content-row'); 154 155 expect( 156 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 157 ).toBeInTheDocument(); 158 expect( 159 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 160 ).not.toBeInTheDocument(); 161 expect(contentRow).toHaveStyle({ display: 'none' }); 162 163 // Expand by clicking on toggle button. 164 act(() => toggleRowButton.click()); 165 expect( 166 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 167 ).not.toBeInTheDocument(); 168 expect( 169 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 170 ).toBeInTheDocument(); 171 expect(contentRow).not.toHaveStyle({ display: 'none' }); 172 173 // Collapse by clicking on toggle button. 174 act(() => toggleRowButton.click()); 175 expect( 176 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 177 ).toBeInTheDocument(); 178 expect( 179 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 180 ).not.toBeInTheDocument(); 181 expect(contentRow).toHaveStyle({ display: 'none' }); 182 183 // Expand again by changing the default state. 184 await act(() => 185 Promise.all([userEvent.keyboard('x'), jest.runAllTimersAsync()]), 186 ); 187 expect( 188 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 189 ).not.toBeInTheDocument(); 190 expect( 191 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 192 ).toBeInTheDocument(); 193 expect(contentRow).not.toHaveStyle({ display: 'none' }); 194 195 // Collapse again by changing the default state. 196 await act(() => 197 Promise.all([userEvent.keyboard('x'), jest.runAllTimersAsync()]), 198 ); 199 expect( 200 toggleRowButton.querySelector("[data-testid='ChevronRightIcon']"), 201 ).toBeInTheDocument(); 202 expect( 203 toggleRowButton.querySelector("[data-testid='ExpandMoreIcon']"), 204 ).not.toBeInTheDocument(); 205 expect(contentRow).toHaveStyle({ display: 'none' }); 206 }); 207 208 it('should notify default state update correctly', async () => { 209 const onExpandSpy = jest.fn((_expanded: boolean) => {}); 210 render( 211 <FakeContextProvider> 212 <CommitTable 213 initDefaultExpanded={true} 214 onDefaultExpandedChanged={onExpandSpy} 215 repoUrl="https://repo.url" 216 > 217 <CommitTableHead> 218 <></> 219 </CommitTableHead> 220 <CommitTableBody> 221 <></> 222 </CommitTableBody> 223 </CommitTable> 224 </FakeContextProvider>, 225 ); 226 await act(() => jest.runAllTimersAsync()); 227 228 // Don't notify on first render. 229 expect(onExpandSpy).not.toHaveBeenCalled(); 230 231 const toggleButton = screen.getByLabelText('toggle-all-rows'); 232 233 // Collapse by clicking on toggle button. 234 act(() => toggleButton.click()); 235 expect(onExpandSpy).toHaveBeenNthCalledWith(1, false); 236 237 // Expand by clicking on toggle button. 238 act(() => toggleButton.click()); 239 expect(onExpandSpy).toHaveBeenNthCalledWith(2, true); 240 }); 241 });