92 lines
6.5 KiB
JavaScript
92 lines
6.5 KiB
JavaScript
// Local-only browser acceptance runner. It uses the disposable fixtures served by browser-qa.mjs.
|
||
import { spawn } from 'node:child_process';
|
||
import { writeFile, mkdir, mkdtemp, rm } from 'node:fs/promises';
|
||
import { tmpdir } from 'node:os';
|
||
import { join } from 'node:path';
|
||
import { chromium } from 'playwright';
|
||
|
||
const server = spawn(process.execPath, ['tests/browser-qa.mjs'], { cwd: new URL('..', import.meta.url), stdio: ['ignore', 'pipe', 'inherit'] });
|
||
let output = '';
|
||
const url = await new Promise((resolve, reject) => {
|
||
const timeout = setTimeout(() => reject(new Error('QA sunucusu başlatılamadı.')), 30_000);
|
||
server.stdout.on('data', chunk => {
|
||
output += chunk;
|
||
const match = /QA_URL=(\S+)/.exec(output);
|
||
if (match) { clearTimeout(timeout); resolve(match[1]); }
|
||
});
|
||
server.once('exit', code => { clearTimeout(timeout); reject(new Error(`QA sunucusu ${code} koduyla kapandı.`)); });
|
||
});
|
||
|
||
const browser = await chromium.launch({ headless: true });
|
||
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
||
const evidence = { url, checks: {}, scope: 'Yerel sentetik QA; gerçek başvuru veya dış sağlayıcı çağrısı yapılmadı.' };
|
||
try {
|
||
const response = await page.goto(url, { waitUntil: 'networkidle' });
|
||
evidence.checks.signInStatus = response?.status();
|
||
await page.getByRole('button', { name: 'Test hesabıyla gir' }).click();
|
||
await page.waitForLoadState('networkidle');
|
||
evidence.checks.visibleCities = await Promise.all(['Giresun', 'Kayseri', 'Akdeniz', 'Aksaray', 'Tepecik'].map(async city => ({ city, count: await page.getByText(city, { exact: false }).count() })));
|
||
if (evidence.checks.visibleCities.some(item => item.count < 1)) throw new Error('Beş sentetik şehirden biri görünmedi.');
|
||
await page.getByRole('button', { name: 'İhale Takvimi' }).click();
|
||
await page.waitForLoadState('networkidle');
|
||
const icsDir = await mkdtemp(join(tmpdir(), 'ihaleist-ics-'));
|
||
const icsPath = join(icsDir, 'qa.ics');
|
||
await writeFile(icsPath, 'BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:qa-calendar\r\nDTSTART:20260910T070000Z\r\nSUMMARY:Harici Mikroskop\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n');
|
||
try {
|
||
await page.locator('input[aria-label="ICS takvim dosyası seç"]').setInputFiles(icsPath);
|
||
await page.getByText('1 harici etkinlik içe aktarıldı.', { exact: true }).waitFor();
|
||
evidence.checks.icsImported = true;
|
||
await page.getByRole('button', { name: 'Keşfet' }).click();
|
||
await page.getByRole('button', { name: 'İhale Takvimi' }).click();
|
||
evidence.checks.icsPersistsAfterTabChange = await page.getByText('1 harici etkinlik içe aktarıldı.', { exact: true }).count() === 1;
|
||
if (!evidence.checks.icsPersistsAfterTabChange) throw new Error('ICS kaydı sekme değişiminden sonra korunmadı.');
|
||
} finally { await rm(icsDir, { recursive: true, force: true }); }
|
||
const readyPage = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
||
try {
|
||
const readyResponse = await readyPage.goto(`${url.replace(/\/qa-signin$/, '')}/qa-source-update?ready=1&file=1`, { waitUntil: 'networkidle' });
|
||
evidence.checks.readyPreparationStatus = readyResponse?.status();
|
||
const outputButtons = readyPage.getByRole('button').filter({ hasText: /Teklif Mektubu|Birim Fiyat Teklif Cetveli|Excel Teklif Cetveli|Evrak Kontrol Listesi/ });
|
||
evidence.checks.readyOutputButtons = await outputButtons.count();
|
||
evidence.checks.readyOutputButtonsEnabled = await outputButtons.evaluateAll(buttons => buttons.every(button => !button.disabled));
|
||
if (evidence.checks.readyOutputButtons !== 4 || !evidence.checks.readyOutputButtonsEnabled) throw new Error('Hazır taslak çıktı düğmeleri etkin değil.');
|
||
const popupPromise = readyPage.waitForEvent('popup');
|
||
await readyPage.getByRole('button', { name: /Evrak Kontrol Listesi/ }).click();
|
||
const popup = await popupPromise;
|
||
await popup.waitForLoadState('domcontentloaded');
|
||
evidence.checks.printPopupTitle = await popup.title();
|
||
if (evidence.checks.printPopupTitle !== 'Evrak Kontrol Listesi') throw new Error('Yazdırma penceresi beklenen başlığı üretmedi.');
|
||
evidence.checks.printPopupTurkishContent = await popup.locator('h1').filter({ hasText: 'EVRAK KONTROL LİSTESİ' }).count() === 1;
|
||
evidence.checks.printPopupChecklistItems = await popup.locator('ul li').count();
|
||
if (!evidence.checks.printPopupTurkishContent || evidence.checks.printPopupChecklistItems < 1) throw new Error('Evrak çıktısı beklenen Türkçe başlık veya kontrol listesini içermiyor.');
|
||
await popup.emulateMedia({ media: 'print' });
|
||
evidence.checks.printButtonHidden = await popup.locator('.yazdirma').evaluate(container => getComputedStyle(container).display === 'none');
|
||
if (!evidence.checks.printButtonHidden) throw new Error('Yazdırma düğmesi print ortamında gizlenmedi.');
|
||
await popup.close();
|
||
} finally { await readyPage.close(); }
|
||
const overview = await page.goto(`${url.replace(/\/qa-signin$/, '')}/qa-overview`, { waitUntil: 'networkidle' });
|
||
evidence.checks.overviewStatus = overview?.status();
|
||
evidence.checks.overviewFrames = await page.locator('iframe[title^="Hazırlık"]').count();
|
||
if (evidence.checks.overviewFrames !== 3) throw new Error('Hazırlık özeti üç görünüm alanında açılmadı.');
|
||
const mobile = await browser.newPage({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 1 });
|
||
try {
|
||
const mobileResponse = await mobile.goto(url, { waitUntil: 'networkidle' });
|
||
evidence.checks.mobileStatus = mobileResponse?.status();
|
||
const mobileOverflow = await mobile.evaluate(() => ({
|
||
viewport: window.innerWidth,
|
||
scrollWidth: document.documentElement.scrollWidth,
|
||
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth + 1,
|
||
}));
|
||
evidence.checks.mobileOverflow = mobileOverflow;
|
||
if (mobileOverflow.horizontalOverflow) throw new Error('390px görünümünde yatay taşma var.');
|
||
await mobile.getByRole('button', { name: 'Test hesabıyla gir' }).click();
|
||
await mobile.waitForLoadState('networkidle');
|
||
evidence.checks.mobileTenderList = await mobile.getByLabel('İhale listesi').count() === 1;
|
||
if (!evidence.checks.mobileTenderList) throw new Error('390px görünümünde ihale listesi bulunamadı.');
|
||
} finally { await mobile.close(); }
|
||
} finally {
|
||
await page.close(); await browser.close(); server.kill('SIGTERM');
|
||
}
|
||
await mkdir('docs/quality/evidence', { recursive: true });
|
||
await writeFile('docs/quality/evidence/qa-browser-acceptance-runtime.json', JSON.stringify(evidence, null, 2) + '\n');
|
||
console.log(JSON.stringify(evidence));
|