import { SELECTORS } from "../../fixtures/selectors"; import { expect, test } from "../../fixtures/test"; test.describe("Hero and Feed Browsing @smoke", () => { test.beforeEach(async ({ gotoApp, waitForAppReady }) => { await gotoApp(); await waitForAppReady(); }); test("hero section loads with article content", async ({ page, waitForHero, }) => { const hero = await waitForHero(); // Hero should be visible await expect(hero).toBeVisible(); // Hero should have headline const headline = hero.locator(SELECTORS.hero.headline); await expect(headline).toBeVisible(); await expect(headline).not.toBeEmpty(); // Hero should have summary const summary = hero.locator(SELECTORS.hero.summary); await expect(summary).toBeVisible(); await expect(summary).not.toBeEmpty(); // Hero should have "Read TL;DR" button const readButton = hero.locator(SELECTORS.hero.readButton); await expect(readButton).toBeVisible(); await expect(readButton).toBeEnabled(); // Hero should have image const image = hero.locator(SELECTORS.hero.image); await expect(image).toBeVisible(); }); test("news feed loads with multiple articles", async ({ page, waitForFeed, }) => { const feed = await waitForFeed(); // Feed section should be visible await expect(feed).toBeVisible(); // Should have "Recent News" heading const heading = feed.locator("h2"); await expect(heading).toContainText("Recent News"); // Should have multiple article cards (at least 1) const articles = feed.locator(SELECTORS.feed.articles); const count = await articles.count(); expect(count).toBeGreaterThanOrEqual(1); // Each article should have required elements const firstArticle = articles.first(); await expect(firstArticle.locator("h3")).toBeVisible(); await expect( firstArticle.locator(SELECTORS.feed.articleSummary), ).toBeVisible(); await expect( firstArticle.locator(SELECTORS.feed.articleReadButton), ).toBeVisible(); }); test("feed article cards have correct structure", async ({ page, waitForFeed, }) => { const feed = await waitForFeed(); const articles = feed.locator(SELECTORS.feed.articles); // Check structure of first article const firstArticle = articles.first(); // Should have image container const imageContainer = firstArticle.locator(".relative.h-48"); await expect(imageContainer).toBeVisible(); // Should have content area const contentArea = firstArticle.locator(".p-5"); await expect(contentArea).toBeVisible(); // Should have headline const headline = firstArticle.locator("h3"); await expect(headline).toBeVisible(); await expect(headline).toHaveClass(/news-card-title/); // Should have summary const summary = firstArticle.locator(SELECTORS.feed.articleSummary); await expect(summary).toBeVisible(); await expect(summary).toHaveClass(/news-card-summary/); }); test("hero article displays correct metadata", async ({ page, waitForHero, }) => { const hero = await waitForHero(); // Should have "LATEST" pill const latestPill = hero.locator(SELECTORS.hero.latestPill); await expect(latestPill).toBeVisible(); await expect(latestPill).toContainText("LATEST"); // Should have time ago const timePill = hero.locator(SELECTORS.hero.timePill); await expect(timePill).toBeVisible(); // Time should contain "ago" or "just now" const timeText = await timePill.textContent(); expect(timeText).toMatch(/ago|just now/); }); test("source link is present and clickable in hero", async ({ page, waitForHero, }) => { const hero = await waitForHero(); const sourceLink = hero.locator(SELECTORS.hero.sourceLink); // Source link may not be present if no source URL const count = await sourceLink.count(); if (count > 0) { await expect(sourceLink).toBeVisible(); await expect(sourceLink).toHaveAttribute("href"); await expect(sourceLink).toHaveAttribute("target", "_blank"); await expect(sourceLink).toHaveAttribute("rel", "noopener"); } }); }); test.describe("Summary Modal Flows", () => { test.beforeEach(async ({ gotoApp, waitForAppReady }) => { await gotoApp(); await waitForAppReady(); }); test("opens summary modal from hero @smoke", async ({ page, waitForHero, isSummaryModalOpen, }) => { const hero = await waitForHero(); // Click "Read TL;DR" button in hero const readButton = hero.locator(SELECTORS.hero.readButton); await readButton.click(); // Modal should open const isOpen = await isSummaryModalOpen(); expect(isOpen).toBe(true); // Modal should have correct structure const modal = page.locator(SELECTORS.summaryModal.root); await expect(modal.locator(SELECTORS.summaryModal.headline)).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.tldrSection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.summarySection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.sourceSection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.shareSection), ).toBeVisible(); }); test("opens summary modal from feed article @smoke", async ({ page, waitForFeed, isSummaryModalOpen, }) => { const feed = await waitForFeed(); // Get first feed article const articles = feed.locator(SELECTORS.feed.articles); const firstArticle = articles.first(); // Click "Read TL;DR" button const readButton = firstArticle.locator(SELECTORS.feed.articleReadButton); await readButton.click(); // Modal should open const isOpen = await isSummaryModalOpen(); expect(isOpen).toBe(true); // Modal headline should match article headline const modal = page.locator(SELECTORS.summaryModal.root); const articleHeadline = await firstArticle.locator("h3").textContent(); const modalHeadline = await modal .locator(SELECTORS.summaryModal.headline) .textContent(); expect(modalHeadline).toBe(articleHeadline); }); test("closes summary modal via close button @smoke", async ({ page, waitForHero, isSummaryModalOpen, closeSummaryModal, }) => { // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); // Verify modal is open expect(await isSummaryModalOpen()).toBe(true); // Close modal await closeSummaryModal(); // Verify modal is closed expect(await isSummaryModalOpen()).toBe(false); }); test("closes summary modal via backdrop click", async ({ page, waitForHero, isSummaryModalOpen, }) => { // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); // Verify modal is open expect(await isSummaryModalOpen()).toBe(true); // Click backdrop (outside modal content) const backdrop = page.locator(".fixed.inset-0.bg-black\\/70").first(); await backdrop.click(); // Verify modal is closed await page.waitForTimeout(500); expect(await isSummaryModalOpen()).toBe(false); }); test("closes summary modal via escape key @smoke", async ({ page, waitForHero, isSummaryModalOpen, }) => { // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); // Verify modal is open expect(await isSummaryModalOpen()).toBe(true); // Press escape await page.keyboard.press("Escape"); // Verify modal is closed await page.waitForTimeout(500); expect(await isSummaryModalOpen()).toBe(false); }); test("modal displays correct content sections", async ({ page, waitForHero, }) => { // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); const modal = page.locator(SELECTORS.summaryModal.root); // Check all required sections are present await expect(modal.locator(SELECTORS.summaryModal.headline)).toBeVisible(); await expect(modal.locator(SELECTORS.summaryModal.image)).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.tldrSection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.summarySection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.sourceSection), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.shareSection), ).toBeVisible(); await expect(modal.locator(SELECTORS.summaryModal.poweredBy)).toBeVisible(); // Check TL;DR list has items const tldrList = modal.locator(SELECTORS.summaryModal.tldrList); const tldrItems = await tldrList.locator("li").count(); expect(tldrItems).toBeGreaterThanOrEqual(1); // Check summary body has content const summaryBody = modal.locator(SELECTORS.summaryModal.summaryBody); await expect(summaryBody).not.toBeEmpty(); // Check source link is present const sourceLink = modal.locator(SELECTORS.summaryModal.sourceLink); await expect(sourceLink).toBeVisible(); await expect(sourceLink).toHaveAttribute("target", "_blank"); }); test("modal share controls are present and accessible", async ({ page, waitForHero, }) => { // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); const modal = page.locator(SELECTORS.summaryModal.root); // Check all share buttons are present await expect(modal.locator(SELECTORS.summaryModal.shareX)).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.shareWhatsApp), ).toBeVisible(); await expect( modal.locator(SELECTORS.summaryModal.shareLinkedIn), ).toBeVisible(); await expect(modal.locator(SELECTORS.summaryModal.shareCopy)).toBeVisible(); // Check accessible labels await expect(modal.locator(SELECTORS.summaryModal.shareX)).toHaveAttribute( "aria-label", "Share on X", ); await expect( modal.locator(SELECTORS.summaryModal.shareWhatsApp), ).toHaveAttribute("aria-label", "Share on WhatsApp"); await expect( modal.locator(SELECTORS.summaryModal.shareLinkedIn), ).toHaveAttribute("aria-label", "Share on LinkedIn"); await expect( modal.locator(SELECTORS.summaryModal.shareCopy), ).toHaveAttribute("aria-label", "Copy article link"); }); test("modal returns to feed context after closing", async ({ page, waitForHero, waitForFeed, isSummaryModalOpen, }) => { // Wait for feed to be visible await waitForFeed(); // Open modal from hero const hero = await waitForHero(); await hero.locator(SELECTORS.hero.readButton).click(); // Close modal await page.keyboard.press("Escape"); await page.waitForTimeout(500); // Verify modal is closed expect(await isSummaryModalOpen()).toBe(false); // Verify we're still on the same page (no navigation occurred) await expect(page).toHaveURL(/\/$/); // Verify feed is still visible const feed = page.locator(SELECTORS.feed.root); await expect(feed).toBeVisible(); }); });