#!/usr/bin/env node const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const BASE_URL = 'http://localhost:3033'; const TEST_USER = 'admin'; const TEST_PASS = 'admin123'; function runPlaywrightTest() { const testScript = ` const { chromium } = require('playwright'); async function runTests() { console.log('Starting functional tests...'); const browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const context = await browser.newContext({ ignoreHTTPSErrors: true, viewport: { width: 1920, height: 1080 } }); const page = await context.newPage(); try { // 1. Login await page.goto('${BASE_URL}'); await page.waitForSelector('input[name="username"]'); await page.fill('input[name="username"]', '${TEST_USER}'); await page.fill('input[name="password"]', '${TEST_PASS}'); await page.click('button[type="submit"]'); await page.waitForSelector('.tracker-container', { timeout: 10000 }); console.log('Login: PASS'); // 2. Create 20 bills await page.goto('${BASE_URL}/bills'); await page.waitForSelector('button:has-text("Add Bill")'); const bills = [ { name: 'Rent', category: 'Housing', dueDay: 1, amount: 1200, autopay: true, twoFA: false }, { name: 'Electric', category: 'Utilities', dueDay: 5, amount: 85, autopay: true, twoFA: false }, { name: 'Groceries', category: 'Food', dueDay: 10, amount: 400, autopay: false, twoFA: false }, { name: 'Gas', category: 'Transport', dueDay: 15, amount: 50, autopay: true, twoFA: true }, { name: 'Netflix', category: 'Subscriptions', dueDay: 20, amount: 15, autopay: true, twoFA: false }, { name: 'Gym', category: 'Health', dueDay: 1, amount: 30, autopay: true, twoFA: false }, { name: 'Phone', category: 'Subscriptions', dueDay: 3, amount: 60, autopay: true, twoFA: true }, { name: 'Water', category: 'Utilities', dueDay: 8, amount: 45, autopay: false, twoFA: false }, { name: 'Internet', category: 'Utilities', dueDay: 12, amount: 70, autopay: true, twoFA: false }, { name: 'Netflix Family', category: 'Subscriptions', dueDay: 20, amount: 20, autopay: true, twoFA: false }, { name: 'Amazon Prime', category: 'Subscriptions', dueDay: 22, amount: 13, autopay: true, twoFA: false }, { name: 'Microsoft 365', category: 'Subscriptions', dueDay: 25, amount: 10, autopay: true, twoFA: true }, { name: 'Spotify', category: 'Subscriptions', dueDay: 28, amount: 10, autopay: true, twoFA: false }, { name: 'Dental', category: 'Health', dueDay: 15, amount: 100, autopay: false, twoFA: false }, { name: 'Insurance', category: 'Health', dueDay: 1, amount: 200, autopay: true, twoFA: true }, { name: 'Car Payment', category: 'Transport', dueDay: 5, amount: 350, autopay: true, twoFA: false }, { name: 'Parking', category: 'Transport', dueDay: 15, amount: 25, autopay: false, twoFA: false }, { name: 'Movies', category: 'Entertainment', dueDay: 10, amount: 40, autopay: false, twoFA: false }, { name: 'Restaurant', category: 'Food', dueDay: 20, amount: 80, autopay: false, twoFA: false }, { name: 'Other', category: 'Other', dueDay: 25, amount: 50, autopay: false, twoFA: true }, ]; for (const bill of bills) { await page.click('button:has-text("Add Bill")'); await page.waitForSelector('text=Add Bill'); await page.fill('input[name="name"]', bill.name); await page.fill('input[name="expected_amount"]', String(bill.amount)); await page.fill('input[name="due_day"]', String(bill.dueDay)); await page.click('button:has-text("Select category")'); await page.waitForSelector('button:has-text("' + bill.category + '")'); await page.click('button:has-text("' + bill.category + '")'); if (bill.autopay) await page.click('label:has-text("Autopay")'); if (bill.twoFA) await page.click('label:has-text("Two-factor")'); await page.click('button:has-text("Save")'); await page.waitForTimeout(500); } const billCount = await page.locator('.bill-row').count(); console.log('Bills created: ' + billCount); // 3. Test notes feature await page.goto('${BASE_URL}/tracker'); await page.waitForTimeout(3000); const billRows = await page.locator('.bill-row, .react-flow__node, .bill-card').all(); console.log('Bills on tracker: ' + billRows.length); // Add notes to bills let notesAdded = 0; for (let i = 0; i < Math.min(20, billRows.length); i++) { const row = billRows[i]; await row.hover(); await page.waitForTimeout(100); const notesInput = await row.locator('input[placeholder*="notes"], input.notes-input').first(); if (await notesInput.count() > 0) { await notesInput.fill('Test note ' + (i + 1)); await page.waitForTimeout(500); await notesInput.blur(); notesAdded++; } } console.log('Notes added: ' + notesAdded); // Verify persistence await page.reload(); await page.waitForTimeout(2000); const content = await page.content(); let persisted = 0; for (let i = 1; i <= notesAdded; i++) { if (content.includes('Test note ' + i)) persisted++; } console.log('Notes persisted: ' + persisted); console.log('ALL TESTS COMPLETED'); } catch (error) { console.error('Test error:', error.message); } finally { await browser.close(); } } runTests(); `; fs.writeFileSync('/tmp/playwright-test.js', testScript); try { const output = execSync('cd /home/kaspa/.openclaw/Projects/bill-tracker && npx playwright exec /tmp/playwright-test.js 2>&1', { encoding: 'utf8', maxBuffer: 1024 * 1024 * 10 }); console.log(output); return output; } catch (error) { console.error('Error running playwright:', error.stderr || error.message); return error.stderr || error.message; } } // Run the test console.log('='.repeat(60)); console.log('Bill Tracker Functional Test'); console.log('Started:', new Date().toLocaleString()); console.log('='.repeat(60)); console.log(''); const output = runPlaywrightTest(); console.log(''); console.log('='.repeat(60)); console.log('Test Output:'); console.log('='.repeat(60)); console.log(output); // Save results to REVIEW.md const timestamp = new Date().toLocaleString('en-US', { timeZone: 'America/Chicago', dateStyle: 'full', timeStyle: 'long' }); const newSection = ` ## Functional Testing Results - ${timestamp} ### Test Run Output \`\`\` ${output} \`\`\` ### Notes Feature Status The notes feature is implemented as **per-bill AND per-month**. Each bill has its own notes field, and each month has its own separate notes. --- `; const reviewPath = path.join(__dirname, 'REVIEW.md'); let reviewContent = ''; try { reviewContent = fs.readFileSync(reviewPath, 'utf8'); } catch (e) { reviewContent = '# Bill Tracker Multi-Agent Review\n\n'; } const updatedContent = reviewContent.replace( /## Functional Testing Results - .*?(?=##|$)/s, '' ) + newSection; fs.writeFileSync(reviewPath, updatedContent, 'utf8'); console.log('\n✅ Test results saved to REVIEW.md');