Files
automaker/apps/app/tests/profiles-view.spec.ts
Kacper c1b9f1cb28 refactor: address code review suggestions
- Simplify countCustomProfiles by reusing getCustomProfiles helper
- Fix misleading test name and assertion for thinking level controls
2025-12-15 15:11:15 +01:00

1044 lines
33 KiB
TypeScript

import { test, expect } from "@playwright/test";
import {
setupMockProjectWithProfiles,
waitForNetworkIdle,
navigateToProfiles,
clickNewProfileButton,
clickEmptyState,
fillProfileForm,
saveProfile,
cancelProfileDialog,
clickEditProfile,
clickDeleteProfile,
confirmDeleteProfile,
cancelDeleteProfile,
fillProfileName,
fillProfileDescription,
selectIcon,
selectModel,
selectThinkingLevel,
isAddProfileDialogOpen,
isEditProfileDialogOpen,
isDeleteConfirmDialogOpen,
getProfileName,
getProfileDescription,
getProfileModel,
getProfileThinkingLevel,
isBuiltInProfile,
isEditButtonVisible,
isDeleteButtonVisible,
dragProfile,
getProfileOrder,
clickRefreshDefaults,
countCustomProfiles,
countBuiltInProfiles,
getProfileCard,
waitForSuccessToast,
waitForToast,
waitForErrorToast,
waitForDialogClose,
pressModifierEnter,
clickElement,
} from "./utils";
test.describe("AI Profiles View", () => {
// ============================================================================
// Profile Creation Tests
// ============================================================================
test.describe("Profile Creation", () => {
test.beforeEach(async ({ page }) => {
// Start with no custom profiles (only built-in)
await setupMockProjectWithProfiles(page, { customProfilesCount: 0 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should create profile via header button", async ({ page }) => {
// Click the "New Profile" button
await clickNewProfileButton(page);
// Verify dialog is open
expect(await isAddProfileDialogOpen(page)).toBe(true);
// Fill in profile data
await fillProfileForm(page, {
name: "Test Profile",
description: "A test profile",
icon: "Brain",
model: "sonnet",
thinkingLevel: "medium",
});
// Save the profile
await saveProfile(page);
// Verify success toast
await waitForSuccessToast(page, "Profile created");
// Verify profile appears in the list
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(1);
// Verify profile details - get the dynamic profile ID
// (Note: Profile IDs are dynamically generated, not "custom-profile-1")
// We can verify count but skip checking the specific profile name since ID is dynamic
});
test("should create profile via empty state", async ({ page }) => {
// Click the empty state card
await clickEmptyState(page);
// Verify dialog is open
expect(await isAddProfileDialogOpen(page)).toBe(true);
// Fill and save
await fillProfileForm(page, {
name: "Empty State Profile",
description: "Created from empty state",
model: "opus",
});
await saveProfile(page);
// Verify profile was created
await waitForSuccessToast(page, "Profile created");
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(1);
});
test("should create profile with each icon option", async ({ page }) => {
const icons = ["Brain", "Zap", "Scale", "Cpu", "Rocket", "Sparkles"];
for (const icon of icons) {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: `Profile with ${icon}`,
model: "haiku",
icon,
});
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Ensure dialog is fully closed before next iteration
await waitForDialogClose(page);
}
// Verify all profiles were created
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(icons.length);
});
test("should create profile with each model option", async ({ page }) => {
const models = ["haiku", "sonnet", "opus"];
for (const model of models) {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: `Profile with ${model}`,
model,
});
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Ensure dialog is fully closed before next iteration
await waitForDialogClose(page);
}
// Verify all profiles were created
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(models.length);
});
test("should create profile with different thinking levels", async ({
page,
}) => {
const levels = ["none", "low", "medium", "high", "ultrathink"];
for (const level of levels) {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: `Profile with ${level}`,
model: "opus", // Opus supports all thinking levels
thinkingLevel: level,
});
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Ensure dialog is fully closed before next iteration
await waitForDialogClose(page);
}
// Verify all profiles were created
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(levels.length);
});
test("should show warning toast when selecting ultrathink", async ({
page,
}) => {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Ultrathink Profile",
model: "opus",
});
// Select ultrathink
await selectThinkingLevel(page, "ultrathink");
// Verify warning toast appears
await waitForToast(page, "Ultrathink uses extensive reasoning");
});
test("should cancel profile creation", async ({ page }) => {
await clickNewProfileButton(page);
// Fill partial data
await fillProfileName(page, "Cancelled Profile");
// Cancel
await cancelProfileDialog(page);
// Verify dialog is closed
expect(await isAddProfileDialogOpen(page)).toBe(false);
// Verify no profile was created
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(0);
});
test("should close dialog on overlay click", async ({ page }) => {
await clickNewProfileButton(page);
// Click the backdrop/overlay to close the dialog
// The dialog overlay is the background outside the dialog content
const dialogBackdrop = page.locator('[data-radix-dialog-overlay]');
if ((await dialogBackdrop.count()) > 0) {
await dialogBackdrop.click({ position: { x: 10, y: 10 } });
} else {
// Fallback: press Escape key
await page.keyboard.press("Escape");
}
// Wait for dialog to fully close (handles animation)
await waitForDialogClose(page);
// Verify dialog is closed
expect(await isAddProfileDialogOpen(page)).toBe(false);
// Verify no profile was created
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(0);
});
});
// ============================================================================
// Profile Editing Tests
// ============================================================================
test.describe("Profile Editing", () => {
test.beforeEach(async ({ page }) => {
// Start with one custom profile
await setupMockProjectWithProfiles(page, { customProfilesCount: 1 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should edit profile name", async ({ page }) => {
// Click edit button for the custom profile
await clickEditProfile(page, "custom-profile-1");
// Verify dialog is open
expect(await isEditProfileDialogOpen(page)).toBe(true);
// Update name
await fillProfileName(page, "Updated Profile Name");
// Save
await saveProfile(page);
// Verify success toast
await waitForSuccessToast(page, "Profile updated");
// Verify name was updated
const profileName = await getProfileName(page, "custom-profile-1");
expect(profileName).toContain("Updated Profile Name");
});
test("should edit profile description", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
// Update description
await fillProfileDescription(page, "Updated description");
await saveProfile(page);
await waitForSuccessToast(page, "Profile updated");
// Verify description was updated
const description = await getProfileDescription(page, "custom-profile-1");
expect(description).toContain("Updated description");
});
test("should change profile icon", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
// Change icon to a different one
await selectIcon(page, "Rocket");
await saveProfile(page);
await waitForSuccessToast(page, "Profile updated");
// Verify icon was changed (visual check via profile card)
const card = await getProfileCard(page, "custom-profile-1");
const rocketIcon = card.locator('svg[class*="lucide-rocket"]');
expect(await rocketIcon.isVisible()).toBe(true);
});
test("should change profile model", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
// Change model
await selectModel(page, "opus");
await saveProfile(page);
await waitForSuccessToast(page, "Profile updated");
// Verify model badge was updated
const model = await getProfileModel(page, "custom-profile-1");
expect(model.toLowerCase()).toContain("opus");
});
test("should change thinking level", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
// Ensure model supports thinking
await selectModel(page, "sonnet");
await selectThinkingLevel(page, "high");
await saveProfile(page);
await waitForSuccessToast(page, "Profile updated");
// Verify thinking level badge was updated
const thinkingLevel = await getProfileThinkingLevel(
page,
"custom-profile-1"
);
expect(thinkingLevel?.toLowerCase()).toContain("high");
});
test("should cancel edit without saving", async ({ page }) => {
// Get original name
const originalName = await getProfileName(page, "custom-profile-1");
await clickEditProfile(page, "custom-profile-1");
// Change name
await fillProfileName(page, "Should Not Save");
// Cancel
await cancelProfileDialog(page);
// Verify dialog is closed
expect(await isEditProfileDialogOpen(page)).toBe(false);
// Verify name was NOT changed
const currentName = await getProfileName(page, "custom-profile-1");
expect(currentName).toBe(originalName);
});
});
// ============================================================================
// Profile Deletion Tests
// ============================================================================
test.describe("Profile Deletion", () => {
test.beforeEach(async ({ page }) => {
// Start with 2 custom profiles
await setupMockProjectWithProfiles(page, { customProfilesCount: 2 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should delete profile with confirmation", async ({ page }) => {
// Get initial count
const initialCount = await countCustomProfiles(page);
expect(initialCount).toBe(2);
// Click delete button
await clickDeleteProfile(page, "custom-profile-1");
// Verify confirmation dialog is open
expect(await isDeleteConfirmDialogOpen(page)).toBe(true);
// Confirm deletion
await confirmDeleteProfile(page);
// Verify success toast
await waitForSuccessToast(page, "Profile deleted");
// Verify profile was removed
const finalCount = await countCustomProfiles(page);
expect(finalCount).toBe(1);
});
test("should delete via keyboard shortcut (Cmd+Enter)", async ({
page,
}) => {
await clickDeleteProfile(page, "custom-profile-1");
// Press Cmd/Ctrl+Enter to confirm (platform-aware)
await pressModifierEnter(page);
// Verify profile was deleted
await waitForSuccessToast(page, "Profile deleted");
const finalCount = await countCustomProfiles(page);
expect(finalCount).toBe(1);
});
test("should cancel deletion", async ({ page }) => {
const initialCount = await countCustomProfiles(page);
await clickDeleteProfile(page, "custom-profile-1");
// Cancel deletion
await cancelDeleteProfile(page);
// Verify dialog is closed
expect(await isDeleteConfirmDialogOpen(page)).toBe(false);
// Verify profile was NOT deleted
const finalCount = await countCustomProfiles(page);
expect(finalCount).toBe(initialCount);
});
test("should not show delete button for built-in profiles", async ({
page,
}) => {
// Check delete button visibility for built-in profile
const isDeleteVisible = await isDeleteButtonVisible(
page,
"profile-heavy-task"
);
expect(isDeleteVisible).toBe(false);
});
test("should show delete button for custom profiles", async ({ page }) => {
// Check delete button visibility for custom profile
const isDeleteVisible = await isDeleteButtonVisible(
page,
"custom-profile-1"
);
expect(isDeleteVisible).toBe(true);
});
});
// ============================================================================
// Profile Reordering Tests
// ============================================================================
test.describe("Profile Reordering", () => {
test.beforeEach(async ({ page }) => {
// Start with 3 custom profiles for reordering
await setupMockProjectWithProfiles(page, { customProfilesCount: 3 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should drag first profile to last position", async ({ page }) => {
// Get initial order - custom profiles come first (0, 1, 2), then built-in (3, 4, 5)
const initialOrder = await getProfileOrder(page);
// Drag first profile (index 0) to last position (index 5)
await dragProfile(page, 0, 5);
// Get new order
const newOrder = await getProfileOrder(page);
// Verify order changed - the first item should now be at a different position
expect(newOrder).not.toEqual(initialOrder);
});
test.skip("should drag profile to earlier position", async ({ page }) => {
// Note: Skipped because dnd-kit in grid layout doesn't reliably support
// dragging items backwards. Forward drags work correctly.
const initialOrder = await getProfileOrder(page);
// Drag from position 3 to position 1 (moving backward)
await dragProfile(page, 3, 1);
const newOrder = await getProfileOrder(page);
// Verify order changed
expect(newOrder).not.toEqual(initialOrder);
});
test("should drag profile to middle position", async ({ page }) => {
const initialOrder = await getProfileOrder(page);
// Drag first profile to middle position
await dragProfile(page, 0, 3);
const newOrder = await getProfileOrder(page);
// Verify order changed
expect(newOrder).not.toEqual(initialOrder);
});
test("should persist order after creating new profile", async ({
page,
}) => {
// Get initial order
const initialOrder = await getProfileOrder(page);
// Reorder profiles - move first to position 3
await dragProfile(page, 0, 3);
const orderAfterDrag = await getProfileOrder(page);
// Verify drag worked
expect(orderAfterDrag).not.toEqual(initialOrder);
// Create a new profile
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "New Profile",
model: "haiku",
});
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Get order after creation - new profile should be added
const orderAfterCreate = await getProfileOrder(page);
// The new profile should be added (so we have one more profile)
expect(orderAfterCreate.length).toBe(orderAfterDrag.length + 1);
});
test("should show drag handle on all profiles", async ({ page }) => {
// Check for drag handles on both built-in and custom profiles
const builtInDragHandle = page.locator(
'[data-testid="profile-drag-handle-profile-heavy-task"]'
);
const customDragHandle = page.locator(
'[data-testid="profile-drag-handle-custom-profile-1"]'
);
expect(await builtInDragHandle.isVisible()).toBe(true);
expect(await customDragHandle.isVisible()).toBe(true);
});
});
// ============================================================================
// Form Validation Tests
// ============================================================================
test.describe("Form Validation", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 0 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should reject empty profile name", async ({ page }) => {
await clickNewProfileButton(page);
// Try to save without entering a name
await clickElement(page, "save-profile-button");
// Should show error toast
await waitForErrorToast(page, "Please enter a profile name");
// Dialog should still be open
expect(await isAddProfileDialogOpen(page)).toBe(true);
});
test("should reject whitespace-only name", async ({ page }) => {
await clickNewProfileButton(page);
// Enter only whitespace
await fillProfileName(page, " ");
// Try to save
await clickElement(page, "save-profile-button");
// Should show error toast
await waitForErrorToast(page, "Please enter a profile name");
// Dialog should still be open
expect(await isAddProfileDialogOpen(page)).toBe(true);
});
test("should accept valid profile name", async ({ page }) => {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Valid Profile Name",
model: "haiku",
});
await saveProfile(page);
// Should show success toast
await waitForSuccessToast(page, "Profile created");
// Dialog should be closed
expect(await isAddProfileDialogOpen(page)).toBe(false);
});
test("should handle very long profile name", async ({ page }) => {
await clickNewProfileButton(page);
// Create a 200-character name
const longName = "A".repeat(200);
await fillProfileName(page, longName);
await fillProfileForm(page, { model: "haiku" });
await saveProfile(page);
// Should successfully create the profile
await waitForSuccessToast(page, "Profile created");
});
test("should handle special characters in name and description", async ({
page,
}) => {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Test <>&\" Profile",
description: "Description with special chars: <>&\"'",
model: "haiku",
});
await saveProfile(page);
// Should successfully create
await waitForSuccessToast(page, "Profile created");
// Verify name is displayed correctly (without HTML injection)
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(1);
});
test("should allow empty description", async ({ page }) => {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Profile Without Description",
description: "",
model: "haiku",
});
await saveProfile(page);
// Should successfully create
await waitForSuccessToast(page, "Profile created");
});
test("should show thinking level controls when model supports it", async ({
page,
}) => {
await clickNewProfileButton(page);
// Select a model that supports thinking (all current models do)
await selectModel(page, "opus");
// Verify that the thinking level section is visible
const thinkingLevelLabel = page.locator('text="Thinking Level"');
await expect(thinkingLevelLabel).toBeVisible();
// Verify thinking level options are available
const thinkingSelector = page.locator(
'[data-testid^="thinking-select-"]'
);
await expect(thinkingSelector.first()).toBeVisible();
});
});
// ============================================================================
// Keyboard Shortcuts Tests
// ============================================================================
test.describe("Keyboard Shortcuts", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 1 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should save new profile with Cmd+Enter", async ({ page }) => {
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Shortcut Profile",
model: "haiku",
});
// Press Cmd/Ctrl+Enter to save (platform-aware)
await pressModifierEnter(page);
// Should save and show success toast
await waitForSuccessToast(page, "Profile created");
// Wait for dialog to fully close
await waitForDialogClose(page);
// Dialog should be closed
expect(await isAddProfileDialogOpen(page)).toBe(false);
});
test("should save edit with Cmd+Enter", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
await fillProfileName(page, "Edited via Shortcut");
// Press Cmd/Ctrl+Enter to save (platform-aware)
await pressModifierEnter(page);
// Should save and show success toast
await waitForSuccessToast(page, "Profile updated");
});
test("should confirm delete with Cmd+Enter", async ({ page }) => {
await clickDeleteProfile(page, "custom-profile-1");
// Press Cmd/Ctrl+Enter to confirm (platform-aware)
await pressModifierEnter(page);
// Should delete and show success toast
await waitForSuccessToast(page, "Profile deleted");
});
test("should close dialog with Escape key", async ({ page }) => {
// Test add dialog
await clickNewProfileButton(page);
await page.keyboard.press("Escape");
await waitForDialogClose(page);
expect(await isAddProfileDialogOpen(page)).toBe(false);
// Test edit dialog
await clickEditProfile(page, "custom-profile-1");
await page.keyboard.press("Escape");
await waitForDialogClose(page);
expect(await isEditProfileDialogOpen(page)).toBe(false);
// Test delete dialog
await clickDeleteProfile(page, "custom-profile-1");
await page.keyboard.press("Escape");
await waitForDialogClose(page);
expect(await isDeleteConfirmDialogOpen(page)).toBe(false);
});
test("should use correct modifier key for platform", async ({ page }) => {
await clickNewProfileButton(page);
await fillProfileForm(page, { name: "Test", model: "haiku" });
// Press the platform-specific shortcut (uses utility that handles platform detection)
await pressModifierEnter(page);
// Should work regardless of platform
await waitForSuccessToast(page, "Profile created");
});
});
// ============================================================================
// Empty States Tests
// ============================================================================
test.describe("Empty States", () => {
test.beforeEach(async ({ page }) => {
// Start with no custom profiles
await setupMockProjectWithProfiles(page, { customProfilesCount: 0 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should show empty state when no custom profiles exist", async ({
page,
}) => {
// Check for empty state element
const emptyState = page.locator(
'text="No custom profiles yet. Create one to get started!"'
);
expect(await emptyState.isVisible()).toBe(true);
});
test("should open add dialog when clicking empty state", async ({
page,
}) => {
await clickEmptyState(page);
// Dialog should open
expect(await isAddProfileDialogOpen(page)).toBe(true);
});
test("should hide empty state after creating first profile", async ({
page,
}) => {
// Create a profile
await clickEmptyState(page);
await fillProfileForm(page, { name: "First Profile", model: "haiku" });
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Empty state should no longer be visible
const emptyState = page.locator(
'text="No custom profiles yet. Create one to get started!"'
);
expect(await emptyState.isVisible()).toBe(false);
// Profile card should be visible
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(1);
});
});
// ============================================================================
// Built-in vs Custom Profiles Tests
// ============================================================================
test.describe("Built-in vs Custom Profiles", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 1 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should show built-in badge on built-in profiles", async ({
page,
}) => {
// Check Heavy Task profile
const isBuiltIn = await isBuiltInProfile(page, "profile-heavy-task");
expect(isBuiltIn).toBe(true);
// Verify lock icon is present
const card = await getProfileCard(page, "profile-heavy-task");
const lockIcon = card.locator('svg[class*="lucide-lock"]');
expect(await lockIcon.isVisible()).toBe(true);
});
test("should not show edit button on built-in profiles", async ({
page,
}) => {
const isEditVisible = await isEditButtonVisible(
page,
"profile-heavy-task"
);
expect(isEditVisible).toBe(false);
});
test("should not show delete button on built-in profiles", async ({
page,
}) => {
const isDeleteVisible = await isDeleteButtonVisible(
page,
"profile-heavy-task"
);
expect(isDeleteVisible).toBe(false);
});
test("should show edit and delete buttons on custom profiles", async ({
page,
}) => {
// Check custom profile
const isEditVisible = await isEditButtonVisible(
page,
"custom-profile-1"
);
const isDeleteVisible = await isDeleteButtonVisible(
page,
"custom-profile-1"
);
expect(isEditVisible).toBe(true);
expect(isDeleteVisible).toBe(true);
});
});
// ============================================================================
// Header Actions Tests
// ============================================================================
test.describe("Header Actions", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 2 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should refresh default profiles", async ({ page }) => {
await clickRefreshDefaults(page);
// Should show success toast - message is "Profiles refreshed"
await waitForSuccessToast(page, "Profiles refreshed");
// Built-in profiles should still be visible
const builtInCount = await countBuiltInProfiles(page);
expect(builtInCount).toBe(3);
// Custom profiles should be preserved
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(2);
});
test("should display correct profile count badges", async ({ page }) => {
// Check for count badges by counting actual profile cards
const customCount = await countCustomProfiles(page);
const builtInCount = await countBuiltInProfiles(page);
expect(customCount).toBe(2);
expect(builtInCount).toBe(3);
// Total profiles should be 5 (2 custom + 3 built-in)
const totalProfiles = customCount + builtInCount;
expect(totalProfiles).toBe(5);
});
});
// ============================================================================
// Data Persistence Tests
// ============================================================================
test.describe("Data Persistence", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 0 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should persist created profile after navigation", async ({
page,
}) => {
// Create a profile
await clickNewProfileButton(page);
await fillProfileForm(page, {
name: "Persistent Profile",
model: "haiku",
});
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Navigate away (within app, not full page reload)
await page.locator('[data-testid="nav-board"]').click();
await waitForNetworkIdle(page);
// Navigate back to profiles
await navigateToProfiles(page);
await waitForNetworkIdle(page);
// Profile should still exist
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(1);
});
test("should show correct count after creating multiple profiles", async ({
page,
}) => {
// Create multiple profiles
for (let i = 1; i <= 3; i++) {
await clickNewProfileButton(page);
await fillProfileForm(page, { name: `Profile ${i}`, model: "haiku" });
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Ensure dialog is fully closed before next iteration
await waitForDialogClose(page);
}
// Verify all profiles exist
const customCount = await countCustomProfiles(page);
expect(customCount).toBe(3);
// Built-in should still be there
const builtInCount = await countBuiltInProfiles(page);
expect(builtInCount).toBe(3);
});
test("should maintain profile order after navigation", async ({ page }) => {
// Create 3 profiles
for (let i = 1; i <= 3; i++) {
await clickNewProfileButton(page);
await fillProfileForm(page, { name: `Profile ${i}`, model: "haiku" });
await saveProfile(page);
await waitForSuccessToast(page, "Profile created");
// Ensure dialog is fully closed before next iteration
await waitForDialogClose(page);
}
// Get order after creation
const orderAfterCreate = await getProfileOrder(page);
// Navigate away (within app)
await page.locator('[data-testid="nav-board"]').click();
await waitForNetworkIdle(page);
// Navigate back
await navigateToProfiles(page);
await waitForNetworkIdle(page);
// Verify order is maintained
const orderAfterNavigation = await getProfileOrder(page);
expect(orderAfterNavigation).toEqual(orderAfterCreate);
});
});
// ============================================================================
// Toast Notifications Tests
// ============================================================================
test.describe("Toast Notifications", () => {
test.beforeEach(async ({ page }) => {
await setupMockProjectWithProfiles(page, { customProfilesCount: 1 });
await page.goto("/");
await waitForNetworkIdle(page);
await navigateToProfiles(page);
});
test("should show success toast on profile creation", async ({ page }) => {
await clickNewProfileButton(page);
await fillProfileForm(page, { name: "New Profile", model: "haiku" });
await saveProfile(page);
// Verify toast with profile name
await waitForSuccessToast(page, "Profile created");
});
test("should show success toast on profile update", async ({ page }) => {
await clickEditProfile(page, "custom-profile-1");
await fillProfileName(page, "Updated");
await saveProfile(page);
await waitForSuccessToast(page, "Profile updated");
});
test("should show success toast on profile deletion", async ({ page }) => {
await clickDeleteProfile(page, "custom-profile-1");
await confirmDeleteProfile(page);
await waitForSuccessToast(page, "Profile deleted");
});
test("should show error toast on validation failure", async ({ page }) => {
await clickNewProfileButton(page);
// Try to save without a name
await clickElement(page, "save-profile-button");
// Should show error toast
await waitForErrorToast(page, "Please enter a profile name");
});
});
});