#!/usr/bin/env node /** * template.js — шаблон теста для нового проекта * * Использование: * CHROME_BIN=/usr/bin/chromium-browser node template.js * TEST_URL=https://example.com node template.js */ const puppeteer = require('puppeteer-core'); const fs = require('fs'); const path = require('path'); // ─── Конфиг ─────────────────────────────────────────────────────────────────── const CONFIG = { url: process.env.TEST_URL || 'https://example.com', chromeBin: process.env.CHROME_BIN || '/usr/bin/chromium-browser', screenshotsDir: process.env.SCREENSHOTS_DIR || '/tmp/ui-test-screenshots', resultsFile: process.env.RESULTS_FILE || '/tmp/ui-test-results.json', viewportDesktop: { width: 1280, height: 800 }, viewportMobile: { width: 375, height: 667 }, defaultTimeout: 15000, }; // ─── Результаты ─────────────────────────────────────────────────────────────── const results = []; function pass(id, note) { results.push({ id, status: 'PASS', note }); console.log(`✅ ${id}: ${note}`); } function fail(id, note, screenshotPath) { results.push({ id, status: 'FAIL', note, screenshot: screenshotPath }); console.log(`❌ ${id}: ${note}`); } function blocked(id, note) { results.push({ id, status: 'BLOCKED', note }); console.log(`⚠️ ${id}: ${note}`); } // ─── Хелперы ────────────────────────────────────────────────────────────────── async function screenshot(page, name) { const filePath = path.join(CONFIG.screenshotsDir, `${name}.png`); await page.screenshot({ path: filePath, fullPage: false }); console.log(` 📸 ${name}.png`); return filePath; } async function waitAndClick(page, selector, timeout = CONFIG.defaultTimeout) { await page.waitForSelector(selector, { timeout }); await page.click(selector); } async function getText(page, selector) { return page.$eval(selector, el => el.textContent.trim()).catch(() => null); } async function isVisible(page, selector) { return page.$eval(selector, el => { const style = getComputedStyle(el); return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0'; }).catch(() => false); } async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } // ─── Тесты ──────────────────────────────────────────────────────────────────── async function runTests(page) { // TC-TEMPLATE-01: страница загружается await page.goto(CONFIG.url, { waitUntil: 'networkidle2', timeout: 30000 }); await sleep(2000); const snap1 = await screenshot(page, 'TC-01-initial'); const title = await page.title(); title ? pass('TC-TEMPLATE-01', `title: "${title}"`) : fail('TC-TEMPLATE-01', 'страница не загрузилась', snap1); // TC-TEMPLATE-02: пример проверки элемента const header = await page.$('h1, header'); header ? pass('TC-TEMPLATE-02', 'заголовок найден') : fail('TC-TEMPLATE-02', 'заголовок не найден'); // Добавляй свои тест-кейсы здесь... } // ─── Главная функция ────────────────────────────────────────────────────────── (async () => { fs.mkdirSync(CONFIG.screenshotsDir, { recursive: true }); console.log(`\n🧪 UI Tests`); console.log(` URL: ${CONFIG.url}`); console.log(` Chrome: ${CONFIG.chromeBin}`); console.log(` Screenshots: ${CONFIG.screenshotsDir}\n`); const browser = await puppeteer.launch({ executablePath: CONFIG.chromeBin, headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu'], }); try { const page = await browser.newPage(); await page.setViewport(CONFIG.viewportDesktop); // Desktop тесты await runTests(page); // Mobile тесты await page.setViewport(CONFIG.viewportMobile); await page.goto(CONFIG.url, { waitUntil: 'networkidle2', timeout: 20000 }); await sleep(1500); await screenshot(page, 'TC-MOBILE-initial'); pass('TC-MOBILE', 'мобильный вид загружен'); } finally { await browser.close(); } // ─── Итог ───────────────────────────────────────────────────────────────── const passed = results.filter(r => r.status === 'PASS').length; const failed = results.filter(r => r.status === 'FAIL').length; const blockedN = results.filter(r => r.status === 'BLOCKED').length; console.log('\n═══════════════════════════════'); console.log(`ИТОГО: ✅ ${passed} / ❌ ${failed} / ⚠️ ${blockedN}`); if (failed > 0) { console.log('\nFAILED:'); results.filter(r => r.status === 'FAIL').forEach(r => console.log(` ❌ ${r.id}: ${r.note}${r.screenshot ? ` → ${r.screenshot}` : ''}`) ); } fs.writeFileSync(CONFIG.resultsFile, JSON.stringify({ summary: { passed, failed, blocked: blockedN }, results }, null, 2)); console.log(`\nРезультаты: ${CONFIG.resultsFile}`); console.log(`Скриншоты: ${CONFIG.screenshotsDir}/`); process.exit(failed > 0 ? 1 : 0); })();