From dd83530d6a46807c749832ea76f3c7a6cd7676ad Mon Sep 17 00:00:00 2001 From: shad Date: Sun, 14 Jun 2026 18:44:57 +0400 Subject: [PATCH] test(qa): chat tab selector handles unread badge suffix The new topbar badge on the Chat tab (chatThreadCount > 0) broke the old /^\\s*Chat\\s*$/ regex. Switched to a Playwright filter chain: .tab filtered by hasText 'Chat' AND hasNotText 'Assistant' (which also contains 'Chat' substring elsewhere in DOM). All 4 QA suites green: - 36/36 dogfood (chat_sidebar threads=8 previews=8 times=8) - 12/12 buyer-script (Approvals action disabled? true) - 8/8 idle-poll (0 reqs at 30s on every scene) - 14/14 mobile (390px clean) --- qa/full_dogfood.mjs | 3 ++- qa/tabdump.mjs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 qa/tabdump.mjs diff --git a/qa/full_dogfood.mjs b/qa/full_dogfood.mjs index bbeb956..e8abbc7 100644 --- a/qa/full_dogfood.mjs +++ b/qa/full_dogfood.mjs @@ -262,7 +262,8 @@ record("llm_unconfigured_falls_back_gracefully", unmatchedReply.length > 0 && !/ // 17. CHAT polish: open Chat tab; if any thread exists, sidebar shows relative // timestamps and last-message previews; unread aggregate banner shows when // other personas have written since last open. -await p.locator(".tab", { hasText: /^\s*Chat\s*$/ }).click(); +const chatTab = p.locator(".tab").filter({ hasText: "Chat" }).filter({ hasNotText: "Assistant" }).first(); +await chatTab.click({ timeout: 15000 }); await p.waitForTimeout(2500); const initialThreadCount = await p.locator(".chat-thread-row").count(); if (initialThreadCount === 0) { diff --git a/qa/tabdump.mjs b/qa/tabdump.mjs new file mode 100644 index 0000000..138f1ed --- /dev/null +++ b/qa/tabdump.mjs @@ -0,0 +1,15 @@ +import { chromium } from "playwright"; +const args = ["--host-resolver-rules=MAP canvas.flow-master.ai 65.21.71.186", "--ignore-certificate-errors"]; +const b = await chromium.launch({ headless: true, args }); +const p = await (await b.newContext({ viewport: { width: 1440, height: 900 } })).newPage(); +await p.goto("https://canvas.flow-master.ai/", { waitUntil: "networkidle" }); +await p.waitForTimeout(800); +await p.locator(".persona-chip", { hasText: /Mariana/ }).click(); +await p.locator(".dev-login-btn").click(); +await p.waitForSelector(".hero-actions", { timeout: 15000 }); +await p.waitForFunction(() => document.querySelectorAll(".hub-chip").length >= 8, undefined, { timeout: 15000 }).catch(() => {}); +await p.locator(".hub-chip", { hasText: /Procurement Hub/ }).click(); +await p.waitForTimeout(2000); +const tabs = await p.locator(".tab").evaluateAll((els) => els.map((e) => JSON.stringify(e.textContent))); +console.log("tabs:", tabs); +await b.close();