All checks were successful
Deploy / deploy (push) Successful in 1m41s
New E2E test files: - e2e/health.spec.ts: 3 tests for health/observability endpoints - e2e/history.spec.ts: 7 tests for history page - e2e/plan.spec.ts: 7 tests for exercise plan page - e2e/decision-engine.spec.ts: 8 tests for decision display and overrides - e2e/cycle.spec.ts: 11 tests for cycle tracking, settings, and period logging Total E2E tests: 100 (up from 64) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
166 lines
5.5 KiB
TypeScript
166 lines
5.5 KiB
TypeScript
// ABOUTME: E2E tests for the exercise plan reference page.
|
|
// ABOUTME: Tests phase display, training guidelines, and current status.
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
test.describe("plan page", () => {
|
|
test.describe("unauthenticated", () => {
|
|
test("redirects to login when not authenticated", async ({ page }) => {
|
|
await page.goto("/plan");
|
|
|
|
// Should redirect to login
|
|
await expect(page).toHaveURL(/\/login/);
|
|
});
|
|
});
|
|
|
|
test.describe("authenticated", () => {
|
|
// These tests require TEST_USER_EMAIL and TEST_USER_PASSWORD env vars
|
|
test.beforeEach(async ({ page }) => {
|
|
const email = process.env.TEST_USER_EMAIL;
|
|
const password = process.env.TEST_USER_PASSWORD;
|
|
|
|
if (!email || !password) {
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
// Login via the login page
|
|
await page.goto("/login");
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
const emailInput = page.getByLabel(/email/i);
|
|
const hasEmailForm = await emailInput.isVisible().catch(() => false);
|
|
|
|
if (!hasEmailForm) {
|
|
test.skip();
|
|
return;
|
|
}
|
|
|
|
await emailInput.fill(email);
|
|
await page.getByLabel(/password/i).fill(password);
|
|
await page.getByRole("button", { name: /sign in/i }).click();
|
|
|
|
// Wait for redirect to dashboard then navigate to plan
|
|
await page.waitForURL("/", { timeout: 10000 });
|
|
await page.goto("/plan");
|
|
});
|
|
|
|
test("displays exercise plan page with title", async ({ page }) => {
|
|
// Check for plan page title
|
|
const heading = page.getByRole("heading", { name: "Exercise Plan" });
|
|
await expect(heading).toBeVisible();
|
|
});
|
|
|
|
test("shows current cycle status section", async ({ page }) => {
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Look for Current Status section
|
|
const statusSection = page.getByRole("heading", {
|
|
name: "Current Status",
|
|
});
|
|
const hasStatus = await statusSection.isVisible().catch(() => false);
|
|
|
|
if (hasStatus) {
|
|
await expect(statusSection).toBeVisible();
|
|
|
|
// Should show day number
|
|
await expect(page.getByText(/day \d+/i)).toBeVisible();
|
|
|
|
// Should show training type
|
|
await expect(page.getByText(/training type:/i)).toBeVisible();
|
|
|
|
// Should show weekly limit
|
|
await expect(page.getByText(/weekly limit:/i)).toBeVisible();
|
|
} else {
|
|
// If no status, should see loading or error state
|
|
const loading = page.getByText(/loading/i);
|
|
const error = page.getByRole("alert");
|
|
const hasLoading = await loading.isVisible().catch(() => false);
|
|
const hasError = await error.isVisible().catch(() => false);
|
|
|
|
expect(hasLoading || hasError).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("shows all 5 phase cards", async ({ page }) => {
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Check for Phase Overview section
|
|
const phaseOverview = page.getByRole("heading", {
|
|
name: "Phase Overview",
|
|
});
|
|
const hasPhaseOverview = await phaseOverview
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (hasPhaseOverview) {
|
|
// Should show all 5 phase cards using data-testid
|
|
await expect(page.getByTestId("phase-MENSTRUAL")).toBeVisible();
|
|
await expect(page.getByTestId("phase-FOLLICULAR")).toBeVisible();
|
|
await expect(page.getByTestId("phase-OVULATION")).toBeVisible();
|
|
await expect(page.getByTestId("phase-EARLY_LUTEAL")).toBeVisible();
|
|
await expect(page.getByTestId("phase-LATE_LUTEAL")).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test("shows strength training reference table", async ({ page }) => {
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Check for Strength Training section
|
|
const strengthSection = page.getByRole("heading", {
|
|
name: /strength training/i,
|
|
});
|
|
const hasStrength = await strengthSection.isVisible().catch(() => false);
|
|
|
|
if (hasStrength) {
|
|
// Should have exercise table
|
|
const table = page.locator("table");
|
|
await expect(table).toBeVisible();
|
|
|
|
// Check for some exercises
|
|
await expect(page.getByText("Squats")).toBeVisible();
|
|
await expect(page.getByText("Push-ups")).toBeVisible();
|
|
await expect(page.getByText("Plank")).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test("shows rebounding techniques", async ({ page }) => {
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Check for Rebounding Techniques section
|
|
const reboundingSection = page.getByRole("heading", {
|
|
name: /rebounding techniques/i,
|
|
});
|
|
const hasRebounding = await reboundingSection
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (hasRebounding) {
|
|
// Should show techniques section - use first() for specific match
|
|
await expect(
|
|
page.getByText("Health bounce, lymphatic drainage"),
|
|
).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test("shows weekly guidelines", async ({ page }) => {
|
|
await page.waitForLoadState("networkidle");
|
|
|
|
// Check for Weekly Guidelines section
|
|
const weeklySection = page.getByRole("heading", {
|
|
name: "Weekly Guidelines",
|
|
});
|
|
const hasWeekly = await weeklySection.isVisible().catch(() => false);
|
|
|
|
if (hasWeekly) {
|
|
// Should show guidelines for each phase - use exact matches
|
|
await expect(
|
|
page.getByRole("heading", { name: "Menstrual Phase (Days 1-3)" }),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("heading", { name: "Follicular Phase (Days 4-14)" }),
|
|
).toBeVisible();
|
|
}
|
|
});
|
|
});
|
|
});
|