251 lines
6.8 KiB
TypeScript
251 lines
6.8 KiB
TypeScript
import { SELECTORS } from "../../fixtures/selectors";
|
|
import { expect, test } from "../../fixtures/test";
|
|
import { getStickyPosition, ViewportSize } from "../../fixtures/viewports";
|
|
|
|
test.describe("Sticky Header Behavior @smoke", () => {
|
|
test.beforeEach(async ({ gotoApp, waitForAppReady }) => {
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
});
|
|
|
|
test("header is sticky on scroll", async ({ page }) => {
|
|
const header = page.locator(SELECTORS.header.root);
|
|
|
|
// Check initial position
|
|
const initialPosition = await getStickyPosition(
|
|
page,
|
|
SELECTORS.header.root,
|
|
);
|
|
expect(initialPosition.isSticky).toBe(true);
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 500));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Header should still be at top
|
|
const scrolledPosition = await getStickyPosition(
|
|
page,
|
|
SELECTORS.header.root,
|
|
);
|
|
expect(scrolledPosition.top).toBeLessThanOrEqual(10); // Allow small offset
|
|
expect(scrolledPosition.isSticky).toBe(true);
|
|
});
|
|
|
|
test("header shrinks on scroll", async ({ page }) => {
|
|
const header = page.locator(SELECTORS.header.root);
|
|
const headerContainer = header.locator("> div");
|
|
|
|
// Get initial height
|
|
const initialHeight = await headerContainer.evaluate(
|
|
(el) => el.offsetHeight,
|
|
);
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 300));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Get scrolled height
|
|
const scrolledHeight = await headerContainer.evaluate(
|
|
(el) => el.offsetHeight,
|
|
);
|
|
|
|
// Header should shrink (or stay same, but not grow)
|
|
expect(scrolledHeight).toBeLessThanOrEqual(initialHeight);
|
|
});
|
|
|
|
test("header maintains glass effect on scroll", async ({ page }) => {
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 500));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check header has backdrop blur
|
|
const hasBlur = await page.evaluate(() => {
|
|
const header = document.querySelector("header");
|
|
if (!header) return false;
|
|
const style = window.getComputedStyle(header);
|
|
return style.backdropFilter.includes("blur");
|
|
});
|
|
|
|
expect(hasBlur).toBe(true);
|
|
});
|
|
});
|
|
|
|
test.describe("Sticky Footer Behavior", () => {
|
|
test.beforeEach(async ({ gotoApp, waitForAppReady }) => {
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
});
|
|
|
|
test("footer is sticky at bottom", async ({ page }) => {
|
|
const footer = page.locator(SELECTORS.footer.root);
|
|
|
|
// Check footer is visible
|
|
await expect(footer).toBeVisible();
|
|
|
|
// Check footer position
|
|
const footerBox = await footer.boundingBox();
|
|
const viewportHeight = await page.evaluate(() => window.innerHeight);
|
|
|
|
// Footer should be at bottom of viewport
|
|
expect(footerBox!.y + footerBox!.height).toBeGreaterThanOrEqual(
|
|
viewportHeight - 10,
|
|
);
|
|
});
|
|
|
|
test("footer does not overlap main content", async ({ page }) => {
|
|
const footer = page.locator(SELECTORS.footer.root);
|
|
const mainContent = page.locator("main");
|
|
|
|
// Get bounding boxes
|
|
const footerBox = await footer.boundingBox();
|
|
const mainBox = await mainContent.boundingBox();
|
|
|
|
// Main content should have padding at bottom to account for footer
|
|
const bodyPadding = await page.evaluate(() => {
|
|
const body = document.body;
|
|
const style = window.getComputedStyle(body);
|
|
return parseInt(style.paddingBottom || "0");
|
|
});
|
|
|
|
expect(bodyPadding).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Back to Top Behavior @smoke", () => {
|
|
test.beforeEach(async ({ gotoApp, waitForAppReady }) => {
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
});
|
|
|
|
test("back to top is hidden initially", async ({ page }) => {
|
|
const backToTop = page.locator(SELECTORS.backToTop.root);
|
|
|
|
// Should not be visible at top of page
|
|
const isVisible = await backToTop.isVisible().catch(() => false);
|
|
expect(isVisible).toBe(false);
|
|
});
|
|
|
|
test("back to top appears on scroll", async ({ page }) => {
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 800));
|
|
await page.waitForTimeout(500);
|
|
|
|
const backToTop = page.locator(SELECTORS.backToTop.root);
|
|
|
|
// Should be visible after scroll
|
|
await expect(backToTop).toBeVisible();
|
|
});
|
|
|
|
test("back to top scrolls to top when clicked", async ({ page }) => {
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 1000));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Click back to top
|
|
const backToTop = page.locator(SELECTORS.backToTop.root);
|
|
await backToTop.click();
|
|
|
|
// Wait for scroll animation
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Should be at top
|
|
const scrollPosition = await page.evaluate(() => window.scrollY);
|
|
expect(scrollPosition).toBeLessThanOrEqual(50);
|
|
});
|
|
|
|
test("back to top is accessible", async ({ page }) => {
|
|
// Scroll down to make visible
|
|
await page.evaluate(() => window.scrollTo(0, 800));
|
|
await page.waitForTimeout(500);
|
|
|
|
const backToTop = page.locator(SELECTORS.backToTop.root);
|
|
|
|
// Should have aria-label
|
|
await expect(backToTop).toHaveAttribute("aria-label");
|
|
|
|
// Should be keyboard focusable
|
|
await backToTop.focus();
|
|
await expect(backToTop).toBeFocused();
|
|
});
|
|
});
|
|
|
|
test.describe("Sticky Behavior Across Breakpoints", () => {
|
|
test("header and footer work on mobile", async ({
|
|
gotoApp,
|
|
waitForAppReady,
|
|
setViewport,
|
|
page,
|
|
}) => {
|
|
await setViewport("mobile");
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
|
|
// Check header is sticky
|
|
const header = page.locator(SELECTORS.header.root);
|
|
await expect(header).toBeVisible();
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 500));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Header should still be visible
|
|
await expect(header).toBeVisible();
|
|
|
|
// Footer should be visible
|
|
const footer = page.locator(SELECTORS.footer.root);
|
|
await expect(footer).toBeVisible();
|
|
});
|
|
|
|
test("header and footer work on tablet", async ({
|
|
gotoApp,
|
|
waitForAppReady,
|
|
setViewport,
|
|
page,
|
|
}) => {
|
|
await setViewport("tablet");
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
|
|
// Check header is sticky
|
|
const header = page.locator(SELECTORS.header.root);
|
|
await expect(header).toBeVisible();
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 500));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Header should still be visible
|
|
await expect(header).toBeVisible();
|
|
|
|
// Footer should be visible
|
|
const footer = page.locator(SELECTORS.footer.root);
|
|
await expect(footer).toBeVisible();
|
|
});
|
|
|
|
test("header and footer work on desktop", async ({
|
|
gotoApp,
|
|
waitForAppReady,
|
|
setViewport,
|
|
page,
|
|
}) => {
|
|
await setViewport("desktop");
|
|
await gotoApp();
|
|
await waitForAppReady();
|
|
|
|
// Check header is sticky
|
|
const header = page.locator(SELECTORS.header.root);
|
|
await expect(header).toBeVisible();
|
|
|
|
// Scroll down
|
|
await page.evaluate(() => window.scrollTo(0, 500));
|
|
await page.waitForTimeout(500);
|
|
|
|
// Header should still be visible
|
|
await expect(header).toBeVisible();
|
|
|
|
// Footer should be visible
|
|
const footer = page.locator(SELECTORS.footer.root);
|
|
await expect(footer).toBeVisible();
|
|
});
|
|
});
|