Task #433: incoming-note popup — render full note + 3-button action row
- Inner body now renders as a real sticky note: keeps colored
surface, supports checklist (read-only) when noteKind === "checklist".
- Action row trimmed to exactly 3 buttons: Done (تم), Open note
(فتح الملاحظة), Reply (رد). Removed the standalone Dismiss button;
header X still closes.
- Reply variant uses the same 3-button row (Done is a dismiss-only
no-op since the note owner can't mark their own note read).
- Wire kind + items from backend `note_received` payload into client
IncomingNotePayload (noteKind/items) via use-notifications-socket.
- Locales: markRead → "Done"/"تم"; new openNote/done keys; openThread
unified to "Open note"/"فتح الملاحظة".
- Tests: replaced removed `incoming-note-popup-dismiss` testid usages
with header `-close`; updated reply variant assertions to expect the
3-button row; fixed pre-existing pluralization bug in reply path
(`/replies` → `/reply`) that was unrelated to this task but blocked
the reply popup test from validating.
Labels and pin were not surfaced in the popup because they are
sender-private state (note_recipients only snapshots
title/content/color/kind/items); the recipient never receives the
sender's labelIds/isPinned, so showing them would be misleading.
Added a new e2e ("recipient popup renders checklist items for a
checklist note") that composes a checklist note via the UI, sends it,
and asserts `incoming-note-popup-checklist` is visible with both
items — locks in the noteKind/items socket plumbing.
All affected e2e tests pass (notes-popup-on-receive note + reply +
checklist, notes-popup-touch-tap both, notes-inbox); queue unit
tests pass.
This commit is contained in:
@@ -377,3 +377,105 @@ test("sender sees a floating reply card when the recipient replies", async ({
|
||||
await recipientCtx.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Task #433: a checklist note must render the inner sticky-note "as a
|
||||
// real note" — i.e. the popup body shows a read-only checklist preview
|
||||
// (not the plain text body), proving `noteKind` + `items` are forwarded
|
||||
// from the backend through the socket payload into the popup.
|
||||
test("recipient popup renders checklist items for a checklist note", async ({
|
||||
browser,
|
||||
}) => {
|
||||
test.setTimeout(90_000);
|
||||
const sender = await createUser("popup_cl_sender", "Popup CL Sender");
|
||||
const recipient = await createUser("popup_cl_recipient", "Popup CL Recipient");
|
||||
|
||||
const senderCtx = await browser.newContext();
|
||||
const recipientCtx = await browser.newContext();
|
||||
const senderPage = await senderCtx.newPage();
|
||||
const recipientPage = await recipientCtx.newPage();
|
||||
|
||||
try {
|
||||
await loginViaUi(recipientPage, recipient.username);
|
||||
await recipientPage.goto("/");
|
||||
await recipientPage.waitForTimeout(3500);
|
||||
|
||||
await loginViaUi(senderPage, sender.username);
|
||||
await senderPage.goto("/notes");
|
||||
await expect(senderPage.getByTestId("notes-page")).toBeVisible();
|
||||
|
||||
const noteTitle = `Checklist Note ${uniq()}`;
|
||||
await senderPage.getByTestId("notes-composer-open").click();
|
||||
await senderPage.getByTestId("notes-composer-title").fill(noteTitle);
|
||||
// Switch composer to checklist mode and add two items.
|
||||
await senderPage.getByTestId("notes-composer-kind-toggle").click();
|
||||
const list = senderPage.getByTestId("notes-composer-checklist-list");
|
||||
await expect(list).toBeVisible();
|
||||
// List starts empty — click "Add item" to insert the first row,
|
||||
// fill it, press Enter to spawn a second row, fill that one too.
|
||||
await senderPage.getByRole("button", { name: "Add item" }).click();
|
||||
// The ChecklistEditor uses shadcn `Input` (no explicit `type=text`),
|
||||
// so match any non-checkbox input inside the list container.
|
||||
const itemInputs = list.locator("input:not([type=checkbox])");
|
||||
await expect(itemInputs.first()).toBeVisible();
|
||||
await itemInputs.first().fill("Buy milk");
|
||||
await itemInputs.first().press("Enter");
|
||||
await expect(itemInputs.nth(1)).toBeVisible();
|
||||
await itemInputs.nth(1).fill("Walk dog");
|
||||
await itemInputs.nth(1).blur();
|
||||
|
||||
const createPromise = senderPage.waitForResponse(
|
||||
(r) =>
|
||||
new URL(r.url()).pathname === "/api/notes" &&
|
||||
r.request().method() === "POST" &&
|
||||
r.status() >= 200 &&
|
||||
r.status() < 300,
|
||||
{ timeout: 15_000 },
|
||||
);
|
||||
await senderPage.getByTestId("notes-composer-save").click();
|
||||
const createResp = await createPromise;
|
||||
const createdNote = await createResp.json();
|
||||
createdNoteIds.push(createdNote.id);
|
||||
|
||||
const card = senderPage.getByTestId(`note-card-${createdNote.id}`);
|
||||
await expect(card).toBeVisible();
|
||||
await card.hover();
|
||||
await senderPage.getByTestId(`note-card-send-${createdNote.id}`).click();
|
||||
await expect(senderPage.getByTestId("send-note-dialog")).toBeVisible();
|
||||
await senderPage
|
||||
.getByTestId("send-note-search")
|
||||
.fill(recipient.displayEn);
|
||||
await senderPage
|
||||
.getByTestId(`send-recipient-option-${recipient.id}`)
|
||||
.click();
|
||||
const sendPromise = senderPage.waitForResponse(
|
||||
(r) =>
|
||||
new URL(r.url()).pathname === `/api/notes/${createdNote.id}/send` &&
|
||||
r.request().method() === "POST" &&
|
||||
r.status() >= 200 &&
|
||||
r.status() < 300,
|
||||
{ timeout: 15_000 },
|
||||
);
|
||||
await senderPage.getByTestId("send-note-submit").click();
|
||||
await expect(senderPage.getByTestId("send-note-confirm")).toBeVisible();
|
||||
await senderPage.getByTestId("send-note-confirm-submit").click();
|
||||
await sendPromise;
|
||||
|
||||
const popup = recipientPage.getByTestId("incoming-note-popup");
|
||||
await expect(popup).toBeVisible({ timeout: 5_000 });
|
||||
await expect(popup).toContainText(noteTitle);
|
||||
// Checklist branch: dedicated container is rendered, with each item
|
||||
// as a row, and the plain text content branch is NOT used.
|
||||
const checklist = recipientPage.getByTestId(
|
||||
"incoming-note-popup-checklist",
|
||||
);
|
||||
await expect(checklist).toBeVisible();
|
||||
await expect(checklist).toContainText("Buy milk");
|
||||
await expect(checklist).toContainText("Walk dog");
|
||||
|
||||
await recipientPage.getByTestId("incoming-note-popup-close").click();
|
||||
await expect(popup).toBeHidden();
|
||||
} finally {
|
||||
await senderCtx.close();
|
||||
await recipientCtx.close();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user