-
-
-
-
+
+ {confidenceTotal > 0 ? (
+ <>
+ {counts.high ? : null}
+ {counts.medium ? (
+
+ ) : null}
+ {counts.low ? : null}
+ {counts.unknown ? (
+
+ ) : null}
+ >
+ ) : null}
@@ -424,15 +457,68 @@ function RightRail({
{counts.unknown}
+ {confidenceExpanded ? (
+
+ {matches.slice(0, 8).map((service) => (
+
+ {service.title}
+
+ {service.verification?.confidence ?? "Unknown"}
+
+
+ ))}
+ {matches.length > 8 ? (
+
+{matches.length - 8} more results
+ ) : null}
+
+ ) : null}
+ {comparisonExpanded ? (
+
+ {selected.map((service) => (
+
+
+
{service.title}
+
+ Open
+
+
+
+ {[
+ ["Contact", text(service.primaryContact?.value)],
+ ["Eligibility", text(service.eligibility, "Eligibility pending")],
+ ["Cost", text(service.cost, "Cost pending")],
+ ["Source", text(service.source?.status, "Source pending")],
+ ["Confidence", text(service.verification?.confidence, "Unknown")],
+ ].map(([label, value]) => (
+
+
- {label}
+ - {value}
+
+ ))}
+
+
+ ))}
+
+ ) : null}
);
}
diff --git a/tests/ui-accessibility.spec.ts b/tests/ui-accessibility.spec.ts
index e18ffd1c..6be58fad 100644
--- a/tests/ui-accessibility.spec.ts
+++ b/tests/ui-accessibility.spec.ts
@@ -48,7 +48,7 @@ async function expectNoPageHorizontalOverflow(page: Page) {
}
async function expectDashboardUsable(page: Page) {
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toHaveCount(1);
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toHaveCount(1);
await expect(page.getByRole("heading", { name: "Answer" })).toBeVisible();
await expect(page.locator('[aria-label^="Search indexed guidelines by question or keyword"]:visible')).toBeVisible();
await expect(page.getByRole("button", { name: "Open answer options" })).toBeVisible();
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index 6f997165..841b848a 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -19,7 +19,7 @@ const dashboardViewports = [
{ name: "laptop", width: 1280, height: 900 },
{ name: "mobile-landscape", width: 667, height: 375 },
] as const;
-const uiAssertionTimeoutMs = 5_000;
+const uiAssertionTimeoutMs = 30_000;
const demoAnswerThreadOwnerId = "local-demo-session";
const demoAnswerThreadStorageKey = `${answerThreadStorageKey}:${demoAnswerThreadOwnerId}`;
const demoRecentQueryStorageKey = `${recentQueryStorageKey}:${demoAnswerThreadOwnerId}`;
@@ -867,7 +867,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await gotoApp(page, "/");
await waitForDemoDashboardReady(page);
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toHaveCount(1);
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toHaveCount(1);
await expect(page.getByRole("heading", { name: "Answer" })).toBeVisible();
await expect(visibleQuestionInput(page)).toBeVisible();
await expect(page.getByRole("button", { name: "Generate source-backed answer" })).toHaveText(/^\s*Ask\s*$/);
@@ -1262,7 +1262,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(page.getByRole("button", { name: "Generate source-backed answer" })).toBeEnabled();
await expect(page.getByTestId("answer-grounding-chip")).toHaveCount(0);
expect(answerRequests).toEqual([]);
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toBeVisible();
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toBeVisible();
await expectDomIntegrity(page, { mobileNav: true, mobileFabReady: false });
await expectNoPageHorizontalOverflow(page);
});
@@ -2400,11 +2400,16 @@ test.describe("Clinical KB UI smoke coverage", () => {
test("dashboard favourites mode param redirects to the standalone favourites route", async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 });
await mockDemoApi(page);
+ const redirectMeasureErrors: string[] = [];
+ page.on("pageerror", (error) => {
+ if (error.message.includes("cannot have a negative time stamp")) redirectMeasureErrors.push(error.message);
+ });
await gotoApp(page, "/?mode=favourites&q=lithium%20set&focus=1");
await expect(page).toHaveURL(/\/favourites\?q=lithium\+set&focus=1$/);
await expect(page.getByTestId("favourites-hub")).toBeVisible();
await expect(page.getByRole("heading", { name: "Favourites command library" })).toBeVisible();
+ expect(redirectMeasureErrors).toEqual([]);
});
test("dashboard differentials mode param redirects to the standalone differentials route", async ({ page }) => {
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index 8f05b1f3..2ed5c540 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -882,7 +882,7 @@ test.describe("Clinical KB tools launcher", () => {
await expectNoPageHorizontalOverflow(page);
});
- test("forms mode shows source-backed form records in search results", async ({ page }) => {
+ test("forms mode shows registry-backed form records without unsupported pathway claims", async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 });
await mockAnswerDashboardApi(page);
await gotoLauncher(page, "/forms?q=transport%20forms&focus=1&run=1");
@@ -902,6 +902,9 @@ test.describe("Clinical KB tools launcher", () => {
await expect(
page.getByTestId("form-search-result-transport-crisis-form").getByLabel("Open Transport order"),
).toHaveAttribute("href", "/forms/transport-crisis-form");
+ await expect(page.getByRole("button", { name: "Refine" })).toHaveCount(0);
+ await expect(page.getByText(/Evidence 278|Pathways 12|Tasks 8|Source verified|Aligned to MHA 2014/)).toHaveCount(0);
+ await expect(page.getByText(/PSOLIS Transport|View full pathway/)).toHaveCount(0);
await expect(page.getByTestId("service-search-results")).toHaveCount(0);
await expectNoPageHorizontalOverflow(page);
});
@@ -1002,6 +1005,7 @@ test.describe("Clinical KB tools launcher", () => {
await expect(page.getByTestId("form-search-mobile-results")).toBeVisible();
await expect(page.getByTestId("form-search-mobile-result-transport-crisis-form")).toContainText("Transport order");
+ await expect(page.getByText(/PSOLIS Transport|View full pathway|Source verified/)).toHaveCount(0);
await expect(visibleGlobalSearchInput(page)).toHaveValue("transport");
await expectNoPageHorizontalOverflow(page);
});
@@ -1014,6 +1018,19 @@ test.describe("Clinical KB tools launcher", () => {
const dock = page.locator("form.answer-footer-search-dock");
await expect(dock).toBeVisible();
await expect(dock).not.toHaveAttribute("data-scroll-hidden", "true");
+ const transition = await dock.evaluate((node) => {
+ const style = window.getComputedStyle(node);
+ const durationMs = Math.max(
+ ...style.transitionDuration.split(",").map((value) => {
+ const normalized = value.trim();
+ const duration = Number.parseFloat(normalized);
+ return normalized.endsWith("ms") ? duration : duration * 1000;
+ }),
+ );
+ return { durationMs, property: style.transitionProperty };
+ });
+ expect(transition.property).toMatch(/transform|all/);
+ expect(transition.durationMs).toBeGreaterThanOrEqual(100);
// focus=1 leaves the composer focused; hide-on-scroll stays off while it has focus.
const input = visibleGlobalSearchInput(page).first();
@@ -1603,6 +1620,7 @@ test.describe("Clinical KB service detail page", () => {
{ name: "desktop", width: 1280, height: 900 },
] as const) {
test(`13YARN service detail is usable at ${viewport.name}`, async ({ page }) => {
+ await mockAnswerDashboardApi(page);
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await gotoLauncher(page, "/services/13yarn");
@@ -1623,7 +1641,40 @@ test.describe("Clinical KB service detail page", () => {
});
}
+ test("long mobile service details clear the bottom search dock at the scroll endpoint", async ({ page }) => {
+ await mockAnswerDashboardApi(page);
+ await page.setViewportSize({ width: 390, height: 820 });
+ await gotoLauncher(page, "/services/city-east-community-mental-health-service");
+
+ const servicePage = page.getByTestId("service-detail-page");
+ const footer = servicePage.getByText("Information accuracy may vary. Confirm locally before use.");
+ const scrollport = page.locator("#main-content");
+ await expect(servicePage).toBeVisible();
+ await scrollport.evaluate((element) => element.scrollTo({ top: element.scrollHeight, behavior: "instant" }));
+
+ await expect
+ .poll(() => scrollport.evaluate((element) => element.scrollHeight - element.clientHeight - element.scrollTop))
+ .toBeLessThanOrEqual(1);
+
+ const clearance = await footer.evaluate((element) => {
+ const scrollElement = document.querySelector