Add Prettier for TypeScript formatting
- Install prettier - Configure .prettierrc.json and .prettierignore - Add npm scripts: format, format:check - Add Makefile target: format-frontend - Format all frontend files
This commit is contained in:
parent
4b394b0698
commit
37de6f70e0
44 changed files with 906 additions and 856 deletions
|
|
@ -4,23 +4,23 @@ import { API_URL, REGULAR_USER, ADMIN_USER, clearAuth, loginUser } from "./helpe
|
|||
|
||||
/**
|
||||
* Appointments Page E2E Tests
|
||||
*
|
||||
*
|
||||
* Tests for viewing and cancelling user appointments.
|
||||
*/
|
||||
|
||||
// Set up availability and create a booking
|
||||
async function createTestBooking(page: Page) {
|
||||
const dateStr = getTomorrowDateStr();
|
||||
|
||||
|
||||
// First login as admin to set availability
|
||||
await clearAuth(page);
|
||||
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
|
||||
|
||||
|
||||
const adminCookies = await page.context().cookies();
|
||||
const adminAuthCookie = adminCookies.find(c => c.name === "auth_token");
|
||||
|
||||
const adminAuthCookie = adminCookies.find((c) => c.name === "auth_token");
|
||||
|
||||
if (!adminAuthCookie) throw new Error("No admin auth cookie");
|
||||
|
||||
|
||||
await page.request.put(`${API_URL}/api/admin/availability`, {
|
||||
headers: {
|
||||
Cookie: `auth_token=${adminAuthCookie.value}`,
|
||||
|
|
@ -31,22 +31,22 @@ async function createTestBooking(page: Page) {
|
|||
slots: [{ start_time: "09:00:00", end_time: "12:00:00" }],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Login as regular user
|
||||
await clearAuth(page);
|
||||
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
|
||||
|
||||
|
||||
const userCookies = await page.context().cookies();
|
||||
const userAuthCookie = userCookies.find(c => c.name === "auth_token");
|
||||
|
||||
const userAuthCookie = userCookies.find((c) => c.name === "auth_token");
|
||||
|
||||
if (!userAuthCookie) throw new Error("No user auth cookie");
|
||||
|
||||
|
||||
// Create booking - use a random minute to avoid conflicts with parallel tests
|
||||
const randomMinute = Math.floor(Math.random() * 11) * 15; // 0, 15, 30, 45 etc up to 165 min
|
||||
const hour = 9 + Math.floor(randomMinute / 60);
|
||||
const minute = randomMinute % 60;
|
||||
const timeStr = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`;
|
||||
|
||||
const timeStr = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}:00`;
|
||||
|
||||
const response = await page.request.post(`${API_URL}/api/booking`, {
|
||||
headers: {
|
||||
Cookie: `auth_token=${userAuthCookie.value}`,
|
||||
|
|
@ -57,7 +57,7 @@ async function createTestBooking(page: Page) {
|
|||
note: "Test appointment",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
|
|
@ -69,20 +69,20 @@ test.describe("Appointments Page - Regular User Access", () => {
|
|||
|
||||
test("regular user can access appointments page", async ({ page }) => {
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
await expect(page).toHaveURL("/appointments");
|
||||
await expect(page.getByRole("heading", { name: "My Appointments" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("regular user sees Appointments link in navigation", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
|
||||
|
||||
await expect(page.getByRole("link", { name: "Appointments" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows empty state when no appointments", async ({ page }) => {
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
await expect(page.getByText("don't have any appointments")).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "Book an appointment" })).toBeVisible();
|
||||
});
|
||||
|
|
@ -92,10 +92,10 @@ test.describe("Appointments Page - With Bookings", () => {
|
|||
test("shows user's appointments", async ({ page }) => {
|
||||
// Create a booking first
|
||||
await createTestBooking(page);
|
||||
|
||||
|
||||
// Go to appointments page
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
// Should see the appointment
|
||||
await expect(page.getByText("Test appointment")).toBeVisible();
|
||||
await expect(page.getByText("Booked", { exact: true })).toBeVisible();
|
||||
|
|
@ -104,16 +104,16 @@ test.describe("Appointments Page - With Bookings", () => {
|
|||
test("can cancel an appointment", async ({ page }) => {
|
||||
// Create a booking
|
||||
await createTestBooking(page);
|
||||
|
||||
|
||||
// Go to appointments page
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
// Click cancel button
|
||||
await page.getByRole("button", { name: "Cancel" }).first().click();
|
||||
|
||||
|
||||
// Confirm cancellation
|
||||
await page.getByRole("button", { name: "Confirm" }).click();
|
||||
|
||||
|
||||
// Should show cancelled status
|
||||
await expect(page.getByText("Cancelled by you")).toBeVisible();
|
||||
});
|
||||
|
|
@ -121,19 +121,19 @@ test.describe("Appointments Page - With Bookings", () => {
|
|||
test("can abort cancellation", async ({ page }) => {
|
||||
// Create a booking
|
||||
await createTestBooking(page);
|
||||
|
||||
|
||||
// Go to appointments page
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
// Wait for appointments to load
|
||||
await expect(page.getByRole("heading", { name: /Upcoming/ })).toBeVisible({ timeout: 10000 });
|
||||
|
||||
|
||||
// Click cancel button
|
||||
await page.getByRole("button", { name: "Cancel" }).first().click();
|
||||
|
||||
|
||||
// Click No to abort
|
||||
await page.getByRole("button", { name: "No" }).click();
|
||||
|
||||
|
||||
// Should still show as booked (use first() since there may be multiple bookings)
|
||||
await expect(page.getByText("Booked", { exact: true }).first()).toBeVisible();
|
||||
});
|
||||
|
|
@ -143,9 +143,9 @@ test.describe("Appointments Page - Access Control", () => {
|
|||
test("admin cannot access appointments page", async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
|
||||
|
||||
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
// Should be redirected
|
||||
await expect(page).not.toHaveURL("/appointments");
|
||||
});
|
||||
|
|
@ -153,17 +153,17 @@ test.describe("Appointments Page - Access Control", () => {
|
|||
test("admin does not see Appointments link", async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
|
||||
|
||||
|
||||
await page.goto("/audit");
|
||||
|
||||
|
||||
await expect(page.getByRole("link", { name: "Appointments" })).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("unauthenticated user redirected to login", async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
|
||||
|
||||
await page.goto("/appointments");
|
||||
|
||||
|
||||
await expect(page).toHaveURL("/login");
|
||||
});
|
||||
});
|
||||
|
|
@ -172,17 +172,17 @@ test.describe("Appointments API", () => {
|
|||
test("regular user can view appointments via API", async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
|
||||
|
||||
|
||||
const cookies = await page.context().cookies();
|
||||
const authCookie = cookies.find(c => c.name === "auth_token");
|
||||
|
||||
const authCookie = cookies.find((c) => c.name === "auth_token");
|
||||
|
||||
if (authCookie) {
|
||||
const response = await page.request.get(`${API_URL}/api/appointments`, {
|
||||
headers: {
|
||||
Cookie: `auth_token=${authCookie.value}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
expect(response.status()).toBe(200);
|
||||
expect(Array.isArray(await response.json())).toBe(true);
|
||||
}
|
||||
|
|
@ -191,22 +191,19 @@ test.describe("Appointments API", () => {
|
|||
test("regular user can cancel appointment via API", async ({ page }) => {
|
||||
// Create a booking
|
||||
const booking = await createTestBooking(page);
|
||||
|
||||
|
||||
const cookies = await page.context().cookies();
|
||||
const authCookie = cookies.find(c => c.name === "auth_token");
|
||||
|
||||
const authCookie = cookies.find((c) => c.name === "auth_token");
|
||||
|
||||
if (authCookie && booking && booking.id) {
|
||||
const response = await page.request.post(
|
||||
`${API_URL}/api/appointments/${booking.id}/cancel`,
|
||||
{
|
||||
headers: {
|
||||
Cookie: `auth_token=${authCookie.value}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: {},
|
||||
}
|
||||
);
|
||||
|
||||
const response = await page.request.post(`${API_URL}/api/appointments/${booking.id}/cancel`, {
|
||||
headers: {
|
||||
Cookie: `auth_token=${authCookie.value}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: {},
|
||||
});
|
||||
|
||||
expect(response.status()).toBe(200);
|
||||
const data = await response.json();
|
||||
expect(data.status).toBe("cancelled_by_user");
|
||||
|
|
@ -216,19 +213,18 @@ test.describe("Appointments API", () => {
|
|||
test("admin cannot view user appointments via API", async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
|
||||
|
||||
|
||||
const cookies = await page.context().cookies();
|
||||
const authCookie = cookies.find(c => c.name === "auth_token");
|
||||
|
||||
const authCookie = cookies.find((c) => c.name === "auth_token");
|
||||
|
||||
if (authCookie) {
|
||||
const response = await page.request.get(`${API_URL}/api/appointments`, {
|
||||
headers: {
|
||||
Cookie: `auth_token=${authCookie.value}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
expect(response.status()).toBe(403);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue