Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/__tests__/CompareResults/Retrigger.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ describe('Retrigger', () => {
mockedGetLocationOrigin.mockImplementation(() => 'http://localhost:3000');
});

async function openRetriggerConfigModal(
compareResult: typeof result = result,
) {
setUpUserCredentials();
render(<RetriggerButton result={compareResult} variant='icon' />);

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(<RetriggerButton result={result} variant='icon' />);

Expand Down Expand Up @@ -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', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/components/CompareResults/Retrigger/RetriggerButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
</>
);
Expand Down
32 changes: 27 additions & 5 deletions src/components/CompareResults/Retrigger/RetriggerConfigModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ const retriggerStrings = Strings.components.retrigger.config;
function RetriggerCountSelect({
prefix,
label,
disabled = false,
}: {
prefix: string;
label: string;
disabled?: boolean;
}) {
return (
<FormControl sx={{ width: '100%' }}>
<FormControl sx={{ width: '100%' }} disabled={disabled}>
<InputLabel id={`${prefix}-retrigger-count-label`}>{label}</InputLabel>
{/*
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.
*/}
<Select
labelId={`${prefix}-retrigger-count-label`}
name={`${prefix}-retrigger-count`}
defaultValue={5}
defaultValue={disabled ? 0 : 5}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a brief explanatory comment here? It’s worth noting that using defaultValue is safe in this context because the modal unmounts when closed, ensuring the state re-initializes on each open.

label={label}
sx={{ height: 32 }}
>
Expand All @@ -44,6 +51,8 @@ type RetriggerModalProps = {
open: boolean;
onClose: () => unknown;
onRetriggerClick: (times: { baseTimes: number; newTimes: number }) => unknown;
hasBaseJobs: boolean;
hasNewJobs: boolean;
};

export function RetriggerConfigModal(props: RetriggerModalProps) {
Expand Down Expand Up @@ -77,18 +86,31 @@ export function RetriggerConfigModal(props: RetriggerModalProps) {
}}
>
<Grid size={3}>
<RetriggerCountSelect prefix='base' label='Base' />
<RetriggerCountSelect
prefix='base'
label='Base'
disabled={!props.hasBaseJobs}
/>
</Grid>
<Grid size={3}>
<RetriggerCountSelect prefix='new' label='New' />
<RetriggerCountSelect
prefix='new'
label='New'
disabled={!props.hasNewJobs}
/>
</Grid>
<Grid
size='auto'
sx={{
ml: 'auto',
}}
>
<Button type='submit'>{retriggerStrings.submitButton}</Button>
<Button
type='submit'
disabled={!props.hasBaseJobs && !props.hasNewJobs}
>
{retriggerStrings.submitButton}
</Button>
</Grid>
</Grid>
</form>
Expand Down