go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/test_verdict/pages/recent_regressions_page/recent_regressions.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 { 16 act, 17 cleanup, 18 fireEvent, 19 render, 20 screen, 21 } from '@testing-library/react'; 22 23 import { 24 ChangepointsClientImpl, 25 QueryChangepointGroupSummariesResponse, 26 } from '@/proto/go.chromium.org/luci/analysis/proto/v1/changepoints.pb'; 27 import { FakeContextProvider } from '@/testing_tools/fakes/fake_context_provider'; 28 import { URLObserver } from '@/testing_tools/url_observer'; 29 import { NEVER_PROMISE } from '@/testing_tools/utils'; 30 31 import { RecentRegressions } from './recent_regressions'; 32 33 const mockedResponse = QueryChangepointGroupSummariesResponse.fromJSON({ 34 groupSummaries: [ 35 { 36 canonicalChangepoint: { 37 project: 'proj', 38 testId: 'prefix/testid', 39 variantHash: 'vhash', 40 refHash: 'refhash', 41 ref: { 42 gitiles: { 43 host: 'chromium.googlesource.com', 44 project: 'chromium/src', 45 ref: 'refs/heads/main', 46 }, 47 }, 48 startHour: '2020-01-01', 49 startPositionLowerBound99th: '123', 50 startPositionUpperBound99th: '125', 51 nominalStartPosition: '124', 52 previousSegmentNominalEndPosition: '120', 53 }, 54 statistics: { 55 count: 1, 56 unexpectedVerdictRateBefore: { 57 average: 0.012269938, 58 buckets: { 59 countLess5Percent: 1, 60 }, 61 }, 62 unexpectedVerdictRateAfter: { 63 average: 0.0625, 64 buckets: { 65 countAbove5LessThan95Percent: 1, 66 }, 67 }, 68 unexpectedVerdictRateCurrent: { 69 average: 0.0625, 70 buckets: { 71 countAbove5LessThan95Percent: 1, 72 }, 73 }, 74 unexpectedVerdictRateChange: { 75 countIncreased0To20Percent: 1, 76 }, 77 }, 78 }, 79 ], 80 }); 81 82 describe('<RecentRegressions />', () => { 83 let queryChangepointGroupSummariesMock: jest.SpiedFunction< 84 ChangepointsClientImpl['QueryChangepointGroupSummaries'] 85 >; 86 87 beforeEach(() => { 88 jest.useFakeTimers(); 89 queryChangepointGroupSummariesMock = jest 90 .spyOn(ChangepointsClientImpl.prototype, 'QueryChangepointGroupSummaries') 91 .mockResolvedValue(NEVER_PROMISE); 92 }); 93 94 afterEach(() => { 95 cleanup(); 96 queryChangepointGroupSummariesMock.mockClear(); 97 jest.useRealTimers(); 98 }); 99 100 it('can set predicate via search param', () => { 101 render( 102 <FakeContextProvider 103 mountedPath="/" 104 routerOptions={{ 105 initialEntries: ['/?cp=%7B"testIdPrefix"%3A"prefix"%7D'], 106 }} 107 > 108 <RecentRegressions project="proj" /> 109 </FakeContextProvider>, 110 ); 111 const prefixInputEle = screen.getByLabelText('Test ID prefix'); 112 expect(prefixInputEle).toHaveValue('prefix'); 113 }); 114 115 it('can save predicate to search param', () => { 116 const urlCallback = jest.fn(); 117 render( 118 <FakeContextProvider mountedPath="/"> 119 <RecentRegressions project="proj" /> 120 <URLObserver callback={urlCallback} /> 121 </FakeContextProvider>, 122 ); 123 124 const prefixInputEle = screen.getByLabelText('Test ID prefix'); 125 fireEvent.change(prefixInputEle, { target: { value: 'prefix' } }); 126 fireEvent.click(screen.getByText('Apply Filter')); 127 128 expect(urlCallback).toHaveBeenLastCalledWith( 129 expect.objectContaining({ 130 search: { 131 cp: JSON.stringify({ testIdPrefix: 'prefix' }), 132 }, 133 }), 134 ); 135 }); 136 137 it('can pass predicate to regression details link', async () => { 138 queryChangepointGroupSummariesMock.mockResolvedValue(mockedResponse); 139 render( 140 <FakeContextProvider 141 mountedPath="test" 142 routerOptions={{ 143 initialEntries: ['/test?cp=%7B"testIdPrefix"%3A"prefix"%7D'], 144 }} 145 > 146 <RecentRegressions project="proj" /> 147 </FakeContextProvider>, 148 ); 149 150 await act(() => jest.runAllTimersAsync()); 151 const link = screen.getByText('details').getAttribute('href')!; 152 const url = new URL(link, 'http://placeholder.com'); 153 const searchParams = Object.fromEntries(url.searchParams.entries()); 154 expect(searchParams).toEqual({ 155 cp: JSON.stringify({ testIdPrefix: 'prefix' }), 156 nsp: '124', 157 sh: '2020-01-01', 158 tvb: 'projects/proj/tests/prefix%2Ftestid/variants/vhash/refs/refhash', 159 }); 160 }); 161 });