/** * Проверка панели деталей на снимках с нарушением качества. * * Все файлы из `Для теста` модель считает качественными, поэтому ветка * «нарушение» (бейдж, overall_quality=POOR, severity=HIGH, текст заключения) * иначе остаётся непроверенной. * * Требуется запущенный сервер: * python -m uvicorn src.main:app --port 8123 * node tests/browser/ui_violation.js * * Переменные окружения: BASE_URL, PLAYWRIGHT (см. ui_check.js). */ const fs = require('fs'); const path = require('path'); const os = require('os'); function resolvePlaywright() { if (process.env.PLAYWRIGHT) return process.env.PLAYWRIGHT; const base = path.join(os.homedir(), '.qwen', 'updates', 'npm'); if (!fs.existsSync(base)) return 'playwright-core'; const versions = fs.readdirSync(path.join(base, fs.readdirSync(base)[0], 'versions')).sort(); for (const v of versions.reverse()) { const candidate = path.join( base, fs.readdirSync(base)[0], 'versions', v, 'node_modules/@qwen-code/qwen-code/bundled/browser-use/runtime/node_modules/playwright-core' ); if (fs.existsSync(candidate)) return candidate; } return 'playwright-core'; } const { chromium } = require(resolvePlaywright()); const BASE = process.env.BASE_URL || 'http://127.0.0.1:8123'; const REPO = path.resolve(__dirname, '..', '..'); const STUDY = 'dataset_hack/НД_для_обучения/Исследования'; // Два размеченных нарушения из обучающего набора (spine и hip_right). const VIOLATIONS = [ path.join(REPO, STUDY, '2.25.11175860580562939441493697221497597640/series_003_2_CR/A2507431060 DXA/CR DXA/spine_01_bad.dcm'), path.join(REPO, STUDY, '2.25.12798473087614376830819854616447908612/series_002_2_CR/A2507538847 DXA/CR DXA/r_hip_01_bad.dcm'), ]; (async () => { const userDataDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'dxa-ui2-')); const context = await chromium.launchPersistentContext(userDataDir, { channel: 'chrome', headless: true }); const page = context.pages()[0] || await context.newPage(); await page.setViewportSize({ width: 1400, height: 1200 }); const errors = []; page.on('pageerror', (e) => errors.push('PAGEERROR ' + e.message)); await page.goto(BASE, { waitUntil: 'domcontentloaded' }); await page.setInputFiles('#fileInput', VIOLATIONS); await page.waitForFunction( () => document.querySelectorAll('#resultsTable tr').length === 2, { timeout: 60000 } ); console.log('stats:', await page.evaluate(() => ({ total: document.getElementById('totalCount').innerText, ok: document.getElementById('okCount').innerText, violation: document.getElementById('violationCount').innerText, }))); for (const idx of [0, 1]) { await page.locator('#resultsTable tr').nth(idx).click(); await page.waitForTimeout(1200); const s = await page.evaluate(() => { const t = (id) => (document.getElementById(id) || {}).innerText?.replace(/\s+/g, ' ').trim(); const cards = (id) => Array.from((document.getElementById(id) || {}).children || []) .map((c) => c.innerText.replace(/\s+/g, ' ').trim()); return { badge: t('detailBadge'), quality: t('detailQuality'), overall: t('detailOverall'), reason: t('detailReason'), roi: cards('detailArtifacts'), measurements: cards('detailMotion'), spine: t('detailSpine'), hip: t('detailHip'), spineHidden: document.getElementById('spineSection').classList.contains('hidden'), hipHidden: document.getElementById('hipSection').classList.contains('hidden'), }; }); console.log(`\n--- нарушение, строка ${idx} ---`); console.log(' badge:', s.badge); console.log(' quality:', s.quality); console.log(' overall:', s.overall); console.log(' roi:', s.roi); console.log(' measurements:', s.measurements); if (!s.spineHidden) console.log(' spine:', s.spine); if (!s.hipHidden) console.log(' hip:', s.hip); console.log(' reason:', s.reason); await page.locator('#detailSection').scrollIntoViewIfNeeded(); await page.screenshot({ path: path.join(os.tmpdir(), `dxa_violation${idx}.png`) }); } console.log('\npageerrors:', errors.length ? errors : 'none'); await context.close(); fs.rmSync(userDataDir, { recursive: true, force: true }); })().catch((e) => { console.error('FAILED:', e.message); process.exit(1); });