Enable 5 previously skipped e2e tests
All checks were successful
Deploy / deploy (push) Successful in 1m37s

- Fix OIDC tests with route interception for auth-methods API
- Add data-testid to DecisionCard for reliable test selection
- Fix /api/today to fetch fresh user data instead of stale cookie data
- Fix period logging test timing with proper API wait patterns
- Fix decision engine test with waitForResponse instead of timeout
- Simplify mobile viewport test locator

All 206 e2e tests now pass with 0 skipped.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-15 06:30:51 +00:00
parent ff3d8fad2c
commit 4a874476c3
7 changed files with 103 additions and 68 deletions

View File

@@ -228,26 +228,42 @@ test.describe("authentication", () => {
});
test.describe("OIDC authentication flow", () => {
// Mock PocketBase auth-methods to return OIDC provider
test.beforeEach(async ({ page }) => {
await page.route("**/api/collections/users/auth-methods*", (route) => {
route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
usernamePassword: true,
oauth2: {
enabled: true,
providers: [
{
name: "oidc",
displayName: "Test Provider",
state: "mock-state",
codeVerifier: "mock-verifier",
codeChallenge: "mock-challenge",
codeChallengeMethod: "S256",
authURL: "https://mock.example.com/auth",
},
],
},
}),
});
});
});
test("OIDC button shows provider name when configured", async ({
page,
}) => {
await page.goto("/login");
await page.waitForLoadState("networkidle");
// Look for OIDC sign-in button with provider name
const oidcButton = page.getByRole("button", { name: /sign in with/i });
const hasOidc = await oidcButton.isVisible().catch(() => false);
if (hasOidc) {
// OIDC button should show provider display name
await expect(oidcButton).toBeVisible();
// Button text should include "Sign in with" prefix
const buttonText = await oidcButton.textContent();
expect(buttonText?.toLowerCase()).toContain("sign in with");
} else {
// Skip test if OIDC not configured (email/password mode)
test.skip();
}
await expect(oidcButton).toBeVisible();
await expect(oidcButton).toContainText("Test Provider");
});
test("OIDC button shows loading state during authentication", async ({
@@ -256,22 +272,18 @@ test.describe("authentication", () => {
await page.goto("/login");
await page.waitForLoadState("networkidle");
// Find button by initial text
const oidcButton = page.getByRole("button", { name: /sign in with/i });
const hasOidc = await oidcButton.isVisible().catch(() => false);
await expect(oidcButton).toBeVisible();
if (hasOidc) {
// Click the button
await oidcButton.click();
// Click and immediately check for loading state
// The button text changes to "Signing in..." so we need a different locator
await oidcButton.click();
// Button should show "Signing in..." state
await expect(oidcButton)
.toContainText(/signing in/i, { timeout: 2000 })
.catch(() => {
// May redirect too fast to catch loading state - that's acceptable
});
} else {
test.skip();
}
// Find the button that shows loading state (text changed)
const loadingButton = page.getByRole("button", { name: /signing in/i });
await expect(loadingButton).toBeVisible();
await expect(loadingButton).toBeDisabled();
});
test("OIDC button is disabled when rate limited", async ({ page }) => {
@@ -279,15 +291,9 @@ test.describe("authentication", () => {
await page.waitForLoadState("networkidle");
const oidcButton = page.getByRole("button", { name: /sign in with/i });
const hasOidc = await oidcButton.isVisible().catch(() => false);
if (hasOidc) {
// Initial state should not be disabled
const isDisabledBefore = await oidcButton.isDisabled();
expect(isDisabledBefore).toBe(false);
} else {
test.skip();
}
// Initial state should not be disabled
await expect(oidcButton).not.toBeDisabled();
});
});

View File

@@ -355,16 +355,23 @@ test.describe("decision engine", () => {
// Toggle flare on (if not already)
if (!flareWasChecked) {
await flareCheckbox.click();
await page.waitForTimeout(500);
// Wait for both API calls when clicking the checkbox
await Promise.all([
page.waitForResponse("**/api/overrides"),
page.waitForResponse("**/api/today"),
flareCheckbox.click(),
]);
// Should now be REST
// Should now be REST (flare mode forces rest)
const restDecision = await decisionCard.textContent();
expect(restDecision).toContain("REST");
// Toggle flare off
await flareCheckbox.click();
await page.waitForTimeout(500);
// Toggle flare off and wait for API calls
await Promise.all([
page.waitForResponse("**/api/overrides"),
page.waitForResponse("**/api/today"),
flareCheckbox.click(),
]);
// Should return to original (or close to it)
const restoredDecision = await decisionCard.textContent();

View File

@@ -77,9 +77,7 @@ test.describe("mobile viewport", () => {
await expect(settingsLink).toBeVisible();
// Decision card should be visible
const decisionCard = page
.locator('[data-testid="decision-card"]')
.or(page.getByText(/rest|gentle|light|reduced|train/i).first());
const decisionCard = page.locator('[data-testid="decision-card"]');
await expect(decisionCard).toBeVisible();
// Data panel should be visible

View File

@@ -192,9 +192,7 @@ test.describe("period logging flow - onboarding user", () => {
await onboardingPage.getByRole("button", { name: /cancel/i }).click();
});
// TODO: This test is flaky - the save succeeds but the dashboard doesn't
// always refresh in time. Needs investigation into React state updates.
test.skip("logging period from modal updates dashboard cycle info", async ({
test("logging period from modal updates dashboard cycle info", async ({
onboardingPage,
}) => {
await onboardingPage.goto("/");
@@ -220,19 +218,20 @@ test.describe("period logging flow - onboarding user", () => {
const dateInput = onboardingPage.locator('input[type="date"]');
await dateInput.fill(dateStr);
// Click Save
// Click Save button
await onboardingPage.getByRole("button", { name: /save/i }).click();
// Modal should close
await expect(modalTitle).not.toBeVisible();
// Modal should close after successful save
await expect(modalTitle).not.toBeVisible({ timeout: 10000 });
// Wait for data to refresh after successful save
// The dashboard refetches data and should show cycle info
// Wait for network activity to settle
await onboardingPage.waitForLoadState("networkidle");
// Look for cycle day display (e.g., "Day 8 · Follicular" or similar)
// This appears after the dashboard refetches data post-save
const cycleInfo = onboardingPage.getByText(/day\s+\d+\s+·/i);
// The page fetches /api/cycle/period, then /api/today and /api/user
// Content only renders when both todayData and userData are available
// Use .first() as the pattern may match multiple elements on the page
const cycleInfo = onboardingPage.getByText(/day\s+\d+\s+·/i).first();
await expect(cycleInfo).toBeVisible({ timeout: 15000 });
});
});