142 lines
5.8 KiB
JavaScript
142 lines
5.8 KiB
JavaScript
#!/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);
|
|
})();
|