Files
ihaleist/tests/browser-acceptance.mjs
T

100 lines
7.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.
// 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: 'Pazar', exact: true }).click();
await page.getByRole('button', { name: 'Analitik Dashboard', exact: true }).click();
await page.getByLabel('Ciro hedefi (TL)', { exact: true }).waitFor({ state: 'attached', timeout: 10_000 });
evidence.checks.analyticsTargetLabel = await page.getByLabel('Ciro hedefi (TL)', { exact: true }).count() === 1;
if (!evidence.checks.analyticsTargetLabel) throw new Error('Analitik ciro hedefi alanı erişilebilir ad taşımıyor.');
await page.getByRole('button', { name: 'Keşfet', exact: true }).click();
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;
evidence.checks.icsStorageScoped = await page.evaluate(() => Object.keys(sessionStorage).some(key => key.startsWith('ihaleist_imported_calendar:')));
if (!evidence.checks.icsStorageScoped) throw new Error('ICS kaydı hesap kapsamlı sessionStorage anahtarı kullanmıyor.');
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));