From a1fb6645d994e72b5f39fae8bd482d400e4005b6 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Fri, 25 Sep 2026 15:52:02 -0700 Subject: [PATCH] test(desktop): synchronize upload edit smoke test (#7903) ## Summary Fix the upload/edit smoke-test race observed on main at `6410e685a80d42db0645fadc9bbe710559ef911e`. - Hold the mock upload at the Tauri IPC boundary and release it explicitly after checking edit rejection, rather than assuming menu interaction finishes within one second. - Wait for the Radix menu to unmount: edit dispatch happens in `onCloseAutoFocus`, after the click. Without that wait, the negative assertion can pass before the edit callback actually runs. - Keep the existing attachment-preservation and subsequent successful-edit assertions. No production code or CI coverage is changed. ## Evidence and scope - [Main failure](https://github.com/block/buzz/actions/runs/36189213149/job/108250341388): `opening edit during an immediate photo upload preserves the draft`, including an upload-progress timeout on retry. - Investigated from #7791, which changes only `docs/nips/NIP-AR.md`. Its PostgreSQL failures and video-menu timeout are separate; PostgreSQL passed on this main run, and the video spec passed locally. This PR does not claim to fix those failures or the separate pointer-interception failure seen in the main job. - #7843 already addresses unrelated runtime suites being selected for docs-only PRs. ## Validation - Full desktop `pnpm test`: 6,676 Node tests + 92 jsdom tests passed. - Upload/edit regression repeated 5 times: passed. Holding the upload without waiting for menu teardown failed all 5 runs, demonstrating the second race. - Complete file-attachment and video-attachment Playwright specs: 30 passed. - Pre-push desktop lint, typecheck, full desktop tests, and file-size gate passed at `46e3d4392e5b56dcb74ea864aa19aa10287f27d3`. - `just ci` attempted: initial formatting issue corrected; second run exceeded the 5-minute local limit during Tauri clippy. Not claiming full repository CI green. ## Review / human verification Self-reviewed the diff against the test contract; no production behavior changes. Draft pending human verification and CI. To verify: activate Hermit, then `cd desktop && pnpm test:e2e:smoke file-attachment.spec.ts --grep "opening edit during" --repeat-each=5`. Expect all five runs to pass, retaining the upload in the draft and allowing edit only after upload release. Originating conversation: buzz://message?channel=aa7f2b48-d367-4ca1-be09-116a2eb45b99&id=1b68585ba4483a87f5a453fd7bbdd11d434e332dcb4aff8acfe1f5d2601b6561 Signed-off-by: Smartie Co-authored-by: Smartie --- desktop/tests/e2e/file-attachment.spec.ts | 40 ++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/desktop/tests/e2e/file-attachment.spec.ts b/desktop/tests/e2e/file-attachment.spec.ts index d5680b6c90..2d8e5f8769 100644 --- a/desktop/tests/e2e/file-attachment.spec.ts +++ b/desktop/tests/e2e/file-attachment.spec.ts @@ -166,25 +166,49 @@ test("opening edit during an immediate photo upload preserves the draft", async page, }) => { await page.goto("/"); - await page.evaluate(() => { - const e2e = ( - window as Window & { - __BUZZ_E2E__?: { mock?: { uploadDelayMs?: number } }; - } - ).__BUZZ_E2E__; - if (e2e?.mock) e2e.mock.uploadDelayMs = 1_000; - }); await page.getByTestId("channel-general").click(); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + // Hold the upload at the IPC boundary until the edit assertion completes. + // A fixed delay races menu interaction and CI scheduling, so it can test + // an already-completed upload instead of the in-flight draft guard. + await page.evaluate(() => { + const w = window as Window & { + __RELEASE_PHOTO_UPLOAD__?: () => void; + __TAURI_INTERNALS__: { + invoke: ( + command: string, + payload: unknown, + options: unknown, + ) => Promise; + }; + }; + const original = w.__TAURI_INTERNALS__.invoke.bind(w.__TAURI_INTERNALS__); + const gate = new Promise((resolve) => { + w.__RELEASE_PHOTO_UPLOAD__ = resolve; + }); + w.__TAURI_INTERNALS__.invoke = async (command, payload, options) => { + if (command === "upload_media_bytes_raw") await gate; + return original(command, payload, options); + }; + }); await choosePhoto(page); await expect(page.getByTestId("upload-progress")).toBeVisible(); await openMoreActionsMenu(page, "mock-general-welcome"); await page.getByTestId("edit-message-mock-general-welcome").click(); + // Edit is dispatched by onCloseAutoFocus after Radix unmounts the menu, + // not by the click itself. Keep the upload held through that handoff. + await expect(page.getByRole("menu")).toHaveCount(0); // Edit entry is rejected while the compacted draft cannot represent the // reserved upload slot. The upload remains current and lands in the draft. await expect(page.getByTestId("edit-target")).toHaveCount(0); await expect(page.getByTestId("upload-progress")).toBeVisible(); + await page.evaluate(() => { + ( + window as Window & { __RELEASE_PHOTO_UPLOAD__: () => void } + ).__RELEASE_PHOTO_UPLOAD__(); + }); await expect(page.getByTestId("upload-progress")).toHaveCount(0, { timeout: 5_000, });