import { test, expect } from '@playwright/test' test.describe('Trainers List Page', () => { test.beforeEach(async ({ page }) => { await page.goto('/trainers') }) test('should display trainers page header', async ({ page }) => { const heading = page.getByRole('heading', { name: /trainers|tabatafit admin/i }) await expect(heading).toBeVisible() }) test('should have Add Trainer button', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return const addButton = page.getByRole('link', { name: /add trainer/i }).or( page.getByRole('button', { name: /add trainer/i }) ) await expect(addButton).toBeVisible() }) test('should display trainer cards or empty state', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return // Wait for loading to finish await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) // Should show either trainer cards or empty state const hasTrainerCards = await page.locator('[class*="grid"]').locator('[class*="bg-neutral-900"]').count() > 0 const hasEmptyState = await page.getByText(/no trainers yet/i).isVisible().catch(() => false) const hasError = await page.getByText(/failed to load/i).isVisible().catch(() => false) expect(hasTrainerCards || hasEmptyState || hasError).toBeTruthy() }) test('should display trainer name and specialty on cards', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return // Wait for loading to finish await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) // If there are trainer cards, check they have name and specialty const cards = page.locator('[class*="bg-neutral-900"]').filter({ has: page.locator('h3') }) const count = await cards.count() if (count > 0) { const firstCard = cards.first() // Card should have a name (h3 element) await expect(firstCard.locator('h3')).toBeVisible() // Card should have specialty text const specialtyText = firstCard.locator('p').first() await expect(specialtyText).toBeVisible() } }) test('should show workout count on trainer cards', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) // Check for "X workouts" text const workoutCountText = page.getByText(/\d+ workouts/i) const visible = await workoutCountText.first().isVisible().catch(() => false) // Only assert if trainers exist if (visible) { await expect(workoutCountText.first()).toBeVisible() } }) test('should have edit and delete action buttons on cards', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) // Find edit and delete buttons (icon buttons with svg) const editButtons = page.locator('button').filter({ has: page.locator('svg.lucide-edit') }) const deleteButtons = page.locator('button').filter({ has: page.locator('svg.lucide-trash-2') }) const editCount = await editButtons.count() const deleteCount = await deleteButtons.count() // If trainers are displayed, they should have action buttons if (editCount > 0) { expect(editCount).toBeGreaterThan(0) expect(deleteCount).toBeGreaterThan(0) } }) }) test.describe('Trainers Delete Dialog', () => { test.beforeEach(async ({ page }) => { await page.goto('/trainers') }) test('should open delete confirmation dialog', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) const deleteButtons = page.locator('button').filter({ has: page.locator('svg.lucide-trash-2') }) const count = await deleteButtons.count() if (count > 0) { await deleteButtons.first().click() // Dialog should appear await expect(page.getByRole('heading', { name: /delete trainer/i })).toBeVisible() await expect(page.getByText(/are you sure/i)).toBeVisible() await expect(page.getByRole('button', { name: /cancel/i })).toBeVisible() await expect(page.getByRole('button', { name: /^delete$/i })).toBeVisible() } }) test('should close delete dialog on Cancel', async ({ page }) => { const url = page.url() if (!url.includes('/trainers')) return await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) const deleteButtons = page.locator('button').filter({ has: page.locator('svg.lucide-trash-2') }) const count = await deleteButtons.count() if (count > 0) { await deleteButtons.first().click() await expect(page.getByRole('heading', { name: /delete trainer/i })).toBeVisible() // Click cancel await page.getByRole('button', { name: /cancel/i }).click() // Dialog should close await expect(page.getByRole('heading', { name: /delete trainer/i })).not.toBeVisible() } }) }) test.describe('Trainers Error State', () => { test('should show error state with retry button on failure', async ({ page }) => { // This test verifies the error UI exists in the component // In actual failure scenarios, it would show the error state await page.goto('/trainers') const url = page.url() if (!url.includes('/trainers')) return await page.waitForSelector('[class*="animate-spin"]', { state: 'detached', timeout: 10000 }).catch(() => {}) // Check if error state is shown (only if Supabase is unreachable) const hasError = await page.getByText(/failed to load trainers/i).isVisible().catch(() => false) if (hasError) { await expect(page.getByRole('button', { name: /try again/i })).toBeVisible() } }) })