import { chromium } from 'playwright'; const browser = await chromium.launch({ channel: 'chrome', headless: false, slowMo: 300 }); const context = await browser.newContext({ viewport: { width: 390, height: 844 } }); const page = await context.newPage(); const BASE = 'http://localhost:5173'; let failed = false; async function step(name, fn) { process.stdout.write(`▶ ${name}... `); try { await fn(); console.log('✅'); } catch (e) { console.log(`❌ ${e.message}`); failed = true; throw e; } } try { await step('Navigate to app → should redirect to /login', async () => { await page.goto(BASE); await page.waitForURL('**/login', { timeout: 5000 }); }); await step('Register a new account', async () => { await page.click('text=Create account'); await page.fill('input[placeholder="Name"]', 'TestChef'); await page.fill('input[placeholder="Password"]', 'password123'); await page.fill('input[placeholder="Family code"]', 'dev-family-code'); await page.click('button[type="submit"]'); await page.waitForURL('**/lists', { timeout: 5000 }); }); await step('Create store "Kroger"', async () => { await page.click('a:has-text("Stores")'); await page.waitForURL('**/stores'); await page.fill('input[placeholder="New store name"]', 'Kroger'); await page.click('button:has-text("Add")'); await page.waitForSelector('text=Kroger'); }); await step('Create store "Costco"', async () => { await page.fill('input[placeholder="New store name"]', 'Costco'); await page.click('button:has-text("Add")'); await page.waitForSelector('text=Costco'); }); await step('Create shopping list "Weekly Groceries"', async () => { await page.click('a:has-text("Lists")'); await page.waitForURL('**/lists'); await page.click('text=+ New list'); await page.fill('input[placeholder="List name"]', 'Weekly Groceries'); await page.selectOption('select', { label: 'Kroger' }); await page.click('button:has-text("Create")'); await page.waitForSelector('text=Weekly Groceries'); }); await step('Open list and add 5 items', async () => { await page.click('text=Weekly Groceries'); await page.waitForURL(/\/lists\/\d+/); for (const item of ['Milk', 'Eggs', 'Bread', 'Chicken breast', 'Broccoli']) { await page.fill('input[placeholder="Add an item..."]', item); await page.click('button:has-text("Add")'); await page.waitForSelector(`text=${item}`); } }); await step('Check off Milk and Eggs', async () => { await page.click('button[aria-label="Check Milk"]'); await page.waitForSelector('text=Checked (1)'); await page.click('button[aria-label="Check Eggs"]'); await page.waitForSelector('text=Checked (2)'); }); await step('Uncheck Milk', async () => { await page.click('button[aria-label="Uncheck Milk"]'); await page.waitForSelector('text=Checked (1)'); }); await step('Remove Broccoli', async () => { await page.click('button[aria-label="Remove Broccoli"]'); await page.waitForFunction(() => !document.body.textContent.includes('Broccoli'), { timeout: 3000 }); }); await step('Create recipe "Chicken Stir Fry"', async () => { await page.click('a:has-text("Recipes")'); await page.waitForURL('**/recipes'); await page.click('text=+ New recipe'); await page.waitForURL('**/recipes/new'); await page.fill('input[placeholder="Recipe title"]', 'Chicken Stir Fry'); await page.fill('textarea[placeholder*="description"]', 'Quick weeknight dinner'); const qtyInputs = page.locator('input[placeholder="Qty"]'); const nameInputs = page.locator('input[placeholder="Ingredient name"]'); await qtyInputs.nth(0).fill('2 lbs'); await nameInputs.nth(0).fill('chicken breast'); await page.click('text=+ Add ingredient'); await qtyInputs.nth(1).fill('1 head'); await nameInputs.nth(1).fill('broccoli'); await page.click('text=+ Add ingredient'); await qtyInputs.nth(2).fill('3 tbsp'); await nameInputs.nth(2).fill('soy sauce'); await page.fill('textarea[placeholder*="Instructions"]', '1. Cut chicken\n2. Stir fry\n3. Add sauce'); await page.click('button:has-text("Save Recipe")'); await page.waitForURL(/\/recipes\/\d+/); }); await step('Add recipe ingredients to shopping list', async () => { await page.click('button:has-text("Add to list")'); await page.waitForSelector('text=Choose a list'); await page.click('text=Weekly Groceries'); await page.waitForURL(/\/lists\/\d+/); await page.waitForSelector('text=chicken breast'); }); await step('Verify list has recipe items', async () => { const content = await page.textContent('body'); for (const item of ['2 lbs chicken breast', '1 head broccoli', '3 tbsp soy sauce']) { if (!content.includes(item)) throw new Error(`Missing recipe item: ${item}`); } }); await step('Navigate back to lists overview', async () => { await page.click('text=← Back'); await page.waitForURL('**/lists'); }); await step('Sign out', async () => { await page.click('text=Sign out'); await page.waitForURL('**/login'); }); console.log('\n🎉 All tests passed!'); } catch (e) { console.error(`\n💥 Test suite stopped at failure: ${e.message}`); process.exitCode = 1; } finally { await new Promise(r => setTimeout(r, 2000)); await browser.close(); }