diff --git a/src/__tests__/CompareResults/Retrigger.test.tsx b/src/__tests__/CompareResults/Retrigger.test.tsx index 302972e45..fc3a1511e 100644 --- a/src/__tests__/CompareResults/Retrigger.test.tsx +++ b/src/__tests__/CompareResults/Retrigger.test.tsx @@ -127,6 +127,25 @@ describe('Retrigger', () => { mockedGetLocationOrigin.mockImplementation(() => 'http://localhost:3000'); }); + async function openRetriggerConfigModal( + compareResult: typeof result = result, + ) { + setUpUserCredentials(); + render(); + + const openModalButton = await screen.findByRole('button', { + name: 'retrigger jobs', + }); + + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + await user.click(openModalButton); + + return { + baseSelect: await screen.findByLabelText('Base'), + newSelect: await screen.findByLabelText('New'), + }; + } + it('should display Sign In modal when there are no credentials', async () => { render(); @@ -267,6 +286,47 @@ describe('Retrigger', () => { expect(new MockedHooks().triggerHook).toHaveBeenCalled(); }); + + it('should disable the Base dropdown when there are no base retriggerable jobs', async () => { + const { baseSelect, newSelect } = await openRetriggerConfigModal({ + ...result, + base_retriggerable_job_ids: [], + }); + + // MUI Select uses a combobox div with aria-disabled rather than native disabled. + expect(baseSelect).toHaveAttribute('aria-disabled', 'true'); + expect(baseSelect).toHaveTextContent('0'); + expect(newSelect).not.toHaveAttribute('aria-disabled', 'true'); + expect(newSelect).toHaveTextContent('5'); + expect(screen.getByRole('button', { name: 'Retrigger' })).toBeEnabled(); + }); + + it('should disable the New dropdown when there are no new retriggerable jobs', async () => { + const { baseSelect, newSelect } = await openRetriggerConfigModal({ + ...result, + new_retriggerable_job_ids: [], + }); + + expect(baseSelect).not.toHaveAttribute('aria-disabled', 'true'); + expect(baseSelect).toHaveTextContent('5'); + expect(newSelect).toHaveAttribute('aria-disabled', 'true'); + expect(newSelect).toHaveTextContent('0'); + expect(screen.getByRole('button', { name: 'Retrigger' })).toBeEnabled(); + }); + + it('should disable both dropdowns when there are no retriggerable jobs', async () => { + const { baseSelect, newSelect } = await openRetriggerConfigModal({ + ...result, + base_retriggerable_job_ids: [], + new_retriggerable_job_ids: [], + }); + + expect(baseSelect).toHaveAttribute('aria-disabled', 'true'); + expect(baseSelect).toHaveTextContent('0'); + expect(newSelect).toHaveAttribute('aria-disabled', 'true'); + expect(newSelect).toHaveTextContent('0'); + expect(screen.getByRole('button', { name: 'Retrigger' })).toBeDisabled(); + }); }); describe('Retrigger in Subtests view', () => { diff --git a/src/components/CompareResults/Retrigger/RetriggerButton.tsx b/src/components/CompareResults/Retrigger/RetriggerButton.tsx index 520be1425..7c4f5bc96 100644 --- a/src/components/CompareResults/Retrigger/RetriggerButton.tsx +++ b/src/components/CompareResults/Retrigger/RetriggerButton.tsx @@ -210,6 +210,8 @@ export function RetriggerButton({ result, variant }: RetriggerButtonProps) { open={status === 'retrigger-modal'} onClose={() => setStatus('pending')} onRetriggerClick={onRetriggerConfirm} + hasBaseJobs={baseRetriggerableJobIds.length > 0} + hasNewJobs={newRetriggerableJobIds.length > 0} /> ); diff --git a/src/components/CompareResults/Retrigger/RetriggerConfigModal.tsx b/src/components/CompareResults/Retrigger/RetriggerConfigModal.tsx index 32220adb2..569be654d 100644 --- a/src/components/CompareResults/Retrigger/RetriggerConfigModal.tsx +++ b/src/components/CompareResults/Retrigger/RetriggerConfigModal.tsx @@ -16,17 +16,24 @@ const retriggerStrings = Strings.components.retrigger.config; function RetriggerCountSelect({ prefix, label, + disabled = false, }: { prefix: string; label: string; + disabled?: boolean; }) { return ( - + {label} + {/* + defaultValue is safe here because CenteredModal unmounts when closed, + so the select remounts with a fresh default each time the modal opens. + FormControl's disabled state is passed to Select via context. + */}