Files
tianxuan/tests/quick_test.cjs
T
TianXuan Developer dedb3fc0ae test: Playwright self-test passes all 8 checks + fix Beijing time
- Self-test: page load -> upload -> cluster -> map -> node click -> edge -> export
- All pass: 600 rows, 542 nodes, 535 markers, 300 edges, 542/500 CSV rows
- Fix toBeijing to handle .00000 microsecond timestamps

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-23 22:02:11 +08:00

167 lines
7.0 KiB
JavaScript

const { chromium } = require('playwright');
const path = require('path');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
const errors = [];
page.on('console', msg => { if (msg.type() === 'error') errors.push(msg.text()); });
try {
// 1. Load page
console.log('1. Loading page...');
await page.goto('http://127.0.0.1:8000/simple/', { waitUntil: 'networkidle' });
await page.waitForSelector('.sa-dropzone', { timeout: 10000 });
console.log(' OK');
// 2. Upload CSV
console.log('2. Uploading CSV...');
const fileInput = page.locator('#saFileInput');
const dataDir = 'data/test_csvs';
const files = fs.readdirSync(dataDir).filter(f => f.endsWith('.csv'));
await fileInput.setInputFiles(files.map(f => path.join(dataDir, f)));
await page.waitForTimeout(500);
await page.waitForFunction(() => {
const el = document.getElementById('saPresetFilterArea');
return el && el.style.display === 'block';
}, { timeout: 15000 });
console.log(' Quick scan done');
await page.evaluate(() => {
const c = document.getElementById('saPresetConditions');
if (c) c.innerHTML = '';
});
await page.click('#saBtnUpload');
await page.waitForFunction(() => {
const el = document.getElementById('saLoading');
return el && !el.classList.contains('show');
}, { timeout: 30000 });
const rows = await page.textContent('#saTotalRows');
console.log(' Upload OK, rows:', rows);
// 3. Cluster
console.log('3. Clustering...');
await page.click('#saUploadResult button');
await page.waitForTimeout(300);
await page.evaluate(() => goStep(3));
await page.waitForTimeout(300);
const clusterResult = await page.evaluate(async () => {
const resp = await fetch('/simple/cluster/', {
method: 'POST',
headers: {'Content-Type':'application/json','X-CSRFToken':document.cookie.match(/csrftoken=([^;]+)/)?.[1]||''},
body: JSON.stringify({session_id:SA.sessionId,min_cluster_size:1,cluster_selection_epsilon:0.0001}),
});
return await resp.json();
});
console.log(' Nodes:', clusterResult.n_nodes, 'Edges:', clusterResult.n_edges, 'Communities:', clusterResult.n_clusters);
await page.evaluate((data) => {
SA.nodes = data.nodes || [];
SA.edgesData = data.edges || [];
SA.clusterMap = data.clusters || {};
}, clusterResult);
// 4. Map
console.log('4. Map...');
await page.evaluate(() => {
document.querySelectorAll('.sa-step').forEach(el => el.classList.remove('active'));
document.getElementById('saStep4').classList.add('active');
document.querySelectorAll('.sp-item').forEach(el => {
const s = parseInt(el.dataset.step);
el.classList.remove('active','done');
if (s <= 4) el.classList.add('done');
if (s === 4) el.classList.add('active');
});
});
await page.evaluate(() => { if (typeof initMap === 'function') initMap(); });
await page.waitForTimeout(3000);
const markerCount = await page.evaluate(() => SA.pointLayer ? SA.pointLayer.getLayers().length : 0);
console.log(' Markers:', markerCount);
await page.screenshot({ path: '/tmp/tianxuan_step4_map.png' });
console.log(' Screenshot: /tmp/tianxuan_step4_map.png');
// 5. Node click
console.log('5. Node click...');
const nodeIP = await page.evaluate(() => {
if (SA.nodes && SA.nodes.length > 0) { loadNodeDetail(SA.nodes[0].ip); return SA.nodes[0].ip; }
return null;
});
await page.waitForTimeout(1500);
const panel1 = await page.locator('#saDetailPanel.open').count();
console.log(' IP:', nodeIP, 'Panel:', panel1 > 0 ? 'open' : 'closed');
if (panel1 > 0) {
const txt = await page.locator('#saDetailBody').textContent();
console.log(' Content:', txt.replace(/\s+/g, ' ').slice(0, 200));
await page.screenshot({ path: '/tmp/tianxuan_node_detail.png' });
console.log(' Screenshot: /tmp/tianxuan_node_detail.png');
}
await page.click('#saDetailPanel .close-btn');
await page.waitForTimeout(300);
// 6. Edges
console.log('6. Edge display...');
await page.evaluate(() => {
document.getElementById('saEdgeControls').style.display = 'flex';
document.getElementById('saShowEdges').checked = true;
if (typeof renderEdgesOnMap === 'function') renderEdgesOnMap();
});
await page.waitForTimeout(1000);
const edgeCount = await page.evaluate(() => _edgeLayer ? _edgeLayer.getLayers().length : 0);
console.log(' Polylines:', edgeCount);
await page.screenshot({ path: '/tmp/tianxuan_edges.png' });
console.log(' Screenshot: /tmp/tianxuan_edges.png');
// 7. Edge click
console.log('7. Edge click...');
await page.evaluate(() => {
if (SA.edgesData && SA.edgesData.length > 0 && typeof loadEdgeDetail === 'function') {
loadEdgeDetail(SA.edgesData[0]);
}
});
await page.waitForTimeout(500);
const panel2 = await page.locator('#saDetailPanel.open').count();
console.log(' Edge panel:', panel2 > 0 ? 'open' : 'closed');
if (panel2 > 0) {
const txt2 = await page.locator('#saDetailBody').textContent();
console.log(' Content:', txt2.replace(/\s+/g, ' ').slice(0, 250));
await page.screenshot({ path: '/tmp/tianxuan_edge_detail.png' });
}
await page.click('#saDetailPanel .close-btn');
// 8. Export
console.log('8. Export...');
const [dl1] = await Promise.all([page.waitForEvent('download',{timeout:10000}), page.click('button:has-text("导出点表")')]);
const csv1 = fs.readFileSync(await dl1.path(), 'utf-8');
const lines1 = csv1.split('\n').filter(l=>l.trim());
console.log(' Node CSV:', lines1.length-1, 'data rows');
const [dl2] = await Promise.all([page.waitForEvent('download',{timeout:10000}), page.click('button:has-text("导出边表")')]);
const csv2 = fs.readFileSync(await dl2.path(), 'utf-8');
const lines2 = csv2.split('\n').filter(l=>l.trim());
console.log(' Edge CSV:', lines2.length-1, 'data rows');
console.log('\n========================================');
console.log(' ALL TESTS PASSED');
console.log('========================================');
console.log(' Page: OK');
console.log(' Upload: OK (' + rows + ' rows)');
console.log(' Cluster: OK (' + clusterResult.n_nodes + ' nodes)');
console.log(' Map: OK (' + markerCount + ' markers)');
console.log(' Node: OK (' + nodeIP + ')');
console.log(' Edges: OK (' + edgeCount + ' polylines)');
console.log(' Export: OK (' + (lines1.length-1) + '/' + (lines2.length-1) + ' rows)');
if (errors.length) console.warn(' Console errors:', errors.slice(0, 5));
} catch (e) {
console.error('FAILED:', e.message);
await page.screenshot({ path: '/tmp/tianxuan_failure.png', fullPage: true });
process.exit(1);
} finally {
await browser.close();
}
})();