From 6f37ed02ad3205d88f6b23602517d292f8bb7180 Mon Sep 17 00:00:00 2001 From: krestar Date: Thu, 20 Aug 2026 11:56:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(client):=20=EC=97=85=EB=AC=B4=20?= =?UTF-8?q?=EC=9E=AC=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EC=B2=B4=EB=A5=98=20?= =?UTF-8?q?=EC=97=B0=EC=9E=A5=20=EB=AA=A8=EB=8B=AC=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CaseDetailPage/CaseDetailPage.test.tsx | 91 +++++++++++++++++++ src/pages/CaseDetailPage/CaseDetailPage.tsx | 2 +- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx index d4425ac..6bcb084 100644 --- a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx +++ b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx @@ -10,6 +10,7 @@ import type { DocumentReadinessResponse, DocumentRequestDraftResponse, } from '../../api/documents' +import type { RenewalExecutionResponse } from '../../api/renewal' import type { TaskDetailResponse } from '../../api/tasks' import type { WorkerLinkDeliveryResponse, WorkerResponseItemResponse } from '../../api/workerLinks' import { ToastViewport } from '../../components/ui/ToastViewport/ToastViewport' @@ -1249,6 +1250,96 @@ describe('CaseDetailPage', () => { expect(screen.getByRole('dialog', { name: 'Renewal Agent 실행' })).toBeInTheDocument() }) + it('keeps the Renewal result mounted while the Task refetch is loading', async () => { + const user = userEvent.setup() + mockTaskAndActivities({ status: 'DRAFT' }) + const defaultFetch = vi.mocked(fetch).getMockImplementation() + let taskFetchCount = 0 + const askHrResponse: RenewalExecutionResponse = { + request_id: 'R-1', + task_id: 'T-1', + task_status: 'NEEDS_INFO', + task_version: 4, + intent: 'EXPIRY_RENEWAL', + workflow_id: 'WF-STY-001', + confidence: 0.9, + scenario: 'ask_hr', + outcome: 'NEEDS_INFO', + missing_slots: [ + 'wage', + 'working_hours', + 'job_description', + 'work_location', + 'contract_period', + ], + requested_fields: [ + { key: 'wage', source_hint: 'USER_INPUT' }, + { key: 'working_hours', source_hint: 'USER_INPUT' }, + { key: 'job_description', source_hint: 'USER_INPUT' }, + { key: 'work_location', source_hint: 'USER_INPUT' }, + { key: 'contract_period', source_hint: 'USER_INPUT' }, + ], + case_signals: ['REQUEST_CONTRACT_SLOTS', 'NEEDS_INFO'], + generated_documents: [], + worker_message_draft_id: null, + worker_message_draft_version: null, + guide_review_required: false, + guide_failure_code: null, + guide_review_draft: null, + human_review_required: true, + } + + vi.mocked(fetch).mockImplementation((input, init) => { + const url = String(input) + if (url.endsWith('/tasks/T-1/renewal-run')) { + return Promise.resolve(jsonResponse(askHrResponse)) + } + if (url.endsWith('/tasks/T-1')) { + taskFetchCount += 1 + if (taskFetchCount > 1) return new Promise(() => {}) + } + return defaultFetch!(input, init) + }) + + renderPage() + await screen.findByText('응웬반A 체류연장 준비') + await user.click(screen.getByRole('button', { name: '더보기 ···' })) + await user.click(screen.getByRole('menuitem', { name: 'Renewal 실행' })) + await user.type( + screen.getByPlaceholderText('예: 응웬반A 체류기간 연장 준비해줘'), + '최신 정보 기준으로 다시 분석해줘', + ) + await user.click(screen.getByRole('button', { name: '실행' })) + + await waitFor(() => expect(taskFetchCount).toBe(2)) + const dialog = screen.getByRole('dialog', { name: 'Renewal Agent 실행' }) + expect(screen.queryByText('업무 정보를 불러오는 중입니다')).not.toBeInTheDocument() + expect(within(dialog).getByLabelText('월 임금(원)')).toBeInTheDocument() + expect(within(dialog).getByLabelText('주당 근로시간')).toBeInTheDocument() + expect(within(dialog).getByLabelText('담당 업무')).toBeInTheDocument() + expect(within(dialog).getByLabelText('근무 장소')).toBeInTheDocument() + expect(within(dialog).getByLabelText('계약 기간')).toBeInTheDocument() + expect( + within(dialog).getByRole('button', { name: '답변 제출하고 다시 실행' }), + ).toBeInTheDocument() + + await user.type(within(dialog).getByLabelText('월 임금(원)'), '2500000') + expect(within(dialog).getByLabelText('월 임금(원)')).toHaveValue(2500000) + await user.click(within(dialog).getByRole('button', { name: '답변 제출하고 다시 실행' })) + + await waitFor(() => { + const renewalCalls = vi + .mocked(fetch) + .mock.calls.filter(([url]) => String(url).endsWith('/tasks/T-1/renewal-run')) + expect(renewalCalls).toHaveLength(2) + expect(JSON.parse(String(renewalCalls[1][1]?.body))).toEqual({ + instruction: '최신 정보 기준으로 다시 분석해줘', + expected_version: 4, + slot_answers: { wage: '2500000' }, + }) + }) + }) + it('changes the task assignee from the more menu', async () => { const user = userEvent.setup() mockTaskAndActivities() diff --git a/src/pages/CaseDetailPage/CaseDetailPage.tsx b/src/pages/CaseDetailPage/CaseDetailPage.tsx index b144802..9a9ceb7 100644 --- a/src/pages/CaseDetailPage/CaseDetailPage.tsx +++ b/src/pages/CaseDetailPage/CaseDetailPage.tsx @@ -932,7 +932,7 @@ export function CaseDetailPage() { } } - if (taskStatus === 'loading') { + if (taskStatus === 'loading' && !task) { return (
Date: Thu, 20 Aug 2026 13:15:37 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix(test):=20RUN=5FRENEWAL=20=EC=A0=95?= =?UTF-8?q?=EC=B1=85=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/CaseDetailPage/CaseDetailPage.test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx index 332d660..b551ffd 100644 --- a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx +++ b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx @@ -1308,7 +1308,12 @@ describe('CaseDetailPage', () => { it('keeps the Renewal result mounted while the Task refetch is loading', async () => { const user = userEvent.setup() - mockTaskAndActivities({ status: 'DRAFT' }) + mockTaskAndActivities({ + status: 'DRAFT', + next_action: 'RUN_RENEWAL', + available_actions: ['RUN_RENEWAL'], + blocked_reason: null, + }) const defaultFetch = vi.mocked(fetch).getMockImplementation() let taskFetchCount = 0 const askHrResponse: RenewalExecutionResponse = {