p19-bug-fixes

This commit is contained in:
2026-02-13 16:57:45 -05:00
parent e2406bf978
commit f6beedd68f
75 changed files with 11989 additions and 48 deletions

View File

@@ -0,0 +1,191 @@
import { SELECTORS } from "../../fixtures/selectors";
import { expect, test } from "../../fixtures/test";
test.describe("Deep Link Permalink Tests @smoke", () => {
test.beforeEach(async ({ waitForAppReady }) => {
await waitForAppReady();
});
test("valid article permalink opens modal automatically", async ({
page,
gotoApp,
isSummaryModalOpen,
}) => {
// First get an article ID from the feed
await gotoApp();
await page.waitForSelector(SELECTORS.feed.articles, { timeout: 10000 });
const firstArticle = page.locator(SELECTORS.feed.articles).first();
const articleId = await firstArticle
.getAttribute("id")
.then((id) => (id ? parseInt(id.replace("news-", "")) : null));
expect(articleId).not.toBeNull();
// Navigate to article permalink
await gotoApp({ articleId: articleId! });
await page.waitForTimeout(2000); // Wait for modal to open
// Modal should be open
const isOpen = await isSummaryModalOpen();
expect(isOpen).toBe(true);
// Modal should show correct article
const modal = page.locator(SELECTORS.summaryModal.root);
const modalHeadline = await modal
.locator(SELECTORS.summaryModal.headline)
.textContent();
const articleHeadline = await firstArticle.locator("h3").textContent();
expect(modalHeadline).toBe(articleHeadline);
});
test("invalid article permalink shows error state", async ({
page,
gotoApp,
}) => {
// Navigate to invalid article ID
await gotoApp({ articleId: 999999 });
await page.waitForTimeout(2000);
// Should not show summary modal
const modal = page.locator(SELECTORS.summaryModal.root);
await expect(modal).not.toBeVisible();
// Should still show the page (not crash)
const hero = page.locator(SELECTORS.hero.root);
const feed = page.locator(SELECTORS.feed.root);
const heroVisible = await hero.isVisible().catch(() => false);
const feedVisible = await feed.isVisible().catch(() => false);
expect(heroVisible || feedVisible).toBe(true);
});
test("hero-origin modal flow via permalink", async ({
page,
gotoApp,
isSummaryModalOpen,
}) => {
// Get hero article ID
await gotoApp();
await page.waitForSelector(SELECTORS.hero.root, { timeout: 10000 });
const hero = page.locator(SELECTORS.hero.root);
const heroId = await hero
.getAttribute("id")
.then((id) => (id ? parseInt(id.replace("news-", "")) : null));
expect(heroId).not.toBeNull();
// Navigate directly to hero article
await gotoApp({ articleId: heroId! });
await page.waitForTimeout(2000);
// Modal should open
const isOpen = await isSummaryModalOpen();
expect(isOpen).toBe(true);
// Modal should show hero article content
const modal = page.locator(SELECTORS.summaryModal.root);
const modalHeadline = await modal
.locator(SELECTORS.summaryModal.headline)
.textContent();
const heroHeadline = await hero.locator("h1").textContent();
expect(modalHeadline).toBe(heroHeadline);
});
test("closing permalink modal updates URL", async ({
page,
gotoApp,
isSummaryModalOpen,
}) => {
// Open via permalink
await gotoApp({ articleId: 1 });
await page.waitForTimeout(2000);
// URL should have article parameter
await expect(page).toHaveURL(/\?article=\d+/);
// Close modal
await page.keyboard.press("Escape");
await page.waitForTimeout(500);
// URL should be cleaned up (parameter removed)
await expect(page).toHaveURL(/\/$/);
await expect(page).not.toHaveURL(/\?article=/);
});
test("modal state persists on page refresh", async ({
page,
gotoApp,
isSummaryModalOpen,
}) => {
// Open via permalink
await gotoApp({ articleId: 1 });
await page.waitForTimeout(2000);
// Verify modal is open
expect(await isSummaryModalOpen()).toBe(true);
// Refresh page
await page.reload();
await page.waitForTimeout(2000);
// Modal should still be open
expect(await isSummaryModalOpen()).toBe(true);
});
});
test.describe("Policy Modal Deep Links", () => {
test("terms policy modal opens via URL parameter", async ({
page,
gotoApp,
}) => {
await gotoApp({ policy: "terms" });
await page.waitForTimeout(1000);
// Policy modal should be visible
const modal = page.locator(SELECTORS.policyModal.root);
await expect(modal).toBeVisible();
// Should show terms title
const title = modal.locator(SELECTORS.policyModal.termsTitle);
await expect(title).toBeVisible();
});
test("attribution policy modal opens via URL parameter", async ({
page,
gotoApp,
}) => {
await gotoApp({ policy: "attribution" });
await page.waitForTimeout(1000);
// Policy modal should be visible
const modal = page.locator(SELECTORS.policyModal.root);
await expect(modal).toBeVisible();
// Should show attribution title
const title = modal.locator(SELECTORS.policyModal.attributionTitle);
await expect(title).toBeVisible();
});
test("closing policy modal clears URL parameter", async ({
page,
gotoApp,
}) => {
await gotoApp({ policy: "terms" });
await page.waitForTimeout(1000);
// URL should have policy parameter
await expect(page).toHaveURL(/\?policy=terms/);
// Close modal
const modal = page.locator(SELECTORS.policyModal.root);
await modal.locator(SELECTORS.policyModal.closeButton).click();
await page.waitForTimeout(500);
// URL should be cleaned up
await expect(page).toHaveURL(/\/$/);
await expect(page).not.toHaveURL(/\?policy=/);
});
});

View File

@@ -0,0 +1,193 @@
import { SELECTORS } from "../../fixtures/selectors";
import { expect, test } from "../../fixtures/test";
test.describe("Source CTA and Share Interactions", () => {
test.beforeEach(async ({ gotoApp, waitForAppReady, waitForHero }) => {
await gotoApp();
await waitForAppReady();
// Open modal from hero
const hero = await waitForHero();
await hero.locator(SELECTORS.hero.readButton).click();
await expect(page.locator(SELECTORS.summaryModal.root)).toBeVisible();
});
let page: any;
test("source link opens in new tab @smoke", async ({ page: p, context }) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const sourceLink = modal.locator(SELECTORS.summaryModal.sourceLink);
// Check link attributes
await expect(sourceLink).toHaveAttribute("target", "_blank");
await expect(sourceLink).toHaveAttribute("rel", "noopener");
await expect(sourceLink).toHaveAttribute("href");
// Click should open new tab
const [newPage] = await Promise.all([
context.waitForEvent("page"),
sourceLink.click(),
]);
// New page should have loaded
expect(newPage).toBeDefined();
await newPage.close();
// Modal should remain open
await expect(modal).toBeVisible();
});
test("share on X opens correct URL", async ({ page: p, context }) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const shareX = modal.locator(SELECTORS.summaryModal.shareX);
// Get article headline for URL verification
const headline = await modal
.locator(SELECTORS.summaryModal.headline)
.textContent();
// Click should open X share in new tab
const [newPage] = await Promise.all([
context.waitForEvent("page"),
shareX.click(),
]);
// Verify URL contains X intent
const url = newPage.url();
expect(url).toContain("x.com/intent/tweet");
expect(url).toContain(encodeURIComponent(headline || ""));
await newPage.close();
});
test("share on WhatsApp opens correct URL", async ({ page: p, context }) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const shareWhatsApp = modal.locator(SELECTORS.summaryModal.shareWhatsApp);
// Get article headline for URL verification
const headline = await modal
.locator(SELECTORS.summaryModal.headline)
.textContent();
// Click should open WhatsApp share in new tab
const [newPage] = await Promise.all([
context.waitForEvent("page"),
shareWhatsApp.click(),
]);
// Verify URL contains WhatsApp share
const url = newPage.url();
expect(url).toContain("wa.me");
expect(url).toContain(encodeURIComponent(headline || ""));
await newPage.close();
});
test("share on LinkedIn opens correct URL", async ({ page: p, context }) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const shareLinkedIn = modal.locator(SELECTORS.summaryModal.shareLinkedIn);
// Click should open LinkedIn share in new tab
const [newPage] = await Promise.all([
context.waitForEvent("page"),
shareLinkedIn.click(),
]);
// Verify URL contains LinkedIn share
const url = newPage.url();
expect(url).toContain("linkedin.com/sharing");
await newPage.close();
});
test("copy link button copies permalink to clipboard @smoke", async ({
page: p,
context,
}) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const copyButton = modal.locator(SELECTORS.summaryModal.shareCopy);
// Grant clipboard permissions
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
// Click copy button
await copyButton.click();
// Wait for success message
const successMessage = modal.locator(SELECTORS.summaryModal.copySuccess);
await expect(successMessage).toBeVisible();
await expect(successMessage).toContainText("Permalink copied");
// Verify clipboard content
const clipboardContent = await page.evaluate(() =>
navigator.clipboard.readText(),
);
expect(clipboardContent).toContain("/?article=");
expect(clipboardContent).toMatch(/http/);
});
test("copy link does not navigate away", async ({ page: p }) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
const copyButton = modal.locator(SELECTORS.summaryModal.shareCopy);
// Get current URL
const currentUrl = page.url();
// Click copy button
await copyButton.click();
// Wait a moment
await page.waitForTimeout(1000);
// URL should not have changed
await expect(page).toHaveURL(currentUrl);
// Modal should still be open
await expect(modal).toBeVisible();
});
test("navigation is preserved after share interactions", async ({
page: p,
context,
}) => {
page = p;
const modal = page.locator(SELECTORS.summaryModal.root);
// Interact with multiple share buttons
const shareButtons = [
modal.locator(SELECTORS.summaryModal.shareX),
modal.locator(SELECTORS.summaryModal.shareWhatsApp),
modal.locator(SELECTORS.summaryModal.shareLinkedIn),
];
for (const button of shareButtons) {
if (await button.isVisible()) {
const [newPage] = await Promise.all([
context.waitForEvent("page"),
button.click(),
]);
await newPage.close();
// Modal should remain open after each interaction
await expect(modal).toBeVisible();
}
}
// Close modal
await page.keyboard.press("Escape");
await page.waitForTimeout(500);
// Should be back on main page without navigation
await expect(page).toHaveURL(/\/$/);
// Feed should be visible
const feed = page.locator(SELECTORS.feed.root);
await expect(feed).toBeVisible();
});
});