Fix flaky E2E tests with proper selectors and response waits
- Use data-testid attributes to target specific day cards in availability tests - Wait for networkidle before interacting with page elements - Set up PUT and GET response listeners before triggering actions - Add retry logic for availability API in booking tests - Fix appointments test to handle multiple 'Booked' elements with .first() - Increase parallel workers to 12 for faster test execution
This commit is contained in:
parent
b3e00b0745
commit
89eec1e9c4
8 changed files with 120 additions and 294 deletions
|
|
@ -107,54 +107,106 @@ test.describe("Availability Page - Admin Access", () => {
|
|||
test("can add availability slot", async ({ page }) => {
|
||||
await page.goto("/admin/availability");
|
||||
|
||||
// Click on the first day
|
||||
const tomorrowText = getTomorrowDisplay();
|
||||
await page.getByText(tomorrowText).click();
|
||||
// Wait for initial data load to complete
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Find a day card with "No availability" and click on it
|
||||
// This ensures we're clicking on a day without existing slots
|
||||
const dayCardWithNoAvailability = page.locator('[data-testid^="day-card-"]').filter({
|
||||
has: page.getByText("No availability")
|
||||
}).first();
|
||||
await dayCardWithNoAvailability.click();
|
||||
|
||||
// Wait for modal
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).toBeVisible();
|
||||
|
||||
// Click Save (default is 09:00-17:00)
|
||||
// Set up listeners for both PUT and GET before clicking Save to avoid race condition
|
||||
const putPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "PUT"
|
||||
);
|
||||
const getPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "GET"
|
||||
);
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await putPromise;
|
||||
await getPromise;
|
||||
|
||||
// Wait for modal to close
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).not.toBeVisible();
|
||||
|
||||
// Should now show the slot
|
||||
// Should now show the slot (the card we clicked should now have this slot)
|
||||
await expect(page.getByText("09:00 - 17:00")).toBeVisible();
|
||||
});
|
||||
|
||||
test("can clear availability", async ({ page }) => {
|
||||
await page.goto("/admin/availability");
|
||||
|
||||
const tomorrowText = getTomorrowDisplay();
|
||||
// Wait for initial data load to complete
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Find a day card with "No availability" and click on it
|
||||
const dayCardWithNoAvailability = page.locator('[data-testid^="day-card-"]').filter({
|
||||
has: page.getByText("No availability")
|
||||
}).first();
|
||||
|
||||
// Get the testid so we can find the same card later
|
||||
const testId = await dayCardWithNoAvailability.getAttribute('data-testid');
|
||||
const targetCard = page.locator(`[data-testid="${testId}"]`);
|
||||
|
||||
// First add availability
|
||||
await page.getByText(tomorrowText).click();
|
||||
await dayCardWithNoAvailability.click();
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).toBeVisible();
|
||||
|
||||
// Set up listeners for both PUT and GET before clicking Save to avoid race condition
|
||||
const savePutPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "PUT"
|
||||
);
|
||||
const saveGetPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "GET"
|
||||
);
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await savePutPromise;
|
||||
await saveGetPromise;
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).not.toBeVisible();
|
||||
|
||||
// Verify slot exists
|
||||
await expect(page.getByText("09:00 - 17:00")).toBeVisible();
|
||||
// Verify slot exists in the specific card we clicked
|
||||
await expect(targetCard.getByText("09:00 - 17:00")).toBeVisible();
|
||||
|
||||
// Now clear it
|
||||
await page.getByText(tomorrowText).click();
|
||||
// Now clear it - click on the same card using the testid
|
||||
await targetCard.click();
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).toBeVisible();
|
||||
|
||||
// Set up listeners for both PUT and GET before clicking Clear to avoid race condition
|
||||
const clearPutPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "PUT"
|
||||
);
|
||||
const clearGetPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "GET"
|
||||
);
|
||||
await page.getByRole("button", { name: "Clear All" }).click();
|
||||
await clearPutPromise;
|
||||
await clearGetPromise;
|
||||
|
||||
// Wait for modal to close
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).not.toBeVisible();
|
||||
|
||||
// Slot should be gone - verify by checking the time slot is no longer visible
|
||||
await expect(page.getByText("09:00 - 17:00")).not.toBeVisible();
|
||||
// Slot should be gone from this specific card
|
||||
await expect(targetCard.getByText("09:00 - 17:00")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("can add multiple slots", async ({ page }) => {
|
||||
await page.goto("/admin/availability");
|
||||
|
||||
const tomorrowText = getTomorrowDisplay();
|
||||
await page.getByText(tomorrowText).click();
|
||||
// Wait for initial data load to complete
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Find a day card with "No availability" and click on it (to avoid conflicts with booking tests)
|
||||
const dayCardWithNoAvailability = page.locator('[data-testid^="day-card-"]').filter({
|
||||
has: page.getByText("No availability")
|
||||
}).first();
|
||||
const testId = await dayCardWithNoAvailability.getAttribute('data-testid');
|
||||
const targetCard = page.locator(`[data-testid="${testId}"]`);
|
||||
await dayCardWithNoAvailability.click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).toBeVisible();
|
||||
|
||||
|
|
@ -169,13 +221,21 @@ test.describe("Availability Page - Admin Access", () => {
|
|||
await timeSelects.nth(2).selectOption("14:00"); // Second slot start
|
||||
await timeSelects.nth(3).selectOption("17:00"); // Second slot end
|
||||
|
||||
// Save
|
||||
// Set up listeners for both PUT and GET before clicking Save to avoid race condition
|
||||
const putPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "PUT"
|
||||
);
|
||||
const getPromise = page.waitForResponse(resp =>
|
||||
resp.url().includes("/api/admin/availability") && resp.request().method() === "GET"
|
||||
);
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
await putPromise;
|
||||
await getPromise;
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).not.toBeVisible();
|
||||
|
||||
// Should see both slots
|
||||
await expect(page.getByText("09:00 - 12:00")).toBeVisible();
|
||||
await expect(page.getByText("14:00 - 17:00")).toBeVisible();
|
||||
// Should see both slots in the card we clicked
|
||||
await expect(targetCard.getByText("09:00 - 12:00")).toBeVisible();
|
||||
await expect(targetCard.getByText("14:00 - 17:00")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue