bone_2026/tests/browser/ui_offline.js

101 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Проверка работы интерфейса в офлайне.
*
* Блокирует все запросы к внешним хостам: если оформление или иконки
* подгружаются с CDN, они отвалятся. Успешное выполнение означает, что
* страница самодостаточна и работает в контейнере без сети.
*
* Требуется запущенный сервер:
* python -m uvicorn src.main:app --port 8123
* node tests/browser/ui_offline.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 FILE = path.join(REPO, 'dataset_hack/Для теста/spine_01.dcm');
(async () => {
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dxa-offline-'));
const context = await chromium.launchPersistentContext(userDataDir, { channel: 'chrome', headless: true });
const page = context.pages()[0] || await context.newPage();
await page.setViewportSize({ width: 1400, height: 1100 });
const external = [];
const failed = [];
await context.route('**/*', (route) => {
const url = route.request().url();
const isLocal = url.startsWith(BASE) || url.startsWith('data:') || url.startsWith('blob:');
if (!isLocal) {
external.push(url);
return route.abort();
}
return route.continue();
});
page.on('requestfailed', (r) => failed.push(r.url()));
await page.goto(BASE, { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(1500);
// Оформление применилось? Проверяем вычисленные стили, а не наличие файла.
const styling = await page.evaluate(() => {
const drop = document.getElementById('dropZone');
const cs = getComputedStyle(drop);
const icon = document.querySelector('i.fas');
const iconFont = icon ? getComputedStyle(icon).fontFamily : null;
const iconWidth = icon ? icon.getBoundingClientRect().width : 0;
return {
dropZoneBorderRadius: cs.borderRadius,
dropZoneDisplay: cs.display,
iconFontFamily: iconFont,
iconWidth,
};
});
console.log('styling:', styling);
// Загрузка файла и открытие панели деталей в офлайне
await page.setInputFiles('#fileInput', [FILE]);
await page.waitForFunction(() => document.querySelectorAll('#resultsTable tr').length === 1, { timeout: 60000 });
await page.locator('#resultsTable tr').first().click();
await page.waitForTimeout(1200);
const panel = await page.evaluate(() => ({
visible: !document.getElementById('detailSection').classList.contains('hidden'),
basic: (document.getElementById('detailBasic') || {}).innerText?.replace(/\s+/g, ' ').trim(),
reason: (document.getElementById('detailReason') || {}).innerText?.replace(/\s+/g, ' ').trim().slice(0, 70),
images: document.querySelectorAll('#detailImage img, #detailMask img').length,
}));
console.log('panel:', panel);
console.log('\nвнешние запросы (заблокированы):', external.length ? external : 'нет');
console.log('проваленные запросы:', failed.length ? failed : 'нет');
const ok =
styling.dropZoneBorderRadius !== '0px' &&
panel.visible && panel.images === 2 &&
external.length === 0;
console.log('\nОФЛАЙН-ПРОВЕРКА:', ok ? 'ПРОЙДЕНА' : 'НЕ ПРОЙДЕНА');
await context.close();
fs.rmSync(userDataDir, { recursive: true, force: true });
process.exit(ok ? 0 : 1);
})().catch((e) => { console.error('FAILED:', e.message); process.exit(1); });