189 lines
7.1 KiB
JavaScript
189 lines
7.1 KiB
JavaScript
#!/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');
|