| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
- const assert = require('node:assert/strict');
- const fs = require('node:fs');
- const root = process.env.UI_TEST_ARTIFACTS || '/tmp/debtik-ui-tests';
- fs.mkdirSync(root,{recursive:true});
- (async () => {
- const browser = await chromium.launch({headless:true,args:['--no-sandbox'],env:process.env});
- const context = await browser.newContext({viewport:{width:1440,height:1000}});
- const page = await context.newPage();
- const errors = [], consoleErrors = [], badResponses = [];
- page.on('pageerror', e => errors.push(e.message));
- page.on('console', m => { if(m.type()==='error') consoleErrors.push(m.text()); });
- page.on('response', r => { if(r.status()>=400) badResponses.push({path:new URL(r.url()).pathname,status:r.status()}); });
- const results = [];
- try {
- await page.goto(process.env.DEBTIK_TEST_URL || 'http://127.0.0.1:8080', {waitUntil:'domcontentloaded'});
- await page.locator('#login-username').fill(process.env.DEBTIK_TEST_USER || 'admin');
- await page.locator('#login-password').fill(process.env.DEBTIK_TEST_PASSWORD || 'admin');
- await page.locator('#login-submit-btn').click();
- await page.waitForFunction(() => state.currentUser !== null);
- await page.evaluate(() => fetchData(false));
- const tabs = ['dashboard','interfaces','bridge','firewall','routes','bgp','ospf','routing-filters','ppp','eoip','aerotun','pppoe','wireguard','ipsec','l2tp','pptp','dhcp-client','dhcp-server','vrrp','tools','bwtest','settings','users','certificates','services','updates','logs'];
- for(const tab of tabs) {
- await page.evaluate(async tab => { navigate(tab); await fetchData(false); }, tab);
- await page.waitForTimeout(200);
- assert.equal(await page.locator('#section-'+tab).isVisible(),true,tab+' not visible');
- results.push('view '+tab);
- }
- await page.evaluate(() => {
- state.bgpInstance={enabled:true,running:true,as:64512};renderBgpStatusPill();
- });
- const pills=await page.locator('.bgp-live-status-pill').allTextContents();
- assert.equal(pills.length,2);assert.ok(pills.every(t=>t==='BGP ACTIVE (AS 64512)'));
- results.push('both BGP badges update');
- await page.evaluate(() => {
- const sample = i => ({elapsed_sec:i/2,tx_mbps:1,rx_mbps:2,tx_bytes:i*100,rx_bytes:i*200,tx_packets:i,rx_packets:i,loss_pct:0,jitter_ms:0,timestamp:new Date().toISOString()});
- processBwTestStatus({session_id:'audit',running:true,recent_samples:Array.from({length:200},(_,i)=>sample(i+1))});
- processBwTestStatus({session_id:'audit',running:true,recent_samples:Array.from({length:200},(_,i)=>sample(i+2))});
- });
- assert.equal(await page.evaluate(()=>bwtestState.lastSampleElapsed),100.5);
- results.push('rolling sample window continues after 200 samples');
- let count=0;
- await page.route('**/api/review/dedup', async route => {count++;await new Promise(r=>setTimeout(r,100));await route.fulfill({json:{ok:true}})});
- const values=await page.evaluate(async()=>Promise.all([fetch('/api/review/dedup').then(r=>r.json()),fetch('/api/review/dedup').then(r=>r.json())]));
- assert.equal(count,1);assert.deepEqual(values,[{ok:true},{ok:true}]);
- results.push('concurrent GET deduplication and response copies');
- await page.route('**/api/tools/bwtest/stop',route=>route.fulfill({status:403,json:{message:'Denied for review'}}));
- await page.evaluate(async()=>{document.getElementById('toast-container').innerHTML='';await stopBwTest()});
- assert.match(await page.locator('#toast-container').innerText(),/Stop Failed/);
- assert.doesNotMatch(await page.locator('#toast-container').innerText(),/Stop Requested/);
- results.push('rejected Stop is reported as failure');
- await page.route('**/api/review/failure',route=>route.fulfill({status:503,body:'unavailable'}));
- await page.evaluate(()=>fetch('/api/review/failure'));
- assert.match(await page.locator('#telemetry-health').innerText(),/unavailable/);
- await page.unroute('**/api/review/failure');
- await page.route('**/api/review/failure',route=>route.fulfill({json:{ok:true}}));
- await page.evaluate(()=>fetch('/api/review/failure'));
- assert.doesNotMatch(await page.locator('#telemetry-health').getAttribute('title'),/review\/failure/);
- results.push('failed telemetry visible and cleared after recovery');
- await page.route('**/api/vpn/aerotun/identity-drafts', route=>route.fulfill({json:{draft_id:'audit-identity',public_key:'audit-public-key'}}));
- await page.evaluate(()=>{navigate('aerotun');openAddAeroTUNModal()});
- await page.waitForFunction(()=>document.getElementById('aerotun-modal-psk').value.length>0);
- await page.waitForFunction(()=>document.getElementById('aerotun-modal-public').value==='audit-public-key');
- assert.match(await page.locator('#aerotun-setup-hint').innerText(),/Copy the local public key/);
- assert.equal(await page.locator('#aerotun-modal-disabled').isChecked(),true);
- await page.locator('#aerotun-modal-peer').fill('10.12.0.9/24');
- assert.equal(await page.locator('#aerotun-modal-allowed').inputValue(),'10.12.0.9/32');
- await page.locator('#aerotun-modal-allowed').fill('192.0.2.0/24');
- await page.locator('#aerotun-modal-peer').fill('10.12.0.10/24');
- assert.equal(await page.locator('#aerotun-modal-allowed').inputValue(),'192.0.2.0/24');
- const existing = await page.evaluate(() => state.aerotunTunnels || []);
- let draft;
- await page.route('**/api/vpn/aerotun/tunnels', async route => {
- if (route.request().method() === 'POST') {
- const request = route.request().postDataJSON();
- assert.equal(request.disabled,true);
- assert.equal(request.protocol_version,3);
- assert.equal(request.identity_draft_id,'audit-identity');
- assert.equal('private_key' in request,false);
- draft = {...request,id:'audit-draft',public_key:'audit-public-key',has_psk:true};
- delete draft.psk;
- await route.fulfill({json:draft});
- } else await route.fulfill({json:[...existing,...(draft?[draft]:[])]});
- });
- await page.evaluate(()=>submitSaveAeroTUN({preventDefault(){}}));
- assert.equal(await page.locator('#aerotun-modal-psk').inputValue(),'');
- assert.equal(await page.locator('#aerotun-modal').isVisible(),false);
- results.push('AeroTUN public key available before save and draft identity submitted');
- await page.screenshot({path:root+'/aerotun-setup.png'});
- results.push('AeroTUN guided setup and disabled default');
- await page.evaluate(()=>{navigate('bwtest');openBwTestServerModal()});
- await page.locator('#bwtest-server-port-input').fill('34567');
- await page.evaluate(()=>fetchBwTestServerStatus());
- assert.equal(await page.locator('#bwtest-server-port-input').inputValue(),'34567');
- await page.evaluate(()=>closeBwTestServerModal());
- results.push('telemetry preserves unsaved server modal values');
- await page.evaluate(()=>handleLogoutClick());
- assert.equal(await page.evaluate(()=>!!window._telemetryInterval || !!trafficEventSource || !!trafficPollFallback || !!bwtestState.pollTimer || !!logsTimer || !!state.fwStatInterval || !!state.natStatInterval || !!state.mangleStatInterval),false);
- results.push('logout stops all session pollers');
- assert.deepEqual(errors,[],'browser runtime errors');
- assert.ok(consoleErrors.every(e => e.startsWith('Failed to load resource:')), 'Unexpected application console error');
- assert.ok(badResponses.every(r => ['/api/auth/me','/api/tools/bwtest/stop','/api/review/failure'].includes(r.path)), 'Unexpected failed API request');
- console.log(JSON.stringify({passed:results,errors,consoleErrors,badResponses},null,2));
- } catch(error) {
- await page.screenshot({path:root+'/browser-failure.png'}).catch(()=>{});
- console.log(JSON.stringify({passed:results,errors,consoleErrors,badResponses},null,2));
- throw error;
- } finally { await browser.close(); }
- })().catch(error=>{console.error(error);process.exitCode=1});
|