Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions tests/e2e/blog.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { test, expect } from "@playwright/test";

test.describe("Blog", () => {
test.describe("Blog Listing Page", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/blog/");
});

test("should display page title", async ({ page }) => {
const heading = page.locator("h1");
await expect(heading).toHaveText("Posts");
});

test("should display blog posts as tiles", async ({ page }) => {
const tiles = page.locator("pob-tile");
await expect(tiles.first()).toBeVisible();
});

test("should display post titles", async ({ page }) => {
const postTitle = page.locator("pob-tile strong");
await expect(postTitle.first()).toBeVisible();
});

test("should display post dates", async ({ page }) => {
const postDate = page.locator("pob-tile time");
await expect(postDate.first()).toBeVisible();
});

test("should have links to individual posts", async ({ page }) => {
const firstTile = page.locator("pob-tile").first();
const href = await firstTile.getAttribute("href");
expect(href).toBeTruthy();
expect(href).toContain("/blog/");
});

test("should display tag filters when posts have tags", async ({ page }) => {
const tagFilters = page.locator(".tag-filters");
// Tags may or may not be present depending on posts
const count = await tagFilters.count();
if (count > 0) {
await expect(tagFilters).toBeVisible();
const tagLinks = page.locator(".tags-list a.tag");
await expect(tagLinks.first()).toBeVisible();
}
});
});

test.describe("Individual Blog Post", () => {
test.beforeEach(async ({ page }) => {
// Navigate to the Hello World post
await page.goto("/blog/2024/07/hello-world/");
});

test("should display post title", async ({ page }) => {
const heading = page.locator("h1");
await expect(heading).toHaveText("Hello, World!");
});

test("should display post date", async ({ page }) => {
// Date is rendered as <time> element directly after h1
const dateElement = page.locator("article time").first();
await expect(dateElement).toBeVisible();
});

test("should display post content", async ({ page }) => {
const content = page.locator("article");
await expect(content).toBeVisible();
// Check for some expected content
await expect(content).toContainText("Eleventy");
});

test("should display tags", async ({ page }) => {
// Tags are in .tags-list container
const tags = page.locator("article .tags-list .tag");
await expect(tags.first()).toBeVisible();
await expect(tags.first()).toHaveText("meta");
});

test("should generate table of contents for posts with headings", async ({ page }) => {
// The TOC is in nav#toc which is slotted into the sidebar
// The ol is generated by the table-of-contents plugin
const toc = page.locator("nav#toc");
await expect(toc).toBeVisible();

// Should have links to headings
const tocLinks = toc.locator("ol a");
await expect(tocLinks.first()).toBeVisible();
});

test("should have heading IDs for TOC navigation", async ({ page }) => {
// Headings get IDs generated from their text
const heading = page.locator("article h2").first();
await expect(heading).toBeVisible();
const id = await heading.getAttribute("id");
expect(id).toBeTruthy();
});

test("should render pob-note components", async ({ page }) => {
const note = page.locator("pob-note");
await expect(note).toBeVisible();
});
});

test.describe("Blog Post Navigation", () => {
test.use({ viewport: { width: 1280, height: 720 } });

test("should navigate from listing to post", async ({ page }) => {
await page.goto("/blog/");

const firstTile = page.locator("pob-tile").first();
await firstTile.click();

// Should be on a blog post page
await expect(page.locator("article")).toBeVisible();
await expect(page.locator("h1")).toBeVisible();
});

test("should have link back to blog from post", async ({ page }) => {
await page.goto("/blog/2024/07/hello-world/");

// Navigation is inside shadow DOM of pob-app
// Use shadow DOM piercing selector to find the Blog nav link
const blogLink = page.locator("pob-app >> .top-nav >> text=Blog");
await expect(blogLink).toBeVisible();
});
});

test.describe("Syntax Highlighting", () => {
test("should render syntax-highlighted code blocks", async ({ page }) => {
// Navigate to a post that might have code blocks
// For now, we'll just check the component exists if present
await page.goto("/blog/2024/07/hello-world/");

// This post may not have code blocks, so we just verify
// the page loads correctly even without them
await expect(page.locator("h1")).toBeVisible();
});
});
});
157 changes: 157 additions & 0 deletions tests/e2e/dark-mode.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { test, expect } from "@playwright/test";

test.describe("Dark Mode", () => {
test.describe("Light Mode (default)", () => {
test.use({ colorScheme: "light" });

test("should use light background color", async ({ page }) => {
await page.goto("/");

const body = page.locator("body");
const backgroundColor = await body.evaluate((el) => {
return getComputedStyle(el).backgroundColor;
});

// Light mode should have a light background (high RGB values)
// Parse the RGB values
const match = backgroundColor.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
const [, r, g, b] = match.map(Number);
// Light background should have high values (close to white)
expect(r).toBeGreaterThan(200);
expect(g).toBeGreaterThan(200);
expect(b).toBeGreaterThan(200);
}
});

test("should use dark text color", async ({ page }) => {
await page.goto("/");

const body = page.locator("body");
const color = await body.evaluate((el) => {
return getComputedStyle(el).color;
});

// Dark text should have low RGB values
const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
const [, r, g, b] = match.map(Number);
// Dark text should have low values
expect(r).toBeLessThan(100);
expect(g).toBeLessThan(100);
expect(b).toBeLessThan(100);
}
});
});

test.describe("Dark Mode (prefers-color-scheme: dark)", () => {
test.use({ colorScheme: "dark" });

test("should use dark background color", async ({ page }) => {
await page.goto("/");

const body = page.locator("body");
const backgroundColor = await body.evaluate((el) => {
return getComputedStyle(el).backgroundColor;
});

// Dark mode should have a dark background (low RGB values)
const match = backgroundColor.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
const [, r, g, b] = match.map(Number);
// Dark background should have low values
expect(r).toBeLessThan(100);
expect(g).toBeLessThan(100);
expect(b).toBeLessThan(100);
}
});

test("should use light text color", async ({ page }) => {
await page.goto("/");

const body = page.locator("body");
const color = await body.evaluate((el) => {
return getComputedStyle(el).color;
});

// Light text should have high RGB values
const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (match) {
const [, r, g, b] = match.map(Number);
// Light text should have high values
expect(r).toBeGreaterThan(150);
expect(g).toBeGreaterThan(150);
expect(b).toBeGreaterThan(150);
}
});

test("should adapt tiles to dark mode", async ({ page }) => {
await page.goto("/blog/");

const tile = page.locator("pob-tile").first();
await expect(tile).toBeVisible();

// Verify tile is visible in dark mode (basic check)
const isVisible = await tile.isVisible();
expect(isVisible).toBe(true);
});

test("should adapt notes to dark mode", async ({ page }) => {
await page.goto("/blog/2024/07/hello-world/");

const note = page.locator("pob-note");
await expect(note).toBeVisible();

// Verify note is visible in dark mode
const isVisible = await note.isVisible();
expect(isVisible).toBe(true);
});

test("should maintain readable contrast on blog posts", async ({ page }) => {
await page.goto("/blog/2024/07/hello-world/");

const article = page.locator("article");
await expect(article).toBeVisible();

// Basic visibility check - content should be readable
const heading = page.locator("h1");
await expect(heading).toBeVisible();
});
});

test.describe("Color Scheme Consistency - Light Mode", () => {
test.use({ colorScheme: "light", viewport: { width: 1280, height: 720 } });

test("header should be visible in light mode", async ({ page }) => {
// Use blog page which doesn't have the hidden home-header
await page.goto("/blog/");
// Header with navigation is in shadow DOM
const header = page.locator("pob-app >> header:has(.top-nav)");
await expect(header).toBeVisible();
});

test("footer should be visible in light mode", async ({ page }) => {
await page.goto("/blog/");
const footer = page.locator("pob-app >> footer:has(.copyright)");
await expect(footer).toBeVisible();
});
});

test.describe("Color Scheme Consistency - Dark Mode", () => {
test.use({ colorScheme: "dark", viewport: { width: 1280, height: 720 } });

test("header should be visible in dark mode", async ({ page }) => {
// Use blog page which doesn't have the hidden home-header
await page.goto("/blog/");
// Header with navigation is in shadow DOM
const header = page.locator("pob-app >> header:has(.top-nav)");
await expect(header).toBeVisible();
});

test("footer should be visible in dark mode", async ({ page }) => {
await page.goto("/blog/");
const footer = page.locator("pob-app >> footer:has(.copyright)");
await expect(footer).toBeVisible();
});
});
});
Loading