go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/build/pages/builder_group_page/builder_group_page.test.tsx (about) 1 // Copyright 2024 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, render } from '@testing-library/react'; 16 17 import { FilterableBuilderTable } from '@/build/components/filterable_builder_table'; 18 import { 19 BuilderID, 20 BuilderItem, 21 } from '@/proto/go.chromium.org/luci/buildbucket/proto/builder_common.pb'; 22 import { 23 ListBuildersResponse, 24 MiloInternalClientImpl, 25 } from '@/proto/go.chromium.org/luci/milo/proto/v1/rpc.pb'; 26 import { FakeContextProvider } from '@/testing_tools/fakes/fake_context_provider'; 27 28 import { BuilderGroupPage } from './builder_group_page'; 29 30 jest.mock('@/build/components/filterable_builder_table', () => 31 self.createSelectiveMockFromModule< 32 typeof import('@/build/components/filterable_builder_table') 33 >('@/build/components/filterable_builder_table', ['FilterableBuilderTable']), 34 ); 35 36 const builders = Array(25) 37 .fill(0) 38 .map((_, i) => 39 BuilderID.fromPartial({ 40 project: 'proj', 41 bucket: `bucket${i}`, 42 builder: `builder${i}`, 43 }), 44 ); 45 46 const builderItems = builders.map((id) => BuilderItem.fromPartial({ id })); 47 48 const pages: { [key: string]: ListBuildersResponse } = { 49 '': ListBuildersResponse.fromPartial({ 50 builders: Object.freeze(builderItems.slice(0, 10)), 51 nextPageToken: 'page2', 52 }), 53 page2: ListBuildersResponse.fromPartial({ 54 builders: Object.freeze(builderItems.slice(10, 20)), 55 nextPageToken: 'page3', 56 }), 57 page3: ListBuildersResponse.fromPartial({ 58 builders: Object.freeze(builderItems.slice(20, 25)), 59 nextPageToken: '', 60 }), 61 }; 62 63 describe('<BuilderGroupPage />', () => { 64 let listBuildersSpy: jest.SpiedFunction< 65 MiloInternalClientImpl['ListBuilders'] 66 >; 67 let builderTableMock: jest.MockedFunction<typeof FilterableBuilderTable>; 68 69 beforeEach(() => { 70 jest.useFakeTimers(); 71 builderTableMock = jest.mocked(FilterableBuilderTable); 72 listBuildersSpy = jest 73 .spyOn(MiloInternalClientImpl.prototype, 'ListBuilders') 74 .mockImplementation(async (req) => pages[req.pageToken]); 75 }); 76 77 afterEach(() => { 78 jest.useRealTimers(); 79 listBuildersSpy.mockReset(); 80 builderTableMock.mockReset(); 81 }); 82 83 it('should render correctly', async () => { 84 render( 85 <FakeContextProvider 86 mountedPath="/p/:project/g/:group/builders" 87 routerOptions={{ 88 initialEntries: ['/p/proj/g/a_group/builders'], 89 }} 90 > 91 <BuilderGroupPage /> 92 </FakeContextProvider>, 93 ); 94 95 // Load all pages automatically. 96 await act(() => jest.runAllTimersAsync()); 97 await act(() => jest.runAllTimersAsync()); 98 expect(listBuildersSpy).toHaveBeenCalledTimes(3); 99 100 // No more page loading. 101 await act(() => jest.runAllTimersAsync()); 102 expect(listBuildersSpy).toHaveBeenCalledTimes(3); 103 104 expect(builderTableMock).toHaveBeenCalledWith( 105 { 106 builders, 107 maxBatchSize: expect.anything(), 108 }, 109 expect.anything(), 110 ); 111 }); 112 });