Files
proxmox/scripts/unifi/test-all-add-button-strategies.js
defiQUG fbda1b4beb
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 15:46:57 -08:00

283 lines
11 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Test All Add Button Strategies
*
* This script systematically tests all possible ways to find and click
* the Add button for static routes, including:
* - All button detection methods
* - Keyboard shortcuts
* - Clicking in different areas
* - Testing menu items
*/
import { chromium } from 'playwright';
import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
// Load environment variables
const envPath = join(homedir(), '.env');
function loadEnvFile(filePath) {
try {
const envFile = readFileSync(filePath, 'utf8');
const envVars = envFile.split('\n').filter(
(line) => line.includes('=') && !line.trim().startsWith('#')
);
for (const line of envVars) {
const [key, ...values] = line.split('=');
if (key && values.length > 0 && /^[A-Z_][A-Z0-9_]*$/.test(key.trim())) {
let value = values.join('=').trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
process.env[key.trim()] = value;
}
}
return true;
} catch {
return false;
}
}
loadEnvFile(envPath);
const UDM_URL = process.env.UNIFI_UDM_URL || 'https://192.168.0.1';
const USERNAME = process.env.UNIFI_BROWSER_USERNAME || process.env.UNIFI_USERNAME || 'unifi_api';
const PASSWORD = process.env.UNIFI_BROWSER_PASSWORD || process.env.UNIFI_PASSWORD;
console.log('🧪 Testing All Add Button Strategies');
console.log('===================================\n');
if (!PASSWORD) {
console.error('❌ UNIFI_PASSWORD must be set in ~/.env');
process.exit(1);
}
(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
try {
console.log('1. Logging in...');
await page.goto(UDM_URL, { waitUntil: 'networkidle' });
await page.waitForSelector('input[type="text"]');
await page.fill('input[type="text"]', USERNAME);
await page.fill('input[type="password"]', PASSWORD);
await page.click('button[type="submit"]');
await page.waitForTimeout(5000);
console.log('2. Navigating to Routing page...');
await page.goto(`${UDM_URL}/network/default/settings/routing`, { waitUntil: 'networkidle' });
await page.waitForTimeout(10000);
await page.waitForURL('**/settings/routing**', { timeout: 20000 }).catch(() => {});
await page.waitForTimeout(5000);
console.log(` Current URL: ${page.url()}\n`);
// Wait for routes API
try {
await page.waitForResponse(response =>
response.url().includes('/rest/routing') || response.url().includes('/trafficroutes'),
{ timeout: 15000 }
);
} catch (error) {
// Continue
}
await page.waitForTimeout(5000);
console.log('3. Testing Strategies:\n');
console.log('='.repeat(80));
const strategies = [];
let success = false;
// Strategy 1: Find all buttons and test each
console.log('\n📋 Strategy 1: Testing All Buttons');
const allButtons = await page.evaluate(() => {
const buttons = Array.from(document.querySelectorAll('button, [role="button"]'));
return buttons.map((btn, index) => {
const rect = btn.getBoundingClientRect();
const styles = window.getComputedStyle(btn);
if (rect.width > 0 && rect.height > 0 && styles.display !== 'none' && styles.visibility !== 'hidden') {
return {
index,
text: btn.textContent?.trim() || '',
className: btn.className || '',
id: btn.id || '',
ariaLabel: btn.getAttribute('aria-label') || '',
enabled: !btn.disabled,
selector: btn.id ? `#${btn.id}` : `.${btn.className.split(' ')[0]}`,
};
}
return null;
}).filter(b => b !== null);
});
console.log(` Found ${allButtons.length} buttons`);
for (const btn of allButtons.slice(0, 10)) { // Test first 10 buttons
try {
console.log(`\n Testing Button ${btn.index}: "${btn.text}" (${btn.className.substring(0, 50)})`);
// Try clicking
await page.click(btn.selector, { timeout: 3000 }).catch(async () => {
// Try JavaScript click
await page.evaluate((id) => {
const el = document.getElementById(id);
if (el) el.click();
}, btn.id).catch(() => {});
});
await page.waitForTimeout(3000);
// Check for form
const hasForm = await page.locator('input[name="name"], input[name="destination"]').first().isVisible({ timeout: 2000 }).catch(() => false);
if (hasForm) {
console.log(` ✅✅✅ SUCCESS! Button ${btn.index} opened the form! ✅✅✅`);
console.log(` Selector: ${btn.selector}`);
console.log(` ID: ${btn.id || 'none'}`);
console.log(` Class: ${btn.className}`);
success = true;
strategies.push({ strategy: 'Button Click', button: btn, success: true });
break;
}
// Check for menu
const hasMenu = await page.locator('[role="menu"], [role="listbox"]').first().isVisible({ timeout: 2000 }).catch(() => false);
if (hasMenu) {
console.log(` ⚠️ Menu appeared`);
const menuItems = await page.evaluate(() => {
const menu = document.querySelector('[role="menu"], [role="listbox"]');
if (!menu) return [];
return Array.from(menu.querySelectorAll('[role="menuitem"], [role="option"], li, div')).map(item => ({
text: item.textContent?.trim() || '',
})).filter(item => item.text.length > 0 && (item.text.toLowerCase().includes('add') || item.text.toLowerCase().includes('route')));
});
if (menuItems.length > 0) {
console.log(` Found Add-related menu items: ${menuItems.map(m => `"${m.text}"`).join(', ')}`);
// Try clicking first Add-related item
for (const item of menuItems) {
try {
await page.click(`text="${item.text}"`, { timeout: 2000 });
await page.waitForTimeout(3000);
const hasForm2 = await page.locator('input[name="name"], input[name="destination"]').first().isVisible({ timeout: 2000 }).catch(() => false);
if (hasForm2) {
console.log(` ✅✅✅ SUCCESS! Menu item "${item.text}" opened the form! ✅✅✅`);
success = true;
strategies.push({ strategy: 'Menu Item Click', button: btn, menuItem: item, success: true });
break;
}
} catch (error) {
// Try next item
}
}
}
// Close menu
await page.keyboard.press('Escape');
await page.waitForTimeout(1000);
}
if (success) break;
} catch (error) {
console.log(` ❌ Error: ${error.message}`);
}
}
// Strategy 2: Keyboard shortcuts
if (!success) {
console.log('\n⌨ Strategy 2: Testing Keyboard Shortcuts');
const shortcuts = ['Control+N', 'Control+KeyN', '+', 'Control+Plus', 'Insert'];
for (const shortcut of shortcuts) {
try {
console.log(` Trying: ${shortcut}`);
await page.keyboard.press(shortcut);
await page.waitForTimeout(3000);
const hasForm = await page.locator('input[name="name"], input[name="destination"]').first().isVisible({ timeout: 2000 }).catch(() => false);
if (hasForm) {
console.log(` ✅✅✅ SUCCESS! Keyboard shortcut ${shortcut} opened the form! ✅✅✅`);
success = true;
strategies.push({ strategy: 'Keyboard Shortcut', shortcut, success: true });
break;
}
} catch (error) {
// Try next shortcut
}
}
}
// Strategy 3: Click in routes area
if (!success) {
console.log('\n🖱 Strategy 3: Clicking in Routes Area');
try {
const routesArea = await page.locator(':text("Static Routes"), :text("Routes"), table').first();
if (await routesArea.isVisible({ timeout: 3000 })) {
const box = await routesArea.boundingBox();
if (box) {
// Try clicking in different areas
const positions = [
{ x: box.x + box.width - 50, y: box.y + 20, name: 'top-right' },
{ x: box.x + box.width - 100, y: box.y + 20, name: 'top-right-2' },
{ x: box.x + 50, y: box.y + 20, name: 'top-left' },
];
for (const pos of positions) {
console.log(` Clicking at ${pos.name} (${pos.x}, ${pos.y})`);
await page.mouse.click(pos.x, pos.y);
await page.waitForTimeout(2000);
const hasForm = await page.locator('input[name="name"], input[name="destination"]').first().isVisible({ timeout: 2000 }).catch(() => false);
if (hasForm) {
console.log(` ✅✅✅ SUCCESS! Clicking at ${pos.name} opened the form! ✅✅✅`);
success = true;
strategies.push({ strategy: 'Area Click', position: pos, success: true });
break;
}
}
}
}
} catch (error) {
console.log(` ❌ Error: ${error.message}`);
}
}
// Summary
console.log('\n\n📊 Summary:');
console.log('='.repeat(80));
if (success) {
console.log('✅ SUCCESS! Found working strategy:');
strategies.filter(s => s.success).forEach(s => {
console.log(` - ${s.strategy}`);
if (s.button) console.log(` Button: "${s.button.text}" (${s.button.selector})`);
if (s.menuItem) console.log(` Menu Item: "${s.menuItem.text}"`);
if (s.shortcut) console.log(` Shortcut: ${s.shortcut}`);
if (s.position) console.log(` Position: ${s.position.name}`);
});
} else {
console.log('❌ No working strategy found');
console.log('\nNext steps:');
console.log('1. Manually inspect the page in the browser');
console.log('2. Identify the Add button location');
console.log('3. Update the script with the correct selector');
}
console.log('\n⏸ Browser will stay open for 60 seconds for manual inspection...');
await page.waitForTimeout(60000);
} catch (error) {
console.error('❌ Error:', error.message);
await page.screenshot({ path: 'test-strategies-error.png', fullPage: true });
} finally {
await browser.close();
}
})();