Add 5 new E2E tests for period logging modal and edit/delete flows
All checks were successful
Deploy / deploy (push) Successful in 2m38s
All checks were successful
Deploy / deploy (push) Successful in 2m38s
New tests cover: - Period date modal opens from dashboard onboarding banner - Period date input restricts future dates via max attribute - Logging period from modal updates dashboard cycle info - Edit period modal flow changes date successfully - Delete period confirmation flow removes entry Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -285,5 +285,264 @@ test.describe("period logging", () => {
|
||||
await expect(editButton.first()).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test("period date modal opens from dashboard onboarding banner", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Look for the "Set date" button in onboarding banner
|
||||
const setDateButton = page.getByRole("button", { name: /set date/i });
|
||||
const hasSetDate = await setDateButton.isVisible().catch(() => false);
|
||||
|
||||
if (!hasSetDate) {
|
||||
// User may already have period date set - skip if no onboarding banner
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click the set date button
|
||||
await setDateButton.click();
|
||||
|
||||
// Modal should open with "Set Period Date" title
|
||||
const modalTitle = page.getByRole("heading", {
|
||||
name: /set period date/i,
|
||||
});
|
||||
await expect(modalTitle).toBeVisible();
|
||||
|
||||
// Should have a date input
|
||||
const dateInput = page.locator('input[type="date"]');
|
||||
await expect(dateInput).toBeVisible();
|
||||
|
||||
// Should have Cancel and Save buttons
|
||||
await expect(page.getByRole("button", { name: /cancel/i })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /save/i })).toBeVisible();
|
||||
|
||||
// Cancel should close the modal
|
||||
await page.getByRole("button", { name: /cancel/i }).click();
|
||||
await expect(modalTitle).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("period date input restricts future dates", async ({ page }) => {
|
||||
// Look for the "Set date" button in onboarding banner
|
||||
const setDateButton = page.getByRole("button", { name: /set date/i });
|
||||
const hasSetDate = await setDateButton.isVisible().catch(() => false);
|
||||
|
||||
if (!hasSetDate) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the modal
|
||||
await setDateButton.click();
|
||||
|
||||
// Get the date input and check its max attribute
|
||||
const dateInput = page.locator('input[type="date"]');
|
||||
await expect(dateInput).toBeVisible();
|
||||
|
||||
// The max attribute should be set to today's date (YYYY-MM-DD format)
|
||||
const maxValue = await dateInput.getAttribute("max");
|
||||
expect(maxValue).toBeTruthy();
|
||||
|
||||
// Parse max date and verify it's today or earlier
|
||||
const maxDate = new Date(maxValue as string);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
maxDate.setHours(0, 0, 0, 0);
|
||||
|
||||
expect(maxDate.getTime()).toBeLessThanOrEqual(today.getTime());
|
||||
|
||||
// Close modal
|
||||
await page.getByRole("button", { name: /cancel/i }).click();
|
||||
});
|
||||
|
||||
test("logging period from modal updates dashboard cycle info", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Look for the "Set date" button in onboarding banner
|
||||
const setDateButton = page.getByRole("button", { name: /set date/i });
|
||||
const hasSetDate = await setDateButton.isVisible().catch(() => false);
|
||||
|
||||
if (!hasSetDate) {
|
||||
// User may already have period date set - skip if no onboarding banner
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click the set date button
|
||||
await setDateButton.click();
|
||||
|
||||
// Wait for modal to be visible
|
||||
const modalTitle = page.getByRole("heading", {
|
||||
name: /set period date/i,
|
||||
});
|
||||
await expect(modalTitle).toBeVisible();
|
||||
|
||||
// Calculate a valid date (7 days ago)
|
||||
const testDate = new Date();
|
||||
testDate.setDate(testDate.getDate() - 7);
|
||||
const dateStr = testDate.toISOString().split("T")[0];
|
||||
|
||||
// Fill in the date
|
||||
const dateInput = page.locator('input[type="date"]');
|
||||
await dateInput.fill(dateStr);
|
||||
|
||||
// Click Save
|
||||
await page.getByRole("button", { name: /save/i }).click();
|
||||
|
||||
// Modal should close
|
||||
await expect(modalTitle).not.toBeVisible();
|
||||
|
||||
// Dashboard should now show cycle information (Day X · Phase)
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Look for cycle day display (e.g., "Day 8 · Follicular" or similar)
|
||||
const cycleInfo = page.getByText(/day\s+\d+\s+·/i);
|
||||
await expect(cycleInfo).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test("edit period modal flow changes date successfully", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/period-history");
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Look for edit button and table to ensure we have data
|
||||
const editButton = page.getByRole("button", { name: /edit/i }).first();
|
||||
const hasEdit = await editButton.isVisible().catch(() => false);
|
||||
|
||||
if (!hasEdit) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the original date from the first row
|
||||
const firstRow = page.locator("tbody tr").first();
|
||||
const originalDateCell = firstRow.locator("td").first();
|
||||
const originalDateText = await originalDateCell.textContent();
|
||||
|
||||
// Click edit button
|
||||
await editButton.click();
|
||||
|
||||
// Edit modal should appear
|
||||
const editModalTitle = page.getByRole("heading", {
|
||||
name: /edit period date/i,
|
||||
});
|
||||
await expect(editModalTitle).toBeVisible();
|
||||
|
||||
// Get the date input in the edit modal
|
||||
const editDateInput = page.locator("#editDate");
|
||||
await expect(editDateInput).toBeVisible();
|
||||
|
||||
// Calculate a new date (14 days ago)
|
||||
const newDate = new Date();
|
||||
newDate.setDate(newDate.getDate() - 14);
|
||||
const newDateStr = newDate.toISOString().split("T")[0];
|
||||
|
||||
// Clear and fill new date
|
||||
await editDateInput.fill(newDateStr);
|
||||
|
||||
// Click Save in the edit modal
|
||||
await page.getByRole("button", { name: /save/i }).click();
|
||||
|
||||
// Modal should close
|
||||
await expect(editModalTitle).not.toBeVisible();
|
||||
|
||||
// Wait for table to refresh
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Verify the date changed (the row should have new date text)
|
||||
const updatedDateCell = page
|
||||
.locator("tbody tr")
|
||||
.first()
|
||||
.locator("td")
|
||||
.first();
|
||||
const updatedDateText = await updatedDateCell.textContent();
|
||||
|
||||
// If we had original data, verify it changed
|
||||
if (originalDateText) {
|
||||
// Format the new date to match display format (e.g., "Jan 1, 2024")
|
||||
const formattedNewDate = newDate.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
expect(updatedDateText).toContain(
|
||||
formattedNewDate.split(",")[0].split(" ")[0],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("delete period confirmation flow removes entry", async ({ page }) => {
|
||||
await page.goto("/period-history");
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Look for delete button
|
||||
const deleteButton = page
|
||||
.getByRole("button", { name: /delete/i })
|
||||
.first();
|
||||
const hasDelete = await deleteButton.isVisible().catch(() => false);
|
||||
|
||||
if (!hasDelete) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the total count text before deletion
|
||||
const totalText = page.getByText(/\d+ periods/);
|
||||
const hasTotal = await totalText.isVisible().catch(() => false);
|
||||
let originalCount = 0;
|
||||
if (hasTotal) {
|
||||
const countMatch = (await totalText.textContent())?.match(
|
||||
/(\d+) periods/,
|
||||
);
|
||||
if (countMatch) {
|
||||
originalCount = parseInt(countMatch[1], 10);
|
||||
}
|
||||
}
|
||||
|
||||
// Click delete button
|
||||
await deleteButton.click();
|
||||
|
||||
// Confirmation modal should appear
|
||||
const confirmModalTitle = page.getByRole("heading", {
|
||||
name: /delete period/i,
|
||||
});
|
||||
await expect(confirmModalTitle).toBeVisible();
|
||||
|
||||
// Should show warning message
|
||||
const warningText = page.getByText(/are you sure.*delete/i);
|
||||
await expect(warningText).toBeVisible();
|
||||
|
||||
// Should have Cancel and Confirm buttons
|
||||
await expect(page.getByRole("button", { name: /cancel/i })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /confirm/i }),
|
||||
).toBeVisible();
|
||||
|
||||
// Click Confirm to delete
|
||||
await page.getByRole("button", { name: /confirm/i }).click();
|
||||
|
||||
// Modal should close
|
||||
await expect(confirmModalTitle).not.toBeVisible();
|
||||
|
||||
// Wait for page to refresh
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// If we had a count, verify it decreased
|
||||
if (originalCount > 1) {
|
||||
const newTotalText = page.getByText(/\d+ periods/);
|
||||
const newTotalVisible = await newTotalText
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
if (newTotalVisible) {
|
||||
const newCountMatch = (await newTotalText.textContent())?.match(
|
||||
/(\d+) periods/,
|
||||
);
|
||||
if (newCountMatch) {
|
||||
const newCount = parseInt(newCountMatch[1], 10);
|
||||
expect(newCount).toBe(originalCount - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user