From 1bf2d7956d66c1e62389f731b5750f7615b33588 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 22 Jul 2026 09:16:29 +0500 Subject: [PATCH] fix(desktop): clear stale validation error when reopening the schedule modal ScheduleModal is rendered unconditionally by SchedulesView and ScheduleDetailView, and its not-open case is an early return inside the component, so it never unmounts and its state survives close/reopen. The isOpen effect reset internalValidationError only in the create branch, so an error raised while creating a schedule stayed on screen when the modal was reopened to edit one. The error banner renders above the isEditMode block, so it showed in edit mode too. Reset the validation error whenever the modal opens, before branching on create vs edit. Adds a test that needs no mocks on this path. Signed-off-by: Seydi Charyyev --- .../src/components/schedule/ScheduleModal.tsx | 2 +- .../schedule/__tests__/ScheduleModal.test.tsx | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 ui/desktop/src/components/schedule/__tests__/ScheduleModal.test.tsx diff --git a/ui/desktop/src/components/schedule/ScheduleModal.tsx b/ui/desktop/src/components/schedule/ScheduleModal.tsx index a88ca54ade50..ddf06ec0843e 100644 --- a/ui/desktop/src/components/schedule/ScheduleModal.tsx +++ b/ui/desktop/src/components/schedule/ScheduleModal.tsx @@ -109,6 +109,7 @@ export const ScheduleModal: React.FC = ({ useEffect(() => { if (isOpen) { + setInternalValidationError(null); if (schedule) { setScheduleId(schedule.id); setCronExpression(schedule.cron); @@ -119,7 +120,6 @@ export const ScheduleModal: React.FC = ({ setDeepLinkInput(''); setParsedRecipe(null); setCronExpression('0 0 14 * * *'); - setInternalValidationError(null); if (initialDeepLink) { setSourceType('deeplink'); handleDeepLinkChange(initialDeepLink); diff --git a/ui/desktop/src/components/schedule/__tests__/ScheduleModal.test.tsx b/ui/desktop/src/components/schedule/__tests__/ScheduleModal.test.tsx new file mode 100644 index 000000000000..42148f3caa81 --- /dev/null +++ b/ui/desktop/src/components/schedule/__tests__/ScheduleModal.test.tsx @@ -0,0 +1,41 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, type RenderOptions, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { ScheduledJobDto } from '@aaif/goose-sdk'; +import { ScheduleModal } from '../ScheduleModal'; +import { IntlTestWrapper } from '../../../i18n/test-utils'; + +const renderWithIntl = (ui: React.ReactElement, options?: RenderOptions) => + render(ui, { wrapper: IntlTestWrapper, ...options }); + +const existingSchedule = { + id: 'daily-summary-job', + cron: '0 0 14 * * *', +} as ScheduledJobDto; + +const baseProps = { + onClose: vi.fn(), + onSubmit: vi.fn().mockResolvedValue(undefined), + isLoadingExternally: false, + apiErrorExternally: null, + initialDeepLink: null, +}; + +describe('ScheduleModal', () => { + it('clears a validation error from create mode when reopened to edit a schedule', async () => { + const user = userEvent.setup(); + const { rerender } = renderWithIntl(); + + await user.type(screen.getByLabelText(/name/i), 'my-job'); + await user.click(screen.getByRole('button', { name: 'Create Schedule' })); + await waitFor(() => { + expect(screen.getByText('Please provide a valid recipe source.')).toBeInTheDocument(); + }); + + rerender(); + rerender(); + + expect(screen.getByText('Edit Schedule')).toBeInTheDocument(); + expect(screen.queryByText('Please provide a valid recipe source.')).not.toBeInTheDocument(); + }); +});