diff --git a/nginx.conf b/nginx.conf index 6eed15d..a3737a1 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,5 +1,5 @@ map $sent_http_content_type $csp_header { - default "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self' https://demo.flow-master.ai https://canvas.flow-master.ai wss://canvas.flow-master.ai; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'"; + default "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: blob: https://*.tile.openstreetmap.org https://unpkg.com; connect-src 'self' https://demo.flow-master.ai https://canvas.flow-master.ai wss://canvas.flow-master.ai; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'"; } server { diff --git a/qa/full_dogfood.mjs b/qa/full_dogfood.mjs index 7535bf5..9c6c747 100644 --- a/qa/full_dogfood.mjs +++ b/qa/full_dogfood.mjs @@ -264,11 +264,31 @@ record("llm_unconfigured_falls_back_gracefully", unmatchedReply.length > 0 && !/ // other personas have written since last open. await p.locator(".tab", { hasText: /^\s*Chat\s*$/ }).click(); await p.waitForTimeout(2500); -await p.waitForTimeout(2000); +// First, ensure at least one thread exists by opening one with the HR persona +// if the sidebar happens to be empty. This makes the assertion non-vacuous. +const initialThreadCount = await p.locator(".chat-thread-row").count(); +if (initialThreadCount === 0) { + const sel = p.locator(".chat-new-row select").first(); + if (await sel.count()) { + await sel.selectOption("hr-head@flow-master.ai").catch(() => {}); + await p.locator(".chat-new-row button", { hasText: /Open/ }).click().catch(() => {}); + await p.waitForSelector(".chat-thread-row", { timeout: 8000 }).catch(() => {}); + } + const ta = p.locator(".chat-composer textarea").first(); + if (await ta.count()) { + await ta.fill(`qa seed message ${Date.now()}`); + await p.locator(".chat-composer button", { hasText: /Send/i }).click().catch(() => {}); + await p.waitForTimeout(2500); + } +} +await p.waitForFunction(() => document.querySelectorAll(".chat-thread-row").length >= 1, undefined, { timeout: 12000 }).catch(() => {}); +await p.waitForTimeout(1500); const threadRowCount = await p.locator(".chat-thread-row").count(); const previewRows = await p.locator(".chat-thread-meta").count(); const timeBadges = await p.locator(".chat-thread-time").count(); -const sidebarHonest = threadRowCount === 0 || (previewRows === threadRowCount && timeBadges >= 1); +// Strict: at least one thread, every thread has a preview, and the timestamp +// count equals the thread count (one per row). +const sidebarHonest = threadRowCount >= 1 && previewRows === threadRowCount && timeBadges === threadRowCount; record( "chat_sidebar_shows_previews_and_timestamps", sidebarHonest,