diff --git a/docs/quality/evidence/qa-browser-acceptance-runtime.json b/docs/quality/evidence/qa-browser-acceptance-runtime.json index 17d0d25..43689ca 100644 --- a/docs/quality/evidence/qa-browser-acceptance-runtime.json +++ b/docs/quality/evidence/qa-browser-acceptance-runtime.json @@ -33,6 +33,11 @@ "outlookStart": "2026-09-10T07:00:00Z", "outlookEnd": "2026-09-10T07:00:00Z" }, + "calendarMissingTimeGuard": { + "googleDisabled": true, + "outlookDisabled": true, + "providerLinks": 0 + }, "analyticsTargetLabel": true, "accountScopedAnalyticsTarget": true, "primaryAnalyticsTargetRestored": true, diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index f92169c..0fb67b5 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2332,3 +2332,9 @@ Doğrulama: `npm test` 301/301, `npm run build` başarılı ve `npm run qa:brows Takvim bağlantıları artık yalnız servis birim testlerinde değil, ihale detay modalının gerçek DOM akışında da kontrol ediliyor. Yerel QA fikstürü kaynak tarihi 10.09.2026 10:00 İstanbul olan bir ihale açıyor; Google bağlantısında `20260910T070000Z`, Outlook bağlantısında `2026-09-10T07:00:00Z` başlangıç/bitiş değerleri ve doğru provider hostları doğrulanıyor. Doğrulama: `npm run qa:browser` başarılı; kanıt [`qa-browser-acceptance-runtime.json`](evidence/qa-browser-acceptance-runtime.json) içindeki `calendarProviderLinks` alanında. Bu kabul sentetik ve salt okunur; OAuth hesabına kaydetme ve zeyilname sonrası gerçek senkronizasyon açık iş olarak kalıyor. + +### 2026-09-06 · Eksik saat için takvim bağlantısı güvenliği + +Kaynakta son teklif saati bulunmadığında Google/Outlook bağlantıları artık `#` gibi sahte bir hedefe gitmiyor; pasif seçenek ve açık “saat gerekli” açıklaması gösteriliyor. Geçerli tarih/saat akışı ile eksik saat akışı ayrı browser kontrollerine ayrıldı. + +Doğrulama: `calendarMissingTimeGuard` içinde iki provider seçeneği pasif ve gerçek provider bağlantısı sayısı 0; `npm run qa:browser` ve `npm test` (301/301) başarılı. diff --git a/src/components/TenderDetailModal.jsx b/src/components/TenderDetailModal.jsx index af46669..d4c8585 100644 --- a/src/components/TenderDetailModal.jsx +++ b/src/components/TenderDetailModal.jsx @@ -440,17 +440,26 @@ export const TenderDetailModal = () => { Takvim Dosyasını İndir (.ics)
- handleCalendarOpen('google', event)} - className="text-center bg-white hover:bg-blue-50 text-blue-700 font-bold py-2 px-3 rounded-lg text-xs border border-blue-200"> - Google Takvim taslağı - - handleCalendarOpen('outlook', event)} - className="text-center bg-white hover:bg-blue-50 text-blue-700 font-bold py-2 px-3 rounded-lg text-xs border border-blue-200"> - Outlook taslağı - + {calendarUrls.google ? ( + handleCalendarOpen('google', event)} + className="text-center bg-white hover:bg-blue-50 text-blue-700 font-bold py-2 px-3 rounded-lg text-xs border border-blue-200"> + Google Takvim taslağı + + ) : ( + Google Takvim (saat gerekli) + )} + {calendarUrls.outlook ? ( + handleCalendarOpen('outlook', event)} + className="text-center bg-white hover:bg-blue-50 text-blue-700 font-bold py-2 px-3 rounded-lg text-xs border border-blue-200"> + Outlook taslağı + + ) : ( + Outlook (saat gerekli) + )}
+ {calendarUrls.error &&

Takvim taslağı için kaynak tarih ve saat birlikte gerekli.

}

Google ve Outlook bağlantıları doğrulanmış tarih/saatle ön-doldurulmuş taslak açar; kaydetme ve değişiklik sonrası yeniden senkronizasyon kullanıcıya aittir. Otomatik OAuth senkronu yapılmaz.

diff --git a/tests/browser-acceptance.mjs b/tests/browser-acceptance.mjs index 7687662..4772ee1 100644 --- a/tests/browser-acceptance.mjs +++ b/tests/browser-acceptance.mjs @@ -55,6 +55,18 @@ try { outlookUrl.searchParams.get('enddt') !== '2026-09-10T07:00:00Z') { throw new Error(`Takvim provider bağlantısı kaynak son tarihi korumuyor: ${JSON.stringify(evidence.checks.calendarProviderLinks)}`); } + const missingCalendarPage = await context.newPage(); + try { + await missingCalendarPage.goto(`${url.replace(/\/qa-signin$/, '')}/qa-calendar-links?missing=1`, { waitUntil: 'networkidle' }); + evidence.checks.calendarMissingTimeGuard = { + googleDisabled: await missingCalendarPage.getByText('Google Takvim (saat gerekli)', { exact: true }).count() === 1, + outlookDisabled: await missingCalendarPage.getByText('Outlook (saat gerekli)', { exact: true }).count() === 1, + providerLinks: await missingCalendarPage.locator('a').filter({ hasText: /Google Takvim taslağı|Outlook taslağı/ }).count(), + }; + if (!evidence.checks.calendarMissingTimeGuard.googleDisabled || !evidence.checks.calendarMissingTimeGuard.outlookDisabled || evidence.checks.calendarMissingTimeGuard.providerLinks !== 0) { + throw new Error(`Saat bilinmiyorken takvim bağlantısı pasifleşmedi: ${JSON.stringify(evidence.checks.calendarMissingTimeGuard)}`); + } + } finally { await missingCalendarPage.close(); } } finally { await calendarPage.close(); } await page.getByRole('button', { name: 'Ana İhale Listesi', exact: true }).click(); await page.getByRole('button', { name: 'Pazar', exact: true }).click(); diff --git a/tests/browser-qa.mjs b/tests/browser-qa.mjs index 7a4844c..dc40151 100644 --- a/tests/browser-qa.mjs +++ b/tests/browser-qa.mjs @@ -140,8 +140,9 @@ const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{targ `; res.setHeader('Content-Type','text/html; charset=utf-8');res.end(await server.transformIndexHtml('/qa-overview-frame',html));return; } - if(req.url==='/qa-calendar-links'){ - const calendarTender={...tenders[0],ikn:'2026/1582991',tarih:'10.09.2026',saat:'10:00',baslik:'QA Giresun mikroskop',kurum:'Giresun TEST KURUMU',ihaleYeri:'QA toplantı salonu'}; + if(req.url==='/qa-calendar-links'||req.url?.startsWith('/qa-calendar-links?')){ + const missingTime=new URL(req.url,'http://localhost').searchParams.has('missing'); + const calendarTender={...tenders[0],id:missingTime?'qa-calendar-missing':'qa-calendar-valid',ikn:'2026/1582991',tarih:'10.09.2026',saat:missingTime?'':'10:00',aciklama:missingTime?'':'Kaynak tarihi doğrulama',baslik:'QA Giresun mikroskop',kurum:'Giresun TEST KURUMU',ihaleYeri:'QA toplantı salonu'}; const html=`
`; - res.setHeader('Content-Type','text/html; charset=utf-8');res.end(await server.transformIndexHtml('/qa-calendar-links',html));return; + res.setHeader('Content-Type','text/html; charset=utf-8');res.end(await server.transformIndexHtml(missingTime?'/qa-calendar-links-missing':'/qa-calendar-links',html));return; } if(req.url==='/qa-source-update'||req.url==='/qa-source-update?ready=1'||req.url==='/qa-source-update?ready=1&file=1') { const query=new URL(req.url,'http://localhost').searchParams;