diff --git a/docs/quality/evidence/qa-browser-acceptance-runtime.json b/docs/quality/evidence/qa-browser-acceptance-runtime.json new file mode 100644 index 0000000..533e9f1 --- /dev/null +++ b/docs/quality/evidence/qa-browser-acceptance-runtime.json @@ -0,0 +1,31 @@ +{ + "url": "http://127.0.0.1:5173/qa-signin", + "checks": { + "signInStatus": 200, + "visibleCities": [ + { + "city": "Giresun", + "count": 3 + }, + { + "city": "Kayseri", + "count": 3 + }, + { + "city": "Akdeniz", + "count": 3 + }, + { + "city": "Aksaray", + "count": 3 + }, + { + "city": "Tepecik", + "count": 3 + } + ], + "overviewStatus": 200, + "overviewFrames": 3 + }, + "scope": "Yerel sentetik QA; gerçek başvuru veya dış sağlayıcı çağrısı yapılmadı." +} diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 3ecb29b..2c489d5 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2104,3 +2104,9 @@ Doğrulama: ICS round-trip, İstanbul saat dönüşümü, kaçış karakterleri, İçe aktarılan harici etkinlikler artık `sessionStorage` içinde tutuluyor; kullanıcı takvim sekmesinden ayrılıp döndüğünde aynı tarayıcı oturumunda kayıtlar korunuyor. Temizleme düğmesi tüm harici etkinlikleri kaldırıyor; özel tarama modunda storage yazımı başarısız olursa uygulama akışı devam ediyor. Doğrulama: 292/292 otomatik test ve üretim derlemesi başarılı. + +### 2026-09-06 · Headless tarayıcı kabul koşucusu + +`npm run qa:browser` komutu geçici QA sunucusunu başlatıp Playwright ile giriş sayfasını, beş sentetik sağlık ihalesinin görünürlüğünü ve 390/768/1440 piksel hazırlık özeti çerçevelerini doğruluyor. Çalışma zamanı kanıtı [`qa-browser-acceptance-runtime.json`](evidence/qa-browser-acceptance-runtime.json) içinde tutuluyor; resmi ihale başvurusu değildir. + +Doğrulama: giriş HTTP 200, beş şehrin tamamı görünür (her biri 3 eşleşme), üç hazırlık özeti çerçevesi açıldı. diff --git a/package.json b/package.json index a3b682f..e24c1ed 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "qa:browser": "node tests/browser-acceptance.mjs", "preview": "vite preview", "server": "node server/index.js", "test": "node --test tests/*.test.mjs" diff --git a/tests/browser-acceptance.mjs b/tests/browser-acceptance.mjs new file mode 100644 index 0000000..3ac50a3 --- /dev/null +++ b/tests/browser-acceptance.mjs @@ -0,0 +1,37 @@ +// Local-only browser acceptance runner. It uses the disposable fixtures served by browser-qa.mjs. +import { spawn } from 'node:child_process'; +import { writeFile, mkdir } from 'node:fs/promises'; +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.'); + 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ı.'); +} 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));