guard calendar links when source time is missing
This commit is contained in:
@@ -33,6 +33,11 @@
|
|||||||
"outlookStart": "2026-09-10T07:00:00Z",
|
"outlookStart": "2026-09-10T07:00:00Z",
|
||||||
"outlookEnd": "2026-09-10T07:00:00Z"
|
"outlookEnd": "2026-09-10T07:00:00Z"
|
||||||
},
|
},
|
||||||
|
"calendarMissingTimeGuard": {
|
||||||
|
"googleDisabled": true,
|
||||||
|
"outlookDisabled": true,
|
||||||
|
"providerLinks": 0
|
||||||
|
},
|
||||||
"analyticsTargetLabel": true,
|
"analyticsTargetLabel": true,
|
||||||
"accountScopedAnalyticsTarget": true,
|
"accountScopedAnalyticsTarget": true,
|
||||||
"primaryAnalyticsTargetRestored": true,
|
"primaryAnalyticsTargetRestored": true,
|
||||||
|
|||||||
@@ -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.
|
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.
|
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ı.
|
||||||
|
|||||||
@@ -440,17 +440,26 @@ export const TenderDetailModal = () => {
|
|||||||
<Calendar className="w-4 h-4 text-blue-600" /> Takvim Dosyasını İndir (.ics)
|
<Calendar className="w-4 h-4 text-blue-600" /> Takvim Dosyasını İndir (.ics)
|
||||||
</button>
|
</button>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||||
<a href={calendarUrls.google || '#'} target="_blank" rel="noopener noreferrer"
|
{calendarUrls.google ? (
|
||||||
|
<a href={calendarUrls.google} target="_blank" rel="noopener noreferrer"
|
||||||
onClick={(event) => handleCalendarOpen('google', event)}
|
onClick={(event) => 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">
|
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 taslağı
|
||||||
</a>
|
</a>
|
||||||
<a href={calendarUrls.outlook || '#'} target="_blank" rel="noopener noreferrer"
|
) : (
|
||||||
|
<span aria-disabled="true" className="text-center bg-slate-50 text-slate-400 font-bold py-2 px-3 rounded-lg text-xs border border-slate-200">Google Takvim (saat gerekli)</span>
|
||||||
|
)}
|
||||||
|
{calendarUrls.outlook ? (
|
||||||
|
<a href={calendarUrls.outlook} target="_blank" rel="noopener noreferrer"
|
||||||
onClick={(event) => handleCalendarOpen('outlook', event)}
|
onClick={(event) => 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">
|
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 taslağı
|
||||||
</a>
|
</a>
|
||||||
|
) : (
|
||||||
|
<span aria-disabled="true" className="text-center bg-slate-50 text-slate-400 font-bold py-2 px-3 rounded-lg text-xs border border-slate-200">Outlook (saat gerekli)</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{calendarUrls.error && <p role="status" className="text-xs text-amber-800">Takvim taslağı için kaynak tarih ve saat birlikte gerekli.</p>}
|
||||||
<p className="text-xs text-slate-600 mt-2">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.</p>
|
<p className="text-xs text-slate-600 mt-2">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.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ try {
|
|||||||
outlookUrl.searchParams.get('enddt') !== '2026-09-10T07:00:00Z') {
|
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)}`);
|
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(); }
|
} finally { await calendarPage.close(); }
|
||||||
await page.getByRole('button', { name: 'Ana İhale Listesi', exact: true }).click();
|
await page.getByRole('button', { name: 'Ana İhale Listesi', exact: true }).click();
|
||||||
await page.getByRole('button', { name: 'Pazar', exact: true }).click();
|
await page.getByRole('button', { name: 'Pazar', exact: true }).click();
|
||||||
|
|||||||
@@ -140,8 +140,9 @@ const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{targ
|
|||||||
</script></body></html>`;
|
</script></body></html>`;
|
||||||
res.setHeader('Content-Type','text/html; charset=utf-8');res.end(await server.transformIndexHtml('/qa-overview-frame',html));return;
|
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'){
|
if(req.url==='/qa-calendar-links'||req.url?.startsWith('/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'};
|
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=`<html lang="tr"><head><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><div id="root"></div><script type="module">
|
const html=`<html lang="tr"><head><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><div id="root"></div><script type="module">
|
||||||
import React from '/node_modules/.vite/deps/react.js';
|
import React from '/node_modules/.vite/deps/react.js';
|
||||||
import ReactDOM from '/node_modules/.vite/deps/react-dom_client.js';
|
import ReactDOM from '/node_modules/.vite/deps/react-dom_client.js';
|
||||||
@@ -152,7 +153,7 @@ const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{targ
|
|||||||
function Probe(){const {setActiveTenderDetail,authUser}=useApp();React.useEffect(()=>{if(!authUser?.email)return;const timer=setTimeout(()=>setActiveTenderDetail(selected),0);return()=>clearTimeout(timer);},[authUser?.email]);return React.createElement(TenderDetailModal);}
|
function Probe(){const {setActiveTenderDetail,authUser}=useApp();React.useEffect(()=>{if(!authUser?.email)return;const timer=setTimeout(()=>setActiveTenderDetail(selected),0);return()=>clearTimeout(timer);},[authUser?.email]);return React.createElement(TenderDetailModal);}
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(AppProvider,{},React.createElement(Probe)));
|
ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(AppProvider,{},React.createElement(Probe)));
|
||||||
</script></body></html>`;
|
</script></body></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') {
|
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;
|
const query=new URL(req.url,'http://localhost').searchParams;
|
||||||
|
|||||||
Reference in New Issue
Block a user