From 349c312989e591f9332f2386f72f24ce9c4aa476 Mon Sep 17 00:00:00 2001 From: ttipo Date: Tue, 3 Nov 2026 23:26:28 +0900 Subject: [PATCH] =?UTF-8?q?test(frontend):=20=EB=8C=80=EC=9A=A9=EB=9F=89?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EB=8B=A4=20=EC=98=AC=EB=A6=B0?= =?UTF-8?q?=20=EB=92=A4=20=EB=B3=B4=EB=82=B8=EB=8B=A4=20=E2=80=94=20?= =?UTF-8?q?=EC=A1=B0=EA=B0=81=20SHA-256=20=EC=9D=B4=20=EC=8A=A4=EB=A0=88?= =?UTF-8?q?=EB=93=9C=20=ED=92=80=EC=9D=B4=EB=9D=BC=20=EB=B6=80=ED=95=98?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8A=A6=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 쓰기 화면의 두 테스트는 대용량 파일을 고른 뒤 양보(flushPromises) 두 번 만에 다 올렸다고 보았다. 조각의 SHA-256 은 jsdom 이 아니라 Node 의 Web Crypto(libuv 스레드 풀)라, CI 부하에서 그 안에 끝나지 않았다. 다 올리기 전에 누른 [보내기]는 막혀 보내기 요청이 끝내 나가지 않아 payload 가 undefined 였다(2026-11-03 CI — ComposePage.spec.ts 첨부 합계 테스트). - 두 테스트(첨부 합계 · 링크로 보내기)는 대용량 줄이 ready 가 될 때까지 vi.waitFor 로 본다. 첨부 합계 테스트는 보내기 전에 막는 까닭이 없음도 단언한다. - test-utils/timeouts.ts 에 WAIT_FOR_TIMEOUT_MS(15초 — 테스트 한도 20초보다 짧게)를 둔다. - digest 를 늦춘 실험으로 고치기 전에는 깨지고 고친 뒤에는 통과함을 확인했다(실험은 되돌림). Co-Authored-By: Claude Opus 5.5 --- .../pages/mail/ComposePage.large-files.spec.ts | 10 +++++++++- frontend/src/pages/mail/ComposePage.spec.ts | 16 ++++++++++++++-- frontend/src/test-utils/timeouts.ts | 8 ++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/mail/ComposePage.large-files.spec.ts b/frontend/src/pages/mail/ComposePage.large-files.spec.ts index f77ca84..cdced1f 100644 --- a/frontend/src/pages/mail/ComposePage.large-files.spec.ts +++ b/frontend/src/pages/mail/ComposePage.large-files.spec.ts @@ -21,6 +21,7 @@ import { } from '@/test-utils/mock-api' import { installProseMirrorDom } from '@/test-utils/prosemirror-dom' import { createTestRouter, routeViewProvide } from '@/test-utils/router' +import { WAIT_FOR_TIMEOUT_MS } from '@/test-utils/timeouts' import ComposePage from './ComposePage.vue' // 쓰기 화면의 대용량 첨부 (11단계 — 디자인 원본 ComposeLargeFiles · 계약 4절 "발송 · 임시 저장과 함께" · "인용 속 링크의 상태") @@ -367,7 +368,14 @@ describe('ComposePage — 대용량 첨부', () => { expect(wrapper.get('#compose-send-why').text()).toBe(SEND_BLOCK_TEXT.uploading('1')) releaseChunk() - await flushPromises() + // 조각을 받으면 끝내기까지 가 ready 가 된다 — 조각을 보내기 전의 SHA-256 은 Node 의 Web Crypto(스레드 풀)라 + // 부하가 크면 양보 두 번으로는 끝나지 않는다 (ComposePage.spec.ts 의 첨부 합계 테스트와 같은 까닭) — ready 가 될 때까지 본다 + await vi.waitFor( + () => { + if (phases(wrapper)[0] !== 'ready') throw new Error('대용량 파일을 올리는 중') + }, + { timeout: WAIT_FOR_TIMEOUT_MS }, + ) await flushPromises() expect(phases(wrapper)).toEqual(['ready']) diff --git a/frontend/src/pages/mail/ComposePage.spec.ts b/frontend/src/pages/mail/ComposePage.spec.ts index ce23e75..273840b 100644 --- a/frontend/src/pages/mail/ComposePage.spec.ts +++ b/frontend/src/pages/mail/ComposePage.spec.ts @@ -36,7 +36,7 @@ import { } from '@/test-utils/mock-api' import { editorOf, fileDragEvent, installProseMirrorDom, pasteEvent } from '@/test-utils/prosemirror-dom' import { createTestRouter, routeViewProvide } from '@/test-utils/router' -import { LONG_DRAFT_TIMEOUT_MS } from '@/test-utils/timeouts' +import { LONG_DRAFT_TIMEOUT_MS, WAIT_FOR_TIMEOUT_MS } from '@/test-utils/timeouts' import ComposePage from './ComposePage.vue' const USER = { email: 'ara.kim@arasystem.it.kr', name: '김아라' } @@ -599,7 +599,17 @@ describe('ComposePage', () => { configurable: true, }) await picker.trigger('change') - await flushPromises() + // 도면은 다 올려야(끝내기 응답 뒤 ready) [보내기]가 풀린다 — 그 전에 누른 보내기는 막혀 보내기 요청이 끝내 나가지 않는다. + // 조각의 SHA-256 은 Node 의 Web Crypto(스레드 풀)라 부하가 크면 양보 두 번으로는 끝나지 않았다 (2026-11-03 CI) — ready 가 될 때까지 본다 + await vi.waitFor( + () => { + const row = wrapper.find('[data-large-upload]') + if (!row.exists() || row.attributes('data-phase') !== 'ready') { + throw new Error('대용량 파일을 올리는 중') + } + }, + { timeout: WAIT_FOR_TIMEOUT_MS }, + ) await flushPromises() expect(wrapper.findAll('.upload-item').map((item) => item.classes())).toEqual([ @@ -612,6 +622,8 @@ describe('ComposePage', () => { ) expect(requests.filter((request) => request.url === '/mail-attachments')).toHaveLength(1) expect(useToastStore().toasts).toEqual([]) + // 다 올렸다 — [보내기]를 막는 까닭이 없다 (아래 보내기는 마이크로태스크로만 이어져 양보 한 번이면 요청이 나간다) + expect(wrapper.find('#compose-send-why').exists()).toBe(false) const input = wrapper.get('#compose-to') await input.setValue('junho.park@arasystem.it.kr') diff --git a/frontend/src/test-utils/timeouts.ts b/frontend/src/test-utils/timeouts.ts index 8777b25..d471b99 100644 --- a/frontend/src/test-utils/timeouts.ts +++ b/frontend/src/test-utils/timeouts.ts @@ -9,3 +9,11 @@ * 전체 한도에 가까워 이 테스트들에만 30초를 준다. */ export const LONG_DRAFT_TIMEOUT_MS = 30_000 + +/** + * 조건이 될 때까지 다시 보는 기다림(vi.waitFor)의 기한 — 몇 번 양보(flushPromises)해서는 끝난다고 믿을 수 없는 일을 기다린다. + * 예: 대용량 파일 조각의 SHA-256 — jsdom 이 아니라 Node 의 Web Crypto 라 스레드 풀에서 돌아, 부하가 크면 양보 두 번 안에 끝나지 않았다 + * (2026-11-03 CI — 다 올리기 전에 누른 [보내기]는 막혀 보내기 요청이 끝내 나가지 않는다). + * 테스트 한도(vitest.config.ts, 20초)보다 짧게 둬, 끝내 되지 않으면 테스트 한도보다 이 기다림이 먼저 까닭(던진 글)을 알린다. + */ +export const WAIT_FOR_TIMEOUT_MS = 15_000