more merging
This commit is contained in:
parent
67ffe6a823
commit
d6f955d2d9
5 changed files with 107 additions and 251 deletions
|
|
@ -75,107 +75,68 @@ test.describe("Authentication Flow", () => {
|
|||
});
|
||||
});
|
||||
|
||||
test.describe("Logged-in User Visiting Invite URL", () => {
|
||||
test("redirects to exchange when logged-in user visits invite URL or signup page", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
const email = uniqueEmail();
|
||||
const inviteCode = await createInvite(request);
|
||||
|
||||
// First sign up to create a user
|
||||
await page.goto("/signup");
|
||||
await page.fill("input#inviteCode", inviteCode);
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page.locator("h1")).toHaveText("Create account");
|
||||
|
||||
await page.fill("input#email", email);
|
||||
await page.fill("input#password", "password123");
|
||||
await page.fill("input#confirmPassword", "password123");
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
|
||||
// Create another invite
|
||||
const anotherInvite = await createInvite(request);
|
||||
|
||||
// Visit invite URL while logged in - should redirect to exchange
|
||||
await page.goto(`/signup/${anotherInvite}`);
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
|
||||
// Try to visit signup page while logged in - should redirect to exchange
|
||||
await page.goto("/signup");
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Signup with Invite", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await clearAuth(page);
|
||||
});
|
||||
|
||||
test("can create a new account with valid invite", async ({ page, request }) => {
|
||||
const email = uniqueEmail();
|
||||
const inviteCode = await createInvite(request);
|
||||
|
||||
await page.goto("/signup");
|
||||
|
||||
// Step 1: Enter invite code
|
||||
await page.fill("input#inviteCode", inviteCode);
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
// Wait for form to transition to registration form
|
||||
await expect(page.locator("h1")).toHaveText("Create account");
|
||||
|
||||
// Step 2: Fill registration form
|
||||
await page.fill("input#email", email);
|
||||
await page.fill("input#password", "password123");
|
||||
await page.fill("input#confirmPassword", "password123");
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
// Should redirect to exchange after signup (regular user home)
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
// Should see Exchange page heading
|
||||
await expect(page.getByRole("heading", { name: "Exchange Bitcoin" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("signup with direct invite URL works", async ({ page, request }) => {
|
||||
const email = uniqueEmail();
|
||||
const inviteCode = await createInvite(request);
|
||||
|
||||
// Use direct URL with code
|
||||
await page.goto(`/signup/${inviteCode}`);
|
||||
|
||||
// Should redirect to signup with code in query and validate
|
||||
await page.waitForURL(/\/signup\?code=/);
|
||||
|
||||
// Wait for form to transition to registration form
|
||||
await expect(page.locator("h1")).toHaveText("Create account");
|
||||
|
||||
// Fill registration form
|
||||
await page.fill("input#email", email);
|
||||
await page.fill("input#password", "password123");
|
||||
await page.fill("input#confirmPassword", "password123");
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
// Should redirect to exchange
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
});
|
||||
|
||||
test("shows error for invalid invite code", async ({ page }) => {
|
||||
await page.goto("/signup");
|
||||
await page.fill("input#inviteCode", "fake-code-99");
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
// Should show error
|
||||
await expect(page.getByText(/not found/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows validation errors for password mismatch and short password", async ({
|
||||
test("can create account with valid invite via form and direct URL, and logged-in users are redirected", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
const inviteCode = await createInvite(request);
|
||||
// Test signup via form
|
||||
const email1 = uniqueEmail();
|
||||
const inviteCode1 = await createInvite(request);
|
||||
|
||||
await page.goto("/signup");
|
||||
await page.fill("input#inviteCode", inviteCode1);
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page.locator("h1")).toHaveText("Create account");
|
||||
|
||||
await page.fill("input#email", email1);
|
||||
await page.fill("input#password", "password123");
|
||||
await page.fill("input#confirmPassword", "password123");
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
await expect(page.getByRole("heading", { name: "Exchange Bitcoin" })).toBeVisible();
|
||||
|
||||
// Test logged-in user visiting invite URL - should redirect to exchange
|
||||
const anotherInvite = await createInvite(request);
|
||||
await page.goto(`/signup/${anotherInvite}`);
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
|
||||
// Test logged-in user visiting signup page - should redirect to exchange
|
||||
await page.goto("/signup");
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
|
||||
// Test signup via direct URL (new session)
|
||||
await clearAuth(page);
|
||||
const email2 = uniqueEmail();
|
||||
const inviteCode2 = await createInvite(request);
|
||||
|
||||
await page.goto(`/signup/${inviteCode2}`);
|
||||
await page.waitForURL(/\/signup\?code=/);
|
||||
await expect(page.locator("h1")).toHaveText("Create account");
|
||||
|
||||
await page.fill("input#email", email2);
|
||||
await page.fill("input#password", "password123");
|
||||
await page.fill("input#confirmPassword", "password123");
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
});
|
||||
|
||||
test("shows errors for invalid invite code and password validation", async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
// Test invalid invite code
|
||||
await page.goto("/signup");
|
||||
await page.fill("input#inviteCode", "fake-code-99");
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page.getByText(/not found/i)).toBeVisible();
|
||||
|
||||
// Test password validation with valid invite
|
||||
const inviteCode = await createInvite(request);
|
||||
await page.goto("/signup");
|
||||
await page.fill("input#inviteCode", inviteCode);
|
||||
await page.click('button[type="submit"]');
|
||||
|
|
@ -220,11 +181,15 @@ test.describe("Login", () => {
|
|||
await clearAuth(page);
|
||||
});
|
||||
|
||||
test("can login with valid credentials", async ({ page }) => {
|
||||
test("can login with valid credentials and shows loading state", async ({ page }) => {
|
||||
// Test loading state
|
||||
await page.goto("/login");
|
||||
await page.fill('input[type="email"]', testEmail);
|
||||
await page.fill('input[type="password"]', testPassword);
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
const submitPromise = page.click('button[type="submit"]');
|
||||
await expect(page.locator('button[type="submit"]')).toHaveText("Signing in...");
|
||||
await submitPromise;
|
||||
|
||||
// Regular user redirects to exchange
|
||||
await expect(page).toHaveURL("/exchange");
|
||||
|
|
@ -246,16 +211,6 @@ test.describe("Login", () => {
|
|||
await page.click('button[type="submit"]');
|
||||
await expect(page.getByText("Incorrect email or password")).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows loading state while submitting", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await page.fill('input[type="email"]', testEmail);
|
||||
await page.fill('input[type="password"]', testPassword);
|
||||
|
||||
const submitPromise = page.click('button[type="submit"]');
|
||||
await expect(page.locator('button[type="submit"]')).toHaveText("Signing in...");
|
||||
await submitPromise;
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Logout", () => {
|
||||
|
|
|
|||
|
|
@ -45,44 +45,7 @@ test.describe("Availability Page - Admin Access", () => {
|
|||
await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("can add availability slot", async ({ page }) => {
|
||||
await page.goto("/admin/availability");
|
||||
|
||||
// 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();
|
||||
|
||||
// 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 (the card we clicked should now have this slot)
|
||||
await expect(page.getByText("09:00 - 17:00")).toBeVisible();
|
||||
});
|
||||
|
||||
test("can clear availability", async ({ page }) => {
|
||||
test("can add, clear, and add multiple availability slots", async ({ page }) => {
|
||||
await page.goto("/admin/availability");
|
||||
|
||||
// Wait for initial data load to complete
|
||||
|
|
@ -139,39 +102,31 @@ test.describe("Availability Page - Admin Access", () => {
|
|||
|
||||
// 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");
|
||||
|
||||
// Wait for initial data load to complete
|
||||
// Now test adding multiple slots - find another day card
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
// Find a day card with "No availability" and click on it (to avoid conflicts with booking tests)
|
||||
const dayCardWithNoAvailability = page
|
||||
const anotherDayCard = 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();
|
||||
const anotherTestId = await anotherDayCard.getAttribute("data-testid");
|
||||
const anotherTargetCard = page.locator(`[data-testid="${anotherTestId}"]`);
|
||||
await anotherDayCard.click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).toBeVisible();
|
||||
|
||||
// First slot is 09:00-17:00 by default - change it to morning only
|
||||
const timeSelects = page.locator("select");
|
||||
await timeSelects.nth(1).selectOption("12:00"); // Change first slot end to 12:00
|
||||
await timeSelects.nth(1).selectOption("12:00");
|
||||
|
||||
// Add another slot for afternoon
|
||||
await page.getByText("+ Add Time Range").click();
|
||||
await timeSelects.nth(2).selectOption("14:00");
|
||||
await timeSelects.nth(3).selectOption("17:00");
|
||||
|
||||
// Change second slot times to avoid overlap
|
||||
await timeSelects.nth(2).selectOption("14:00"); // Second slot start
|
||||
await timeSelects.nth(3).selectOption("17:00"); // Second slot end
|
||||
|
||||
// Set up listeners for both PUT and GET before clicking Save to avoid race condition
|
||||
// Save multiple slots
|
||||
const putPromise = page.waitForResponse(
|
||||
(resp) => resp.url().includes("/api/admin/availability") && resp.request().method() === "PUT"
|
||||
);
|
||||
|
|
@ -183,9 +138,9 @@ test.describe("Availability Page - Admin Access", () => {
|
|||
await getPromise;
|
||||
await expect(page.getByRole("heading", { name: /Edit Availability/ })).not.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();
|
||||
// Should see both slots
|
||||
await expect(anotherTargetCard.getByText("09:00 - 12:00")).toBeVisible();
|
||||
await expect(anotherTargetCard.getByText("14:00 - 17:00")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ test.describe("Exchange Page - Regular User Access", () => {
|
|||
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
|
||||
});
|
||||
|
||||
test("regular user can access exchange page and all UI elements are visible", async ({
|
||||
test("regular user can access exchange page, all UI elements work, and buy/sell toggle functions", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Test navigation
|
||||
|
|
@ -56,10 +56,15 @@ test.describe("Exchange Page - Regular User Access", () => {
|
|||
await expect(page.getByText("Market:")).toBeVisible();
|
||||
await expect(page.getByText("Our price:")).toBeVisible();
|
||||
|
||||
// Test buy/sell toggle
|
||||
await expect(page.getByRole("button", { name: "Buy BTC" })).toBeVisible();
|
||||
// Test buy/sell toggle visibility and functionality
|
||||
const buyButton = page.getByRole("button", { name: "Buy BTC" });
|
||||
await expect(buyButton).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Sell BTC" })).toBeVisible();
|
||||
|
||||
// Test clicking buy/sell changes direction
|
||||
await page.getByRole("button", { name: "Sell BTC" }).click();
|
||||
await expect(page.getByText(/You buy €\d/)).toBeVisible();
|
||||
|
||||
// Test payment method selector
|
||||
await expect(page.getByText("Payment Method")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /Onchain/ })).toBeVisible();
|
||||
|
|
@ -68,33 +73,10 @@ test.describe("Exchange Page - Regular User Access", () => {
|
|||
// Test amount slider
|
||||
await expect(page.getByText("Amount")).toBeVisible();
|
||||
await expect(page.locator('input[type="range"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test("clicking buy/sell changes direction", async ({ page }) => {
|
||||
await page.goto("/exchange");
|
||||
|
||||
// Initially in buy mode - summary shows BTC first: "You buy [sats], you sell €X"
|
||||
// Verify buy mode is initially active
|
||||
const buyButton = page.getByRole("button", { name: "Buy BTC" });
|
||||
await expect(buyButton).toBeVisible();
|
||||
|
||||
// Click Sell BTC to switch direction
|
||||
await page.getByRole("button", { name: "Sell BTC" }).click();
|
||||
|
||||
// In sell mode, the summary shows EUR first: "You buy €X, you sell [sats]"
|
||||
// We can verify by checking the summary text contains "You buy €" (EUR comes first)
|
||||
await expect(page.getByText(/You buy €\d/)).toBeVisible();
|
||||
});
|
||||
|
||||
test("exchange page shows date selection after continue", async ({ page }) => {
|
||||
await page.goto("/exchange");
|
||||
|
||||
// Step 1: Click "Continue to Booking" to proceed to step 2
|
||||
// Test date selection appears after continue
|
||||
await page.getByRole("button", { name: "Continue to Booking" }).click();
|
||||
|
||||
// Step 2: Now date selection should be visible
|
||||
await expect(page.getByRole("heading", { name: "Select a Date" })).toBeVisible();
|
||||
// Should see multiple date buttons
|
||||
const dateButtons = page
|
||||
.locator("button")
|
||||
.filter({ hasText: /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/ });
|
||||
|
|
@ -163,10 +145,10 @@ test.describe("Exchange Page - With Availability", () => {
|
|||
await expect(page.getByText("Payment:")).toBeVisible();
|
||||
});
|
||||
|
||||
test("payment method selector works", async ({ page }) => {
|
||||
test("payment method selector works and lightning disabled above threshold", async ({ page }) => {
|
||||
await page.goto("/exchange");
|
||||
|
||||
// Default should be Onchain
|
||||
// Test payment method selector
|
||||
const onchainButton = page.getByRole("button", { name: /Onchain/ });
|
||||
const lightningButton = page.getByRole("button", { name: /Lightning/ });
|
||||
await expect(onchainButton).toHaveCSS("border-color", "rgb(167, 139, 250)");
|
||||
|
|
@ -179,20 +161,11 @@ test.describe("Exchange Page - With Availability", () => {
|
|||
// Click back to Onchain
|
||||
await onchainButton.click();
|
||||
await expect(onchainButton).toHaveCSS("border-color", "rgb(167, 139, 250)");
|
||||
});
|
||||
|
||||
test("lightning disabled above threshold", async ({ page }) => {
|
||||
await page.goto("/exchange");
|
||||
|
||||
// Set amount above threshold (€1000 = 100000 cents)
|
||||
// Test lightning disabled above threshold
|
||||
const amountInput = page.locator('input[type="text"]').filter({ hasText: "" });
|
||||
await amountInput.fill("1100");
|
||||
|
||||
// Lightning button should be disabled
|
||||
const lightningButton = page.getByRole("button", { name: /Lightning/ });
|
||||
await expect(lightningButton).toBeDisabled();
|
||||
|
||||
// Should show threshold message
|
||||
await expect(page.getByText(/Lightning payments are only available/)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -249,27 +222,18 @@ test.describe("Admin Trades Page", () => {
|
|||
await loginUser(page, ADMIN_USER.email, ADMIN_USER.password);
|
||||
});
|
||||
|
||||
test("admin can access trades page", async ({ page }) => {
|
||||
test("admin can access trades page with tabs, regular user cannot", async ({ page }) => {
|
||||
// Test admin access
|
||||
await page.goto("/admin/trades");
|
||||
|
||||
await expect(page).toHaveURL("/admin/trades");
|
||||
await expect(page.getByRole("heading", { name: "Trades" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("admin trades page shows tabs", async ({ page }) => {
|
||||
await page.goto("/admin/trades");
|
||||
|
||||
await expect(page.getByRole("button", { name: /Upcoming/ })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /History/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("regular user cannot access admin trades page", async ({ page }) => {
|
||||
// Test regular user cannot access
|
||||
await clearAuth(page);
|
||||
await loginUser(page, REGULAR_USER.email, REGULAR_USER.password);
|
||||
|
||||
await page.goto("/admin/trades");
|
||||
|
||||
// Should be redirected away
|
||||
await expect(page).not.toHaveURL("/admin/trades");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ test.describe("Profile - Form Behavior", () => {
|
|||
await clearProfileData(page);
|
||||
});
|
||||
|
||||
test("form state management - empty fields, button states", async ({ page }) => {
|
||||
test("form state management, save, persistence, and clearing fields", async ({ page }) => {
|
||||
await page.goto("/profile");
|
||||
|
||||
// All editable fields should be empty
|
||||
|
|
@ -135,12 +135,8 @@ test.describe("Profile - Form Behavior", () => {
|
|||
// Make a change - button should be enabled
|
||||
await page.fill("#telegram", "@testhandle");
|
||||
await expect(saveButton).toBeEnabled();
|
||||
});
|
||||
|
||||
test("can save profile, values persist, and can clear fields", async ({ page }) => {
|
||||
await page.goto("/profile");
|
||||
|
||||
// Fill in all fields
|
||||
// Now test saving and persistence - fill in all fields
|
||||
await page.fill("#contact_email", "contact@test.com");
|
||||
await page.fill("#telegram", "@testuser");
|
||||
await page.fill("#signal", "signal.42");
|
||||
|
|
@ -181,7 +177,7 @@ test.describe("Profile - Validation", () => {
|
|||
await clearProfileData(page);
|
||||
});
|
||||
|
||||
test("validation - telegram auto-prepend, errors for invalid inputs", async ({ page }) => {
|
||||
test("validation - all field validations and error fixing", async ({ page }) => {
|
||||
await page.goto("/profile");
|
||||
|
||||
// Test telegram auto-prepend
|
||||
|
|
@ -190,8 +186,7 @@ test.describe("Profile - Validation", () => {
|
|||
|
||||
// Test telegram error - no characters after @
|
||||
await page.fill("#telegram", "@");
|
||||
await page.waitForTimeout(600);
|
||||
await expect(page.getByText(/at least one character after @/i)).toBeVisible();
|
||||
await expect(page.getByText(/at least one character after @/i)).toBeVisible({ timeout: 2000 });
|
||||
const saveButton = page.getByRole("button", { name: /save changes/i });
|
||||
await expect(saveButton).toBeDisabled();
|
||||
|
||||
|
|
@ -199,45 +194,29 @@ test.describe("Profile - Validation", () => {
|
|||
await page.fill("#nostr_npub", "invalidnpub");
|
||||
await expect(page.getByText(/must start with 'npub1'/i)).toBeVisible();
|
||||
await expect(saveButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test("can fix validation error and save", async ({ page }) => {
|
||||
await page.goto("/profile");
|
||||
// Test invalid email format
|
||||
await page.fill("#contact_email", "not-an-email");
|
||||
await expect(page.getByText(/valid email/i)).toBeVisible();
|
||||
await expect(saveButton).toBeDisabled();
|
||||
|
||||
// Enter invalid telegram (just @ with no handle)
|
||||
await page.fill("#telegram", "@");
|
||||
|
||||
// Wait for debounced validation
|
||||
await page.waitForTimeout(600);
|
||||
|
||||
await expect(page.getByText(/at least one character after @/i)).toBeVisible();
|
||||
|
||||
// Fix it
|
||||
// Fix all validation errors and save
|
||||
await page.fill("#telegram", "@validhandle");
|
||||
await expect(page.getByText(/at least one character after @/i)).not.toBeVisible({
|
||||
timeout: 2000,
|
||||
});
|
||||
|
||||
// Wait for debounced validation
|
||||
await page.waitForTimeout(600);
|
||||
await page.fill("#nostr_npub", VALID_NPUB);
|
||||
await expect(page.getByText(/must start with 'npub1'/i)).not.toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Error should disappear
|
||||
await expect(page.getByText(/at least one character after @/i)).not.toBeVisible();
|
||||
await page.fill("#contact_email", "valid@email.com");
|
||||
await expect(page.getByText(/valid email/i)).not.toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Should be able to save
|
||||
const saveButton = page.getByRole("button", { name: /save changes/i });
|
||||
// Now all errors are fixed, save button should be enabled
|
||||
await expect(saveButton).toBeEnabled();
|
||||
|
||||
await page.click('button:has-text("Save Changes")');
|
||||
await expect(page.getByText(/saved successfully/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows error for invalid email format", async ({ page }) => {
|
||||
await page.goto("/profile");
|
||||
|
||||
// Enter invalid email
|
||||
await page.fill("#contact_email", "not-an-email");
|
||||
|
||||
// Should show error
|
||||
await expect(page.getByText(/valid email/i)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Profile - Admin User Access", () => {
|
||||
|
|
|
|||
|
|
@ -17,5 +17,8 @@ export default defineConfig({
|
|||
baseURL: "http://localhost:3000",
|
||||
// Action timeout (clicks, fills, etc.)
|
||||
actionTimeout: 5000,
|
||||
// Reduce screenshot/recording overhead
|
||||
screenshot: "only-on-failure",
|
||||
trace: "retain-on-failure",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue