Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
147 lines
4.4 KiB
JavaScript
147 lines
4.4 KiB
JavaScript
const fs = require('fs');
|
|
const http = require('http');
|
|
const WebSocket = require('ws');
|
|
const path = require('path');
|
|
|
|
const ARTIFACTS_DIR = "C:\\Users\\encep\\.gemini\\antigravity\\brain\\8f83f382-c9ee-4ef3-8e9b-a7dec5371b5a";
|
|
|
|
function getTargets() {
|
|
return new Promise((resolve, reject) => {
|
|
http.get('http://127.0.0.1:9222/json/list', (res) => {
|
|
let data = '';
|
|
res.on('data', chunk => data += chunk);
|
|
res.on('end', () => resolve(JSON.parse(data)));
|
|
}).on('error', reject);
|
|
});
|
|
}
|
|
|
|
function sendCDP(ws, method, params = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
const id = Math.floor(Math.random() * 1000000);
|
|
const handler = (data) => {
|
|
const msg = JSON.parse(data);
|
|
if (msg.id === id) {
|
|
ws.removeListener('message', handler);
|
|
if (msg.error) reject(msg.error);
|
|
else resolve(msg.result);
|
|
}
|
|
};
|
|
ws.on('message', handler);
|
|
ws.send(JSON.stringify({ id, method, params }));
|
|
});
|
|
}
|
|
|
|
async function runFullSuite() {
|
|
const targets = await getTargets();
|
|
const mainTarget = targets.find(t => t.title === 'D3RO Voice' || t.url.includes('index.html'));
|
|
if (!mainTarget) {
|
|
console.error('Target not found:', targets);
|
|
return;
|
|
}
|
|
|
|
const wsUrl = mainTarget.webSocketDebuggerUrl.replace('localhost', '127.0.0.1');
|
|
const ws = new WebSocket(wsUrl);
|
|
|
|
await new Promise((resolve) => ws.on('open', resolve));
|
|
console.log('--- STARTING FULL E2E VISUAL TEST SUITE ---');
|
|
|
|
await sendCDP(ws, 'Page.enable');
|
|
await sendCDP(ws, 'Runtime.enable');
|
|
|
|
const errors = [];
|
|
ws.on('message', (data) => {
|
|
try {
|
|
const msg = JSON.parse(data);
|
|
if (msg.method === 'Runtime.consoleAPICalled' && msg.params.type === 'error') {
|
|
const errText = msg.params.args.map(a => a.value || a.description || JSON.stringify(a)).join(' ');
|
|
console.error('[RENDERER CONSOLE ERROR DETECTED]:', errText);
|
|
errors.push(errText);
|
|
}
|
|
} catch(e){}
|
|
});
|
|
|
|
const captureScreen = async (filename) => {
|
|
try {
|
|
const shot = await sendCDP(ws, 'Page.captureScreenshot', { format: 'png' });
|
|
const targetPath = path.join(ARTIFACTS_DIR, filename);
|
|
fs.writeFileSync(targetPath, Buffer.from(shot.data, 'base64'));
|
|
console.log(`[Screen Captured] ${filename}`);
|
|
} catch(e) {
|
|
console.error(`Failed to capture ${filename}:`, e.message);
|
|
}
|
|
};
|
|
|
|
// 1. Initial Dashboard
|
|
await captureScreen('page_1_dashboard.png');
|
|
|
|
// 2. Dismiss modal
|
|
try {
|
|
await sendCDP(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const btns = Array.from(document.querySelectorAll('button'));
|
|
const laterBtn = btns.find(b => b.innerText.includes('나중에'));
|
|
if (laterBtn) laterBtn.click();
|
|
})()
|
|
`
|
|
});
|
|
} catch(e) {
|
|
console.error('Modal dismiss error:', e.message);
|
|
}
|
|
await new Promise(r => setTimeout(r, 600));
|
|
|
|
await captureScreen('page_1_dashboard_clean.png');
|
|
|
|
// 3. Test Navigation Tabs
|
|
const menuItems = ['히스토리', '사전', '명령어', '대화', '지식 베이스', '회의', '설정'];
|
|
for (let i = 0; i < menuItems.length; i++) {
|
|
const item = menuItems[i];
|
|
console.log(`Navigating to tab: ${item}`);
|
|
|
|
try {
|
|
await sendCDP(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const elements = Array.from(document.querySelectorAll('div, span, p, a, button'));
|
|
const el = elements.find(e => e.innerText && e.innerText.trim() === '${item}');
|
|
if (el) {
|
|
el.click();
|
|
return true;
|
|
}
|
|
return false;
|
|
})()
|
|
`
|
|
});
|
|
} catch(e) {
|
|
console.error(`Tab click error (${item}):`, e.message);
|
|
}
|
|
|
|
await new Promise(r => setTimeout(r, 800));
|
|
await captureScreen(`page_${i + 2}_${item.replace(/\s+/g, '_')}.png`);
|
|
}
|
|
|
|
// 4. Test Subtab
|
|
console.log('Testing Settings Sub-Tabs...');
|
|
try {
|
|
await sendCDP(ws, 'Runtime.evaluate', {
|
|
expression: `
|
|
(() => {
|
|
const tabs = Array.from(document.querySelectorAll('button, div[role="tab"]'));
|
|
if (tabs.length > 0) tabs[0].click();
|
|
})()
|
|
`
|
|
});
|
|
} catch(e){}
|
|
await new Promise(r => setTimeout(r, 500));
|
|
await captureScreen('page_settings_subtab.png');
|
|
|
|
console.log('--- E2E TEST SUITE FINISHED ---');
|
|
console.log('Total Console Errors Detected during flow:', errors.length);
|
|
|
|
ws.close();
|
|
}
|
|
|
|
runFullSuite().catch(err => {
|
|
console.error('Suite error:', err);
|
|
process.exit(1);
|
|
});
|