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,233 @@
import { SELECTORS } from "../../fixtures/selectors";
import { expect, test } from "../../fixtures/test";
import {
hasHorizontalOverflow,
isClipped,
VIEWPORT_SIZES,
type ViewportSize,
} from "../../fixtures/viewports";
test.describe("Responsive Breakpoint Tests @smoke", () => {
for (const viewport of VIEWPORT_SIZES) {
test.describe(`${viewport} viewport`, () => {
test.beforeEach(async ({ gotoApp, waitForAppReady, setViewport }) => {
await setViewport(viewport);
await gotoApp();
await waitForAppReady();
});
test("page has no horizontal overflow", async ({ page }) => {
// Check body for overflow
const bodyOverflow = await hasHorizontalOverflow(page, "body");
expect(bodyOverflow).toBe(false);
// Check main content
const mainOverflow = await hasHorizontalOverflow(page, "main");
expect(mainOverflow).toBe(false);
// Check hero section
const heroOverflow = await hasHorizontalOverflow(
page,
SELECTORS.hero.root,
);
expect(heroOverflow).toBe(false);
// Check feed section
const feedOverflow = await hasHorizontalOverflow(
page,
SELECTORS.feed.root,
);
expect(feedOverflow).toBe(false);
});
test("hero section is not clipped", async ({ page, waitForHero }) => {
const hero = await waitForHero();
const isHeroClipped = await isClipped(page, SELECTORS.hero.root);
expect(isHeroClipped).toBe(false);
});
test("feed articles are not clipped", async ({ page, waitForFeed }) => {
const feed = await waitForFeed();
const isFeedClipped = await isClipped(page, SELECTORS.feed.root);
expect(isFeedClipped).toBe(false);
});
test("modal fits within viewport", async ({
page,
waitForHero,
setViewport,
}) => {
// Open modal
const hero = await waitForHero();
await hero.locator(SELECTORS.hero.readButton).click();
const modal = page.locator(SELECTORS.summaryModal.root);
await expect(modal).toBeVisible();
// Get viewport dimensions
const viewport = await page.evaluate(() => ({
width: window.innerWidth,
height: window.innerHeight,
}));
// Get modal dimensions
const modalBox = await modal.boundingBox();
expect(modalBox).not.toBeNull();
// Modal should fit within viewport (with some padding)
expect(modalBox!.width).toBeLessThanOrEqual(viewport.width);
expect(modalBox!.height).toBeLessThanOrEqual(viewport.height * 0.96); // max-h-[96vh]
});
test("interactive controls are reachable", async ({
page,
waitForFeed,
}) => {
const feed = await waitForFeed();
const firstArticle = feed.locator(SELECTORS.feed.articles).first();
// Check read button is visible and clickable
const readButton = firstArticle.locator(
SELECTORS.feed.articleReadButton,
);
await expect(readButton).toBeVisible();
await expect(readButton).toBeEnabled();
// Check source link is visible if present
const sourceLink = firstArticle.locator(SELECTORS.feed.articleSource);
const hasSourceLink = (await sourceLink.count()) > 0;
if (hasSourceLink) {
await expect(sourceLink).toBeVisible();
}
});
test("header controls remain accessible", async ({ page }) => {
// Check logo is visible
const logo = page.locator(SELECTORS.header.logo);
await expect(logo).toBeVisible();
// Check theme button is visible
const themeButton = page.locator(SELECTORS.header.themeMenuButton);
await expect(themeButton).toBeVisible();
await expect(themeButton).toBeEnabled();
// Check language select is visible (may be hidden on very small screens)
const languageSelect = page.locator(SELECTORS.header.languageSelect);
const isVisible = await languageSelect.isVisible().catch(() => false);
if (isVisible) {
await expect(languageSelect).toBeEnabled();
}
});
});
}
});
test.describe("Responsive Layout Adaptations", () => {
test("mobile shows single column feed", async ({
gotoApp,
waitForAppReady,
setViewport,
waitForFeed,
}) => {
await setViewport("mobile");
await gotoApp();
await waitForAppReady();
const feed = await waitForFeed();
const articles = feed.locator(SELECTORS.feed.articles);
// Articles should be in single column (full width)
const firstArticle = articles.first();
const articleBox = await firstArticle.boundingBox();
// Get feed container width
const feedBox = await feed.boundingBox();
// Article should take most of the width (single column)
expect(articleBox!.width).toBeGreaterThan(feedBox!.width * 0.8);
});
test("tablet shows appropriate layout", async ({
gotoApp,
waitForAppReady,
setViewport,
waitForFeed,
}) => {
await setViewport("tablet");
await gotoApp();
await waitForAppReady();
const feed = await waitForFeed();
const articles = feed.locator(SELECTORS.feed.articles);
// Should have multiple articles visible
const count = await articles.count();
expect(count).toBeGreaterThanOrEqual(2);
// Articles should be side by side (multi-column)
const firstArticle = articles.first();
const secondArticle = articles.nth(1);
const firstBox = await firstArticle.boundingBox();
const secondBox = await secondArticle.boundingBox();
// Second article should be to the right of first (or below in some layouts)
expect(secondBox!.x).not.toBe(firstBox!.x);
});
test("desktop shows multi-column feed", async ({
gotoApp,
waitForAppReady,
setViewport,
waitForFeed,
}) => {
await setViewport("desktop");
await gotoApp();
await waitForAppReady();
const feed = await waitForFeed();
const articles = feed.locator(SELECTORS.feed.articles);
// Should have multiple articles in a row
const count = await articles.count();
expect(count).toBeGreaterThanOrEqual(3);
// First three articles should be in a row
const articleBoxes = await articles
.slice(0, 3)
.evaluateAll((els) => els.map((el) => el.getBoundingClientRect()));
// Articles should be at different x positions (side by side)
const xPositions = articleBoxes.map((box) => box.x);
const uniqueXPositions = [...new Set(xPositions)];
expect(uniqueXPositions.length).toBeGreaterThanOrEqual(2);
});
test("hero image maintains aspect ratio", async ({
gotoApp,
waitForAppReady,
setViewport,
waitForHero,
}) => {
for (const viewport of ["mobile", "tablet", "desktop"] as ViewportSize[]) {
await setViewport(viewport);
await gotoApp();
await waitForAppReady();
const hero = await waitForHero();
const image = hero.locator(SELECTORS.hero.image);
const box = await image.boundingBox();
expect(box).not.toBeNull();
// Image should have reasonable dimensions
expect(box!.width).toBeGreaterThan(0);
expect(box!.height).toBeGreaterThan(0);
// Aspect ratio should be roughly maintained (wider than tall)
expect(box!.width / box!.height).toBeGreaterThan(1);
expect(box!.width / box!.height).toBeLessThan(5);
}
});
});