diff --git a/frontend/e2e/letter.spec.ts b/frontend/e2e/letter.spec.ts index 6ef1a3a..dc1e3e5 100644 --- a/frontend/e2e/letter.spec.ts +++ b/frontend/e2e/letter.spec.ts @@ -27,16 +27,15 @@ test.describe("Letter Drafting (Real Backend)", () => { logger.info(`>> [Draft] Current URL after click: ${page.url()}`); - // Wait for the recipient input to be present in the DOM + // Editor page + await expect(page.getByTestId("recipient-input")).toBeVisible(); const recipientInput = page.getByTestId("recipient-input"); - await recipientInput.waitFor({ state: "visible", timeout: 20000 }); const recipientName = "Dear Friend"; await recipientInput.fill(recipientName); // Initial load: verify textarea value (populated by Fabric when focused) const canvasInput = page.locator("textarea"); - await canvasInput.waitFor({ state: "attached" }); await canvasInput.focus(); await expect(canvasInput).toHaveValue(/Take a deep breath/i); @@ -50,7 +49,7 @@ test.describe("Letter Drafting (Real Backend)", () => { await page.getByTestId("draft-btn").click(); // Verify Success Modal/Alert - await expect(page.getByText(/your letter is saved/i)).toBeVisible(); + await expect(page.getByTestId("save-success-toast")).toBeVisible(); // Verify URL updated with a UUID await expect(page).toHaveURL(/\/quill\/[0-9a-f-]{36}/); @@ -62,13 +61,7 @@ test.describe("Letter Drafting (Real Backend)", () => { await page.goto(savedUrl); // Wait for initial load overlay to appear and then definitely disappear - await page - .getByText(/opening your draft/i) - .waitFor({ state: "visible", timeout: 2000 }) - .catch(() => {}); - await expect(page.getByText(/opening your draft/i)).toBeHidden({ - timeout: 10000, - }); + await expect(page.getByTestId("opening-draft-overlay")).toBeHidden(); // Check recipient await expect(page.getByTestId("recipient-input")).toHaveValue(recipientName); @@ -77,9 +70,7 @@ test.describe("Letter Drafting (Real Backend)", () => { // We wait for the content to appear in the textarea. // toHaveValue will poll until it matches or timeouts. await canvasInput.focus(); - await expect(canvasInput).toHaveValue(/This is a secret draft/i, { - timeout: 10000, - }); + await expect(canvasInput).toHaveValue(/This is a secret draft/i); await expect(canvasInput).toHaveValue(/It should persist/i); }); @@ -96,7 +87,6 @@ test.describe("Letter Drafting (Real Backend)", () => { await page.getByTestId("write-letter-btn").click(); const recipientInput = page.getByTestId("recipient-input"); - await recipientInput.waitFor({ state: "visible", timeout: 10000 }); await recipientInput.fill("A Secret Guest"); const canvasInput = page.locator("textarea"); @@ -110,40 +100,36 @@ test.describe("Letter Drafting (Real Backend)", () => { // Should show sealed confirmation modal logger.info(">> [Seal] Verifying sealed modal..."); - await expect(page.getByText(/your letter is sealed/i)).toBeVisible({ - timeout: 10000, - }); + await expect(page.getByTestId("post-seal-modal")).toBeVisible(); // Navigate to Reader via "View letter" await page.getByTestId("view-letter-btn").click(); // Should be on Reader URL - await expect(page).toHaveURL(/\/read\/[a-f0-9-]{36}$/, { timeout: 15000 }); + await expect(page).toHaveURL(/\/read\/[a-f0-9-]{36}$/); // Open the envelope to reveal the letter - await expect(page.getByText(/breaking the seal/i)).toBeHidden({ - timeout: 10000, - }); + await expect(page.getByTestId("decryption-overlay")).toBeHidden(); // Flip the envelope to show the seal and reveal the letter await revealEnvelope(page); - await expect(page.getByTestId("envelope-letter")).toBeHidden({ timeout: 20000 }); + await expect(page.getByTestId("envelope-letter")).toBeHidden(); // Share on demand logger.info(">> [Seal] Clicking Share button in Reader..."); await page.getByTestId("share-letter-btn").click(); // Verify share modal with a valid link - await expect(page.getByText(/send this letter/i)).toBeVisible(); + await expect(page.getByTestId("share-letter-modal")).toBeVisible(); const linkInput = page.locator("#share-link-input"); const linkValue = await linkInput.inputValue(); expect(linkValue).toContain("/read/"); expect(linkValue).toContain("#"); logger.info(`>> [Seal] Sharing link: ${linkValue}`); - await expect(page.getByRole("button", { name: /copy/i })).toBeVisible(); + await expect(page.getByTestId("copy-link-btn")).toBeVisible(); // Assuming Close button in ShareModal might need a testid too, but for now let's use text if unique or add testid - await page.getByRole("button", { name: /close/i }).click(); - await expect(page.getByText(/send this letter/i)).toBeHidden(); + await page.getByTestId("modal-close-btn").click(); + await expect(page.getByTestId("share-letter-modal")).toBeHidden(); }); test("should allow author to access sealed letter from drawer without sharing key", async ({ @@ -161,7 +147,6 @@ test.describe("Letter Drafting (Real Backend)", () => { await page.getByTestId("write-letter-btn").click(); const recipientInput = page.getByTestId("recipient-input"); - await recipientInput.waitFor({ state: "visible" }); await recipientInput.fill(recipientName); const canvasInput = page.locator("textarea"); @@ -173,34 +158,30 @@ test.describe("Letter Drafting (Real Backend)", () => { await page.getByTestId("seal-confirm-btn").click(); // Sealed modal should appear — click "Keep it" to go to Drawer - await expect(page.getByText(/your letter is sealed/i)).toBeVisible({ - timeout: 10000, - }); + await expect(page.getByTestId("post-seal-modal")).toBeVisible(); await page.getByTestId("keep-it-btn").click(); // Open "Kept" section - search for the section with id='kept' and click its toggle button logger.info(">> [Drawer] Opening Kept section..."); - const keptSection = page.locator("#kept"); - await keptSection.getByRole("button", { name: /kept/i }).click(); + await page.getByTestId("drawer-section-kept").click(); // Find the sealed letter in the drawer by recipient name and click it logger.info(">> [Drawer] Clicking sealed letter in drawer..."); const sealedItem = page - .getByRole("button", { name: new RegExp(recipientName, "i") }) + .getByTestId(/^letter-item-/) + .filter({ hasText: recipientName }) .first(); await sealedItem.click(); // Verify it opens the Reader without a hash logger.info(">> [Drawer] Verifying Reader page..."); // Give it a bit more time for decryption - await expect(page).toHaveURL(/\/read\/[a-f0-9-]{36}$/, { timeout: 15000 }); + await expect(page).toHaveURL(/\/read\/[a-f0-9-]{36}$/); // Reveal and check decrypted content in Reader - await expect(page.getByText(/breaking the seal/i)).toBeHidden({ - timeout: 10000, - }); + await expect(page.getByTestId("decryption-overlay")).toBeHidden(); // Flip the envelope and reveal the letter await revealEnvelope(page); - await expect(page.getByTestId("envelope-letter")).toBeHidden({ timeout: 20000 }); + await expect(page.getByTestId("envelope-letter")).toBeHidden(); // Also check if we are redirected to the Reader if we manually go to the Editor URL const readerUrl = page.url(); diff --git a/frontend/e2e/utils/auth.ts b/frontend/e2e/utils/auth.ts index 66b86e8..3e92f04 100644 --- a/frontend/e2e/utils/auth.ts +++ b/frontend/e2e/utils/auth.ts @@ -38,7 +38,7 @@ async function registerAndLogin( await page.goto(activationLink); - await expect(page.getByText(/account activated/i)).toBeVisible(); + await expect(page.getByTestId("activation-success-header")).toBeVisible(); await page.getByTestId("start-writing-btn").click(); // Dismiss the Welcom Modal and Perform Login diff --git a/frontend/src/components/drawer/DrawerSection.tsx b/frontend/src/components/drawer/DrawerSection.tsx index 0865d9b..f189bf3 100644 --- a/frontend/src/components/drawer/DrawerSection.tsx +++ b/frontend/src/components/drawer/DrawerSection.tsx @@ -35,6 +35,7 @@ export function DrawerSection({ - - )} + }; - {status === "error" && ( -
-
- -
-

Activation Failed

-

- The link might be expired or already used. Please try registering - again. -

-
- + activateAccount(); + }, [uidb64, token]); + + return ( +
+ {status === "loading" && ( +
+ +

Activating your account...

+
+ )} + + {status === "success" && ( +
+
+ +
+

+ You're in. +

+

+ Welcome to +
+ Just one more step and you can start writing timeless letters. +

+
+ +
+ )} + + {status === "error" && ( +
+
+ +
+

Activation Failed

+

+ The link might be expired or already used. Please try registering + again. +

+
+ +
+ )}
- )} -
- ); + ); } diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx index 418811f..912ac1e 100644 --- a/frontend/src/pages/Editor.tsx +++ b/frontend/src/pages/Editor.tsx @@ -376,7 +376,10 @@ export default function Editor() { weight="bold" className="animate-spin text-primary" /> -

+

Opening your draft...

@@ -406,6 +409,7 @@ export default function Editor() { {saveOverlay === "SAVED" && (
-

+

Breaking the seal...