← Files DataARCHIVED FILE

tests/data-app-browser-dashboard-composition.mjs

72.6 KB · Sep 30, 2026 · 23:19 UTC

↓ Download file

import assert from "node:assert/strict";
import { dirname, join, resolve } from "node:path";
import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";
import { tmpdir } from "node:os";
import { installDashboardBrowserMocks, runDataAppFixtureBuild } from "./browser-helpers.mjs";

export async function verifyDashboardFilterMenuGeometry({ browser, failures, dataAppPath, settleSelectionUi }) {
  const stableMenuPage = await browser.newPage({ viewport: { width: 1440, height: 1050 } });
  stableMenuPage.on("pageerror", (error) => failures.push(error.message));
  await stableMenuPage.addInitScript(() => {
    Object.defineProperty(window, "innerWidth", {
      configurable: true,
      get: () => document.documentElement.clientWidth + 17,
    });
  });
  await installDashboardBrowserMocks(stableMenuPage);
  await stableMenuPage.goto(pathToFileURL(dataAppPath).href, { waitUntil: "load" });
  const stableFilter = stableMenuPage.getByRole("button", { name: "Product segment" });
  await stableFilter.waitFor();
  await settleSelectionUi(stableMenuPage);
  const dashboardGeometry = () => stableMenuPage.evaluate(() => ({
    scrollY,
    elements: [".dashboard-topbar", "main", '[data-component-id="active-users"]',
      '[data-component-id="usage-trend"]', ".recharts-wrapper"].map((selector) => {
      const bounds = document.querySelector(selector).getBoundingClientRect();
      return { selector, x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
    }),
  }));
  const menuBaseline = await dashboardGeometry();
  await stableFilter.click();
  await stableMenuPage.getByRole("menu").waitFor();
  await settleSelectionUi(stableMenuPage);
  assert.deepEqual(await dashboardGeometry(), menuBaseline,
    "Opening a modal filter must not shift the sticky top bar, dashboard cards, or responsive charts");
  assert.deepEqual(await stableMenuPage.evaluate(() => ({
    locked: document.body.hasAttribute("data-scroll-locked"),
    marginRight: getComputedStyle(document.body).marginRight,
    paddingRight: getComputedStyle(document.body).paddingRight,
  })), { locked: true, marginRight: "0px", paddingRight: "0px" },
  "Modal dashboard filters must preserve outside-interaction blocking without redundant scrollbar compensation");
  await stableMenuPage.keyboard.press("Escape");
  await settleSelectionUi(stableMenuPage);
  assert.deepEqual(await dashboardGeometry(), menuBaseline,
    "Closing a modal filter must leave the original dashboard geometry unchanged");
  await stableMenuPage.close();
}

export async function verifyTemporalAxisGeometry(page, reviewedTimes) {
  await page.locator("[data-temporal-axis-tick]").first().waitFor({state:"visible"});
  await page.evaluate(() => document.fonts.ready);
  await page.evaluate(() => new Promise(resolveFrame => {
    requestAnimationFrame(() => requestAnimationFrame(resolveFrame));
  }));
  const violations = await page.evaluate((reviewedTimes) => {
    const failures=[], axes=new Map();
    for (const label of document.querySelectorAll("[data-temporal-axis-tick]")) {
      const svg=label.closest("svg.recharts-surface");
      if (!svg) continue;
      const rect=label.getBoundingClientRect(), frame=svg.getBoundingClientRect();
      if (!rect.width || !frame.width) continue;
      if (rect.left<frame.left-1 || rect.right>frame.right+1 || rect.bottom>frame.bottom+1)
        failures.push({kind:"clipped",label:label.textContent,left:rect.left,right:rect.right,frameLeft:frame.left,frameRight:frame.right});
      const group=axes.get(svg) ?? []; group.push({left:rect.left,right:rect.right,center:(rect.left+rect.right)/2,label:label.textContent,value:label.dataset.axisValue});
      axes.set(svg,group);
    }
    for (const [svg,labels] of axes) {
      labels.sort((a,b)=>a.center-b.center);
      const domain=svg.querySelector("[data-temporal-axis-start]")?.dataset;
      if (domain?.temporalAxisScale === "time") {
        const start=Date.parse(domain.temporalAxisStart), end=Date.parse(domain.temporalAxisEnd);
        if (labels.some(label=>Number(label.value)<start || Number(label.value)>end)) failures.push({kind:"outside time extent"});
        const dates=labels.map(label=>new Date(Number(label.value)));
        const gaps=dates.slice(1).map((date,i)=>date-dates[i]);
        const monthIndexes=dates.map(date=>date.getUTCFullYear()*12+date.getUTCMonth());
        const monthGaps=monthIndexes.slice(1).map((month,i)=>month-monthIndexes[i]);
        const calendarCadence=dates.every(date=>date.getUTCDate()===1 && date.getUTCHours()===0)
          && monthGaps.every(gap=>gap>0 && gap===monthGaps[0]);
        if (!calendarCadence && !gaps.every(gap=>gap>0 && gap===gaps[0])) failures.push({kind:"irregular time cadence",labels});
        if (svg.closest('[data-component-id]')?.dataset.componentId === "usage-trend") {
          // This fixture hides resting dots. Its monotone path still ends each
          // cubic segment at the reviewed observation; inspect those vertices.
          const points=[...svg.querySelectorAll('.recharts-line-curve')].flatMap(path=>
            (path.getAttribute('d').match(/[MLC][^MLC]*/gu)??[]).map(command=>{
              const numbers=command.slice(1).match(/-?\d*\.?\d+(?:e[-+]?\d+)?/giu).map(Number);
              return new DOMPoint(numbers.at(-2),numbers.at(-1)).matrixTransform(path.getScreenCTM()).x;
            }));
          for (const label of labels) {
            if (!reviewedTimes.includes(Number(label.value)) || !points.some(x=>Math.abs(x-label.center)<1))
              failures.push({kind:"regular date label detached from its observation",label});
          }
        }
      } else if (labels.length>2) {
        const gaps=labels.slice(1).map((label,i)=>label.center-labels[i].center);
        if (gaps.some(gap=>Math.abs(gap-gaps[0])>1)) failures.push({kind:"irregular bucket cadence",labels});
      }
      for (let index=1;index<labels.length;index++) {
        if (labels[index].left<labels[index-1].right+8) failures.push({kind:"overlap",labels:[labels[index-1].label,labels[index].label]});
      }
    }
    return failures;
  }, reviewedTimes);
  assert.deepEqual(violations,[],"Temporal labels must be contained, separated, and use a consistent time or bucket cadence");
}

async function verifySmoothCardSurfaces(page) {
  await page.waitForFunction(() => [...document.querySelectorAll('[data-component-id]')].every((card) => {
    const surface = card.querySelector(':scope > .smooth-card-surface[data-ready]');
    if (!surface) return false;
    const bounds = card.getBoundingClientRect();
    const viewBox = surface.viewBox.baseVal;
    return Math.abs(viewBox.width - bounds.width) < .1 && Math.abs(viewBox.height - bounds.height) < .1;
  }));
  const surfaces = await page.locator('[data-component-id]').evaluateAll((cards) => cards.map((card) => {
    const surface = card.querySelector(':scope > .smooth-card-surface');
    const path = surface.querySelector('path');
    const style = getComputedStyle(card);
    const bounds = path.getBBox();
    return {
      smoothing: surface.dataset.cornerSmoothing,
      hasBezierTransitions: /c/iu.test(path.getAttribute('d')),
      width: bounds.width, height: bounds.height,
      expectedWidth: surface.viewBox.baseVal.width - parseFloat(style.borderTopWidth),
      expectedHeight: surface.viewBox.baseVal.height - parseFloat(style.borderTopWidth),
      fill: getComputedStyle(path).fill, stroke: getComputedStyle(path).stroke,
      transparentContainer: style.backgroundColor === 'rgba(0, 0, 0, 0)' && style.borderTopColor === 'rgba(0, 0, 0, 0)',
      clip: style.clipPath, overflow: style.overflow, pointerEvents: getComputedStyle(surface).pointerEvents,
    };
  }));
  assert.ok(surfaces.length > 0, "The surface check must inspect rendered cards");
  for (const surface of surfaces) {
    assert.equal(surface.smoothing, '0.6');
    assert.equal(surface.hasBezierTransitions, true, 'Cards must paint smooth transition curves, not just round CSS corners');
    assert.ok(Math.abs(surface.width - surface.expectedWidth) < .1 && Math.abs(surface.height - surface.expectedHeight) < .1,
      'Smoothed surfaces must follow the card size without scaling the corner radius');
    assert.equal(surface.fill, 'rgb(255, 255, 255)');
    assert.equal(surface.stroke, 'rgb(237, 237, 237)');
    assert.equal(surface.transparentContainer, true, 'Do not paint the old circular border underneath the smoothed border');
    assert.equal(surface.clip, 'none', 'Smoothing must not clip tooltips or focus rings');
    assert.equal(surface.overflow, 'visible');
    assert.equal(surface.pointerEvents, 'none', 'Decorative card surfaces must not intercept controls');
  }
}

async function verifyFilterBarEdges(page) {
  const settleGeometry = () => page.evaluate(() => new Promise(resolveFrame => {
    requestAnimationFrame(() => requestAnimationFrame(resolveFrame));
  }));
  const verifyGeometry = async (expectedScrollbarWidth = null) => {
    await settleGeometry();
    const geometry = await page.locator('.filter-bar').evaluate((bar) => {
      const bounds = bar.getBoundingClientRect();
      const root = document.querySelector('.dashboard-root').getBoundingClientRect();
      const style = getComputedStyle(bar);
      return {
        left: bounds.left, right: bounds.right, rootLeft: root.left, rootRight: root.right,
        scrollbarWidth: window.innerWidth - root.width,
        controlLeft: bar.firstElementChild.getBoundingClientRect().left,
        cardLeft: document.querySelector('[data-component-id]').getBoundingClientRect().left,
        pageWidth: document.documentElement.scrollWidth, clientWidth: document.documentElement.clientWidth,
        stuck: bar.hasAttribute('data-stuck'),
        borderWidth: style.borderBottomWidth, borderColor: style.borderBottomColor,
      };
    });
    if (expectedScrollbarWidth != null) {
      assert.ok(Math.abs(geometry.scrollbarWidth - expectedScrollbarWidth) < .5,
        `The scrollbar regression must reserve ${expectedScrollbarWidth}px: ${JSON.stringify(geometry)}`);
    }
    assert.ok(Math.abs(geometry.left - geometry.rootLeft) < .5 && Math.abs(geometry.right - geometry.rootRight) < .5,
      `Sticky filter backgrounds must match both dashboard edges: ${JSON.stringify(geometry)}`);
    assert.ok(Math.abs(geometry.controlLeft - geometry.cardLeft) < 1,
      'Full-width filter backgrounds must keep controls aligned with the cards');
    assert.ok(geometry.pageWidth <= geometry.clientWidth, 'Full-width filters must not introduce horizontal page scrolling');
    assert.equal(geometry.borderWidth, '1px');
    assert.equal(geometry.borderColor, geometry.stuck ? 'rgb(237, 237, 237)' : 'rgba(0, 0, 0, 0)',
      'The full-width bottom border must be visible only while the filter bar is sticky');
  };
  await verifyGeometry();
  // An explicit width makes macOS use a classic scrollbar instead of an overlay.
  const scrollbarStyle = await page.addStyleTag({ content: 'html::-webkit-scrollbar { width: 15px; height: 15px; }' });
  try {
    await verifyGeometry(15);
  } finally {
    await scrollbarStyle.evaluate((element) => element.remove());
    await settleGeometry();
  }
}

export async function verifyDashboardVisualPolish(page) {
  await page.evaluate(() => window.scrollTo(0, 0));
  await page.locator('.filter-bar:not([data-stuck])').waitFor();
  await verifyFilterBarEdges(page);
  await verifySmoothCardSurfaces(page);
  assert.deepEqual(await page.locator(".data-section-title").allTextContents(),
    ["Adoption and acquisition", "Engagement patterns", "Growth and outlook", "Feature and regional adoption", "Account health"]);
  const appearance = await page.evaluate(() => {
    const metrics = [...document.querySelectorAll('.metric-strip [data-component-id]')];
    const cards = [...document.querySelectorAll('[data-component-id]')];
    const trend = document.querySelector('[data-component-id="usage-trend"]');
    return {
      kpiGap: trend.getBoundingClientRect().top - Math.max(...metrics.map((card) => card.getBoundingClientRect().bottom)),
      background: getComputedStyle(document.querySelector('.dashboard-root')).backgroundColor,
      cardBackground: getComputedStyle(trend.querySelector('.smooth-card-surface > path')).fill,
      radii: [...new Set(cards.map((card) => getComputedStyle(card).borderRadius))],
      shadows: [...new Set(cards.map((card) => getComputedStyle(card).boxShadow))],
      headingsPinned: [...document.querySelectorAll('.data-section-title')]
        .every((heading) => !heading.closest('[data-sortable-item-id]')),
      sectionGaps: [...document.querySelectorAll('.dashboard-section-start')].map((row) => {
        const heading = row.querySelector('.sortable-row-header').getBoundingClientRect();
        const firstCard = row.querySelector('[data-component-id]').getBoundingClientRect();
        return firstCard.top - heading.bottom;
      }),
    };
  });
  assert.equal(appearance.kpiGap, 32, "The KPI-to-chart gap must total 32px, not stacked row padding");
  assert.notEqual(appearance.background, appearance.cardBackground, "The page must contrast with card surfaces");
  assert.deepEqual(appearance.radii, ["20px"], "The Figma polish must not change card radii");
  assert.deepEqual(appearance.shadows, ["rgba(0, 0, 0, 0.01) 0px 4px 12px 0px"]);
  assert.equal(appearance.headingsPinned, true);
  assert.deepEqual(appearance.sectionGaps, [20, 20, 20, 20, 20]);
  const filterAppearance = () => page.locator('.filter-bar').evaluate((element) => {
    const style = getComputedStyle(element);
    return { background: style.backgroundColor, image: style.backgroundImage, blur: style.backdropFilter };
  });
  const transparentFilter = { background: "rgba(0, 0, 0, 0)", image: "none", blur: "none" };
  assert.deepEqual(await filterAppearance(), transparentFilter,
    "Resting filters must not paint a background or blur the page");
  const supportingCards = page.locator('[data-component-id="segment-breakdown"], [data-component-id="priority-accounts"]');
  const initialHeights = await supportingCards.evaluateAll((cards) => cards.map((card) => card.getBoundingClientRect().height));
  assert.ok(Math.abs(initialHeights[0] - initialHeights[1]) < 1, "Cards in the supporting row must stretch to equal heights");
  await page.getByRole('button', { name: 'Show 7 more', exact: true }).click();
  await page.locator('.filter-bar[data-stuck]').waitFor();
  const stickyFilter = await filterAppearance();
  assert.notEqual(stickyFilter.background, transparentFilter.background);
  assert.equal(stickyFilter.image, 'none', 'The divider must use the full border rather than an inset gradient');
  assert.equal(stickyFilter.blur, 'blur(14px)');
  await verifyFilterBarEdges(page);
  await page.waitForFunction((initialHeight) => {
    const list = document.querySelector('[data-component-id="segment-breakdown"]').getBoundingClientRect();
    const map = document.querySelector('[data-component-id="priority-accounts"]').getBoundingClientRect();
    return list.height > initialHeight && Math.abs(list.height - map.height) < 1;
  }, initialHeights[0]);
  await verifySmoothCardSurfaces(page);
  await page.getByRole('button', { name: 'Show fewer', exact: true }).click();
  await page.waitForFunction((initialHeight) => [...document.querySelectorAll('[data-component-id="segment-breakdown"], [data-component-id="priority-accounts"]')]
    .every((card) => Math.abs(card.getBoundingClientRect().height - initialHeight) < 1), initialHeights[0]);
  await verifySmoothCardSurfaces(page);
  const originalViewport = page.viewportSize();
  try {
    // Wider than the maximum content column: fixed negative gutters leave a gap.
    await page.setViewportSize({ width: 1920, height: 1080 });
    await page.evaluate(() => window.scrollTo(0, 400));
    await page.locator('.filter-bar[data-stuck]').waitFor();
    await verifyFilterBarEdges(page);
    await page.setViewportSize({ width: 390, height: 844 });
    await verifyFilterBarEdges(page);
    await verifySmoothCardSurfaces(page);
  } finally {
    await page.setViewportSize(originalViewport);
  }
  const firstMetric = page.locator('[data-component-id="active-users"]');
  const originalStyle = await firstMetric.getAttribute('style');
  try {
    for (const radius of ['0px', '50%', '20px 4px']) {
      await firstMetric.evaluate((card, value) => { card.style.borderRadius = value; }, radius);
      await firstMetric.locator('.smooth-card-surface:not([data-ready])').waitFor({ state: 'attached' });
      assert.equal(await firstMetric.evaluate((card) => getComputedStyle(card).backgroundColor), 'rgb(255, 255, 255)',
        'Square, percentage, and nonuniform radii must retain the original CSS surface');
    }
  } finally {
    await firstMetric.evaluate((card, value) => value == null ? card.removeAttribute('style') : card.setAttribute('style', value), originalStyle);
  }
  await verifySmoothCardSurfaces(page);
  await page.evaluate(() => window.scrollTo(0, 0));
  await page.locator('.filter-bar:not([data-stuck])').waitFor();
  assert.deepEqual(await filterAppearance(), transparentFilter, "Scrolling back must remove the sticky filter treatment");
}

export async function verifyDashboardSectionFilters(page) {
  const filters = page.getByRole("region", { name: "Engagement filters", exact: true });
  const product = filters.getByRole("button", { name: "Product", exact: true });
  const scatter = page.locator('[data-component-id="engagement-scatter"]');
  const headline = page.locator('[data-component-id="active-users"] .metric-value');
  const beforeHeadline = await headline.innerText();
  const beforeEvidence = await page.locator('[data-component-id="usage-details"] tbody').innerText();
  const heatScale = page.locator('[aria-label="Sessions color scale"]');
  const beforeScale = await heatScale.innerText();
  await product.click();
  await page.getByRole("menuitemradio", { name: "Studio", exact: true }).click();
  await page.waitForFunction(() => document.querySelectorAll('[data-component-id="engagement-scatter"] .recharts-scatter-symbol').length === 30);
  assert.notEqual(await heatScale.innerText(), beforeScale, "The local product filter must also update aggregated heatmap values");
  assert.equal(await headline.innerText(), beforeHeadline);
  assert.equal(await page.locator('[data-component-id="usage-details"] tbody').innerText(), beforeEvidence,
    "Engagement scope must not filter another section's table");
  for (const [id, title, count] of [["engagement-heatmap", "When customers are most active", 84],
    ["engagement-scatter", "Account size by engagement", 30]]) {
    await page.locator(`[data-component-id="${id}"]`).getByRole("button", { name: `${title} actions`, exact: true }).click();
    await page.getByRole("menuitem", { name: "View data source", exact: true }).click();
    const source = page.getByRole("complementary", { name: `Data source for ${title}`, exact: true });
    await source.getByRole("tab", { name: "Overview", exact: true }).click();
    assert.match(await source.innerText(), /Product: Studio/u);
    await source.getByRole("tab", { name: "Data preview", exact: true }).click();
    assert.match(await source.innerText(), new RegExp(`${count} results`, "u"));
    assert.equal(await source.getByRole("cell", { name: "Studio", exact: true }).count(),
      await source.locator("tbody tr").count(), "Source preview must retain scoped raw product rows");
    await source.getByRole("button", { name: "Close data source", exact: true }).click();
  }
  const pageProduct = page.getByRole("region", { name: "Data app filters", exact: true })
    .getByRole("button", { name: "Product segment", exact: true });
  await pageProduct.click();
  await page.getByRole("menuitemradio", { name: "Search", exact: true }).click();
  assert.equal(await scatter.locator(".recharts-scatter-symbol").count(), 0,
    "Conflicting page and section products must not silently broaden either scope");
  assert.equal(await filters.locator(".clear-filters").count(), 0, "Section filters should use their existing All option, not an extra reset button");
  await product.click();
  await page.getByRole("menuitemradio", { name: "All", exact: true }).click();
  await page.waitForFunction(() => document.querySelectorAll('[data-component-id="engagement-scatter"] .recharts-scatter-symbol').length === 30);
  assert.match(await pageProduct.innerText(), /Search/u, "Clearing a section must leave its page filter intact");
  await pageProduct.click();
  await page.getByRole("menuitemradio", { name: "All", exact: true }).click();
  await page.waitForFunction(() => document.querySelectorAll('[data-component-id="engagement-scatter"] .recharts-scatter-symbol').length === 90);
  const accountFilters = page.getByRole("region", { name: "Account health filters", exact: true });
  await accountFilters.getByRole("button", { name: "Risk", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "Elevated", exact: true }).click();
  assert.equal(await page.locator('[data-component-id="usage-details"] tbody tr').count(), 3);
  assert.equal(await headline.innerText(), beforeHeadline);
  assert.equal(await scatter.locator(".recharts-scatter-symbol").count(), 90);
  await accountFilters.getByRole("button", { name: "Risk", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "All", exact: true }).click();
  await accountFilters.getByRole("button", { name: "Risk", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "Elevated", exact: true }).click();
  await page.getByRole("button", { name: "Edit text and layout", exact: true }).click();
  const moveTable = page.getByRole("button", { name: "Move Reviewed account-level evidence", exact: true });
  await moveTable.press("Space");
  await moveTable.press("ArrowUp");
  await moveTable.press("Space");
  const movedFilters = page.locator('[data-component-id="usage-details"]')
    .getByRole("region", { name: "Reviewed account-level evidence filters", exact: true });
  await movedFilters.waitFor();
  assert.match(await movedFilters.innerText(), /Elevated/u,
    "Moving a filtered table must keep its active controls available on the moved block");
  assert.equal(await page.locator('[data-component-id="usage-details"] tbody tr').count(), 3);
  assert.equal(await movedFilters.locator(".clear-filters").count(), 0, "Moved filters must omit the redundant reset action too");
  await movedFilters.getByRole("button", { name: "Risk", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "All", exact: true }).click();
  assert.equal(await page.locator('[data-component-id="usage-details"] tbody tr').count(), 8);
  await page.getByRole("button", { name: "Undo dashboard change", exact: true }).click();
  await accountFilters.waitFor();
  assert.equal(await page.locator('[data-component-id="usage-details"] .component-section-filters').count(), 0,
    "Returning a block to its scoped row must restore header controls without duplicates");
  await page.getByRole("button", { name: "Save", exact: true }).click();
  await page.getByRole("button", { name: "Edit text and layout", exact: true }).waitFor();
  const viewport = page.viewportSize();
  await page.setViewportSize({ width: 390, height: 844 });
  for (const label of ["Engagement filters", "Account health filters"]) {
    const bounds = await page.getByRole("region", { name: label, exact: true }).boundingBox();
    assert.ok(bounds.x >= 0 && bounds.x + bounds.width <= 390, `${label} must fit the mobile section header`);
  }
  await page.setViewportSize(viewport);
  await page.evaluate(() => window.scrollTo(0, 0));
}

export async function verifyDashboardComposition({ page, failures, screenshots }) {
  await verifyDashboardVisualPolish(page);
  for (const width of [320, 390, 430, 650, 651, 760, 1440]) {
    await page.setViewportSize({ width, height: 1050 });
    const chartInfoAlignment = await page.locator(".component-title-tail:has(.info-wrap)").evaluateAll((tails) =>
      tails.map((tail) => {
        const text = document.createRange();
        text.selectNodeContents(tail.firstChild);
        const label = text.getBoundingClientRect();
        const icon = tail.querySelector(".info [data-dashboard-icon=info]").getBoundingClientRect();
        return { title: tail.textContent, gap: icon.left - label.right,
          centerOffset: (icon.top + icon.bottom - label.top - label.bottom) / 2 };
      }));
    assert.ok(chartInfoAlignment.length > 0, "The fixture needs a described chart title");
    assert.ok(chartInfoAlignment.every(({ gap, centerOffset }) =>
      gap >= 5 && gap <= 11 && Math.abs(centerOffset) <= 2),
    `Chart information icons should be spaced and centered at ${width}px: ${JSON.stringify(chartInfoAlignment)}`);
  }
  await page.setViewportSize({ width: 1440, height: 1050 });
  const dashboardContentTitles = page.locator("main .component-title-text");
  const initialDashboardTitles = await dashboardContentTitles.allInnerTexts();
  assert.equal(await page.locator(".dashboard-topbar-title").innerText(), "Product adoption and engagement");
  assert.ok(initialDashboardTitles.includes("Active accounts over time"));
  assert.ok(initialDashboardTitles.includes("Active accounts by feature"));
  assert.ok(initialDashboardTitles.indexOf("When customers are most active")
    < initialDashboardTitles.indexOf("Weekly change in active accounts"),
  "Customer activity and account size should appear before growth drivers and target attainment");
  assert.equal(await page.locator('[data-component-id="growth-drivers"] .chart-axis-label').count(), 0,
    "Clearly labeled waterfall categories should not repeat an unnecessary growth-driver axis title");
  assert.equal(await page.locator('[data-component-id="channel-composition"] .chart-legend')
    .evaluate((legend) => getComputedStyle(legend).marginTop), "6px",
  "Dashboard chart legends should sit closer to their x-axis tick labels");
  assert.equal(await page.locator(".data-metric-card").count(), 4,
    "Starter KPIs must consume the reusable public metric-card primitive");
  assert.equal(await page.locator(".date-range-trigger [data-dashboard-icon=calendar]").count(), 0,
    "Date-range controls should rely on their clear text label instead of a redundant calendar icon");
  assert.ok(Number(await page.locator(".chart-heatmap-cell").first().getAttribute("rx")) > 0,
    "Classic heatmaps should inherit the theme's rounded chart-mark geometry");
  const waterfallTotal = page.locator('[data-component-id="growth-drivers"] .recharts-bar-rectangle path').first();
  await waterfallTotal.hover();
  const waterfallTooltipSwatch = page.locator('[data-component-id="growth-drivers"] .chart-tooltip i').first();
  await waterfallTooltipSwatch.waitFor();
  assert.equal(await waterfallTooltipSwatch.evaluate((swatch) => getComputedStyle(swatch).backgroundColor),
    await waterfallTotal.evaluate((mark) => getComputedStyle(mark).fill),
    "Waterfall total tooltips must use the same neutral color as their reviewed beginning and ending bars");
  const chartTooltipTransition = await waterfallTooltipSwatch
    .locator("xpath=ancestor::*[contains(@class, 'recharts-tooltip-wrapper')]")
    .evaluate((tooltip) => getComputedStyle(tooltip).transition);
  assert.match(chartTooltipTransition, /opacity 0\.09s/u,
    "Chart tooltips should use a short, unobtrusive appearance transition");
  assert.doesNotMatch(chartTooltipTransition, /transform/u,
    "Chart tooltip positions must not animate in from the origin or jump between heatmap cells");
  assert.equal(await page.locator(".eyebrow, .hero-copy").count(), 0,
    "The dashboard starter must not render an autogenerated eyebrow or unnecessary subtitle");
  assert.equal(await page.getByRole("heading", {
    name: /executive summary|key insights|what this means|recommended next steps/iu,
  }).count(), 0, "The dashboard starter must not introduce narrative or recommendation sections");
  assert.ok(initialDashboardTitles.every((title) => !/\d+%|is surging|is accelerating/iu.test(title)),
    "Authored dashboard and chart headings must not encode snapshot-specific findings");
  assert.equal(await page.locator(".dashboard-fixture-label").count(), 0,
    "The protected application top bar should not contain a sample-data label");
  const funnel = page.locator('[data-component-id="activation-funnel"]');
  assert.deepEqual(await funnel.locator(".chart-funnel-stage-name").allTextContents(),
    ["Signed up", "Created workspace", "First key action", "Activated"]);
  await page.getByRole("button", { name: "Product segment", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "Studio", exact: true }).click();
  assert.deepEqual(await funnel.locator(".chart-funnel-stage").evaluateAll((stages) =>
    stages.map((stage) => Number(stage.getAttribute("aria-label").match(/: ([\d,]+), /u)[1].replaceAll(",", "")))),
  [1247, 665, 470, 292], "The example funnel must show the selected Studio cohort, not all accounts");
  await page.getByRole("button", { name: "Clear all", exact: true }).click();
  assert.ok(await page.locator(".metric-mini-trend").count() >= 3,
    "KPI cards should show compact sparklines derived from reviewed historical values");
  assert.equal(await page.locator(".metric-item").first()
    .evaluate((metric) => getComputedStyle(metric).alignContent), "space-between",
  "KPI titles and values should occupy opposite ends of the existing card height");
  assert.ok(await page.locator(".metric-mini-trend linearGradient stop[offset='100%']").count() >= 3,
    "Reviewed KPI sparklines should fade into a subtle transparent vertical gradient");
  const priorityAccounts = page.locator('[data-component-id="priority-accounts"]');
  assert.equal(await priorityAccounts.locator(".regional-marker").count(), 3,
    "The regional activity map should show one marker for each reviewed region");
  assert.ok((await priorityAccounts.locator(".regional-map-land").getAttribute("d")).length >= 10000,
    "Regional activity should use detailed real-world Natural Earth coastline geometry");
  assert.equal(await priorityAccounts.locator(".regional-map-dot-field, #regional-map-dots").count(), 0,
    "Regional maps should render clean geography without hidden decorative dot textures");
  assert.deepEqual(await priorityAccounts.locator(".regional-summary-name").allInnerTexts(),
    ["North America", "EMEA", "APAC"], "The compact map legend must use the reviewed account regions");
  assert.deepEqual(await priorityAccounts.locator(".regional-marker-value").allInnerTexts(),
    ["5.4K", "4.2K", "2.9K"], "Reviewed regional account totals should appear directly on the map");
  assert.equal(await priorityAccounts.locator(".regional-summary-risk").count(), 0,
    "Regional risk details should remain in marker tooltips instead of crowding the legend");
  const emeaMarker = priorityAccounts.getByRole("button", {
    name: /EMEA: 4\.2K active users, 3 accounts, 2 elevated risk/u,
  });
  const regionalMapGeometry = await emeaMarker.evaluate((marker) => {
    const map = marker.closest(".regional-map");
    const surface = map.querySelector(".regional-map-surface");
    const position = surface.createSVGPoint();
    position.x = 303;
    position.y = 79;
    const anchor = position.matrixTransform(surface.getScreenCTM());
    const bounds = marker.getBoundingClientRect();
    return {
      height: map.getBoundingClientRect().height,
      aspectRatio: surface.getBoundingClientRect().width / surface.getBoundingClientRect().height,
      horizontalOffset: Math.abs(bounds.x + bounds.width / 2 - anchor.x),
      verticalOffset: Math.abs(bounds.y + bounds.height / 2 - anchor.y),
    };
  });
  assert.ok(regionalMapGeometry.height >= 168,
    "The regional map should use the extra space recovered by compact inline risk labels");
  assert.ok(Math.abs(regionalMapGeometry.aspectRatio - 560 / 220) <= .01,
    "The regional map must preserve its real-world projected proportions without stretching");
  assert.ok(regionalMapGeometry.horizontalOffset <= 1 && regionalMapGeometry.verticalOffset <= 1,
    "Regional activity markers must remain pinned to their actual geographic map coordinates");
  const regionalLegendLayout = await priorityAccounts.locator(".regional-legend")
    .evaluate((legend) => ({ display: getComputedStyle(legend).display,
      justification: getComputedStyle(legend).justifyContent }));
  assert.deepEqual(regionalLegendLayout, { display: "flex", justification: "center" },
    "Regional colors should use the same compact centered legend treatment as other charts");
  const regionalLegendBottom = await priorityAccounts.evaluate((card) => {
    const legend = card.querySelector(".regional-legend").getBoundingClientRect();
    return card.getBoundingClientRect().bottom - legend.bottom;
  });
  assert.ok(regionalLegendBottom <= 20,
    "The regional map legend should sit at the bottom like the other chart legends");
  await emeaMarker.hover();
  assert.match(await priorityAccounts.getByRole("tooltip").filter({ hasText: "EMEA" }).innerText(),
    /4\.2K active users[\s\S]*2 elevated risk/u,
    "Regional marker hover details must describe reviewed activity and elevated risk");
  for (const componentId of ["segment-composition", "channel-composition", "engagement-heatmap",
    "engagement-scatter"]) {
    const chart = page.locator(`[data-component-id="${componentId}"]`);
    assert.equal(await chart.count(), 1, `${componentId} must appear in the richer starter dashboard`);
    assert.ok(await chart.locator("svg.recharts-surface").count(),
      `${componentId} must render an actual chart rather than a decorative placeholder`);
  }
  assert.ok(await page.locator('[data-component-id="engagement-heatmap"] .chart-heatmap-cell').count() >= 70,
    "The starter heatmap should demonstrate a dense sequential color scale");
  const customerActivity = page.locator('[data-component-id="engagement-heatmap"]');
  assert.match(await customerActivity.innerText(), /12 AM/u,
    "Customer-activity time labels should use an understandable 12-hour clock");
  assert.match(await customerActivity.innerText(), /12 PM/u,
    "Customer-activity time labels should distinguish afternoon hours");
  assert.equal(await customerActivity.locator(".chart-axis-label").count(), 0,
    "Customer activity should omit redundant hour-of-day axis titles");
  const spacingViewport = page.viewportSize();
  for (const width of [spacingViewport.width, 1000, 760]) {
    await page.setViewportSize({ width, height: spacingViewport.height });
    for (const selector of [".visual-analysis-grid", ".diagnostic-layout", ".analysis-layout"]) {
      const spacing = await page.locator(selector).first().evaluate((section) => {
        const style = getComputedStyle(section);
        return { columnGap: style.columnGap, rowGap: style.rowGap, top: style.paddingTop,
          sectionStart: section.classList.contains("dashboard-section-start") };
      });
      assert.equal(spacing.columnGap, "20px", `${selector} should preserve the horizontal card gap`);
      assert.equal(spacing.rowGap, "20px", `${selector} should preserve the vertical card gap`);
      assert.equal(spacing.top, spacing.sectionStart ? "40px" : "20px",
        `${selector} should separate titled sections without changing the card gaps`);
    }
  }
  await page.setViewportSize(spacingViewport);
  for (const id of ["segment-composition", "channel-composition", "growth-drivers", "forecast-outlook",
    "engagement-heatmap", "engagement-scatter", "adoption-scenario", "segment-breakdown", "priority-accounts"]) {
    const card = page.locator(`[data-component-id="${id}"]`);
    assert.equal(await card.evaluate((element) => getComputedStyle(element).getPropertyValue("--dashboard-visualization-height").trim()), "320px",
      `${id} should inherit the shared compact visualization-height token`);
    assert.ok((await card.boundingBox()).height >= 250, `${id} must retain readable plot space inside the sortable row`);
    const cardDimensions = await card.evaluate((element) => ({
      scrollHeight: element.scrollHeight,
      clientHeight: element.clientHeight,
      children: [...element.children].map((child) => ({
        className: child.className,
        height: Math.round(child.getBoundingClientRect().height),
        bottom: Math.round(child.getBoundingClientRect().bottom - element.getBoundingClientRect().top),
      })),
    }));
    assert.ok(cardDimensions.scrollHeight <= cardDimensions.clientHeight + 2,
      `${id} should fit its chart, labels, and controls inside the compact card (${JSON.stringify(cardDimensions)})`);
  }
  const metricHeight = Math.round((await page.locator(".metric-item").first().boundingBox()).height);
  assert.ok(metricHeight >= 112 && metricHeight <= 156,
    `KPI cards fit a separate value and comparison row independently of visualization height: ${metricHeight}`);
  assert.ok(await page.locator('[data-component-id="engagement-scatter"] .recharts-scatter-symbol').count() >= 60,
    "The starter scatter plot should include enough reviewed account observations to show a distribution");
  const channelMix = page.locator('[data-component-id="channel-composition"]');
  async function assertAlignedStackClips(action) {
    const mismatches = await channelMix.evaluate((chart) => {
      const columns = new Map();
      for (const group of chart.querySelectorAll("g[data-stack-sign]")) {
        const clip = group.querySelector("clipPath path");
        const mark = group.querySelector("path[clip-path]");
        if (!clip || !mark) continue;
        const clipBounds = clip.getBBox();
        const markBounds = mark.getBBox();
        const key = Math.round(markBounds.x * 10) / 10;
        const entries = columns.get(key) ?? [];
        entries.push({ y: clipBounds.y, height: clipBounds.height });
        columns.set(key, entries);
      }
      return [...columns.entries()].flatMap(([column, clips]) => clips.slice(1).flatMap((clip) =>
        Math.abs(clip.y - clips[0].y) > 0.75 || Math.abs(clip.height - clips[0].height) > 0.75
          ? [{ column, clips }] : []));
    });
    assert.deepEqual(mismatches, [],
      `Visible stack segments must share one rounded outer clip ${action}: ${JSON.stringify(mismatches.slice(0, 2))}`);
  }
  const referralSeries = channelMix.getByRole("button", { name: "Toggle Referrals" });
  const communitySeries = channelMix.getByRole("button", { name: "Toggle Community" });
  await referralSeries.click();
  await assertAlignedStackClips("after hiding a middle legend item");
  await communitySeries.click();
  await assertAlignedStackClips("after hiding the exposed top legend item");
  await referralSeries.click();
  await assertAlignedStackClips("after restoring a middle legend item");
  await communitySeries.click();
  await assertAlignedStackClips("after restoring the exposed top legend item");
  assert.equal(await page.locator(".brand-mark, .showcase-mark").count(), 0);
  const initialChrome = await page.locator(".dashboard-topbar").boundingBox();
  const dashboardWidth = await page.locator(".dashboard-topbar")
    .evaluate((header) => header.closest(".dashboard-root").getBoundingClientRect().width);
  assert.equal(initialChrome.x, 0, "Dashboard header does not start at the viewport edge");
  assert.equal(initialChrome.y, 0, "Dashboard header is not aligned to the top of the viewport");
  assert.equal(initialChrome.width, dashboardWidth, "Dashboard header does not span the dashboard");
  assert.equal(await page.locator('[data-data-app-chrome="topbar"]').count(), 1,
    "Dashboards must retain exactly one protected application top bar");
  assert.equal(await page.locator('main[data-data-app-content="dashboard"]').count(), 1,
    "Dashboard composition must remain inside the authored content boundary");
  const reportingRange = page.getByRole("button", { name: "Date range" });
  await reportingRange.click();
  await page.getByRole("menuitem", { name: "Custom range…", exact: true }).click();
  const reportingCalendar = page.locator(".date-range-calendar");
  assert.equal(await reportingCalendar.count(), 1,
    `Opening the reporting range should show the calendar: ${failures.join("; ")}`);
  assert.equal(await reportingCalendar.getByRole("group", { name: "July 2026" }).count(), 1,
    `Custom reporting ranges should open the navigable calendar: ${await reportingCalendar.innerText()}`);
  assert.equal(await reportingCalendar.getByRole("group", { name: "June 2026" }).count(), 1,
    "The desktop range picker should show adjacent calendar months like the shadcn range-picker pattern");
  assert.equal(await reportingCalendar.locator('input[type="date"]').count(), 0,
    "The custom range should use the existing two-month calendar");
  await reportingCalendar.getByRole("button", { name: "Monday, July 6, 2026" }).click();
  assert.match(await reportingCalendar.innerText(), /Select an end date/u,
    "A real date-range picker should keep the calendar open after choosing the start date");
  await reportingCalendar.getByRole("button", { name: "Monday, July 20, 2026" }).click();
  assert.match(await reportingRange.innerText(), /Jul 6\s+–\s+20, 2026/u,
    "Selecting a reviewed range should display both endpoints in the compact same-month format");
  await reportingRange.click();
  await page.getByRole("menuitem", { name: "All available dates", exact: true }).click();
  assert.ok(await page.locator(".metric-mini-trend").count() >= 4,
    "Every KPI with reviewed historical observations should display its compact gradient sparkline");
  const usageTrend = page.locator('[data-component-id="usage-trend"]');
  assert.equal(await usageTrend.locator(".recharts-area").count(), 0,
    "Line charts must not render an area beneath the reviewed line");
  assert.equal(await usageTrend.locator(".trend-target-summary, .trend-target-marker").count(), 0,
    "The primary trend should not repeat target progress in a separate summary strip");
  await page.screenshot({ path: join(screenshots, "desktop-baseline.png"), fullPage: true });

  for (const width of [1440, 760]) {
    await page.setViewportSize({ width, height: 1050 });
    for (const trigger of await page.getByRole("button", { name: "More information" }).all()) {
      await trigger.evaluate((button) => button.scrollIntoView({ block: "center" }));
      await trigger.hover();
      const tooltip = await trigger.evaluate((button) => {
        const description = document.getElementById(button.getAttribute("aria-describedby"));
        if (!description) return { section: button.closest("[data-component-id]")?.dataset.componentId,
          visible: "missing", clippedBy: [] };
        const bounds = description.getBoundingClientRect();
        const component = button.closest("[data-component-id]");
        const componentBounds = component?.getBoundingClientRect();
        const componentLeft = componentBounds ? Math.max(12, componentBounds.left + 12) : 0;
        const componentRight = componentBounds
          ? Math.min(window.innerWidth - 12, componentBounds.right - 12) : window.innerWidth;
        const clippedBy = [];
        for (let ancestor = description.parentElement; ancestor && ancestor !== document.body; ancestor = ancestor.parentElement) {
          const style = getComputedStyle(ancestor);
          const frame = ancestor.getBoundingClientRect();
          if ([style.overflow, style.overflowX, style.overflowY].some((value) => /hidden|clip|auto|scroll/.test(value)) &&
            (bounds.left < frame.left || bounds.right > frame.right || bounds.top < frame.top || bounds.bottom > frame.bottom)) {
            clippedBy.push(ancestor.className);
          }
        }
        return { section: button.closest("[data-component-id]")?.dataset.componentId,
          visible: getComputedStyle(description).visibility,
          whiteSpace: getComputedStyle(description).whiteSpace,
          contentOverflow: description.scrollWidth - description.clientWidth,
          left: bounds.left,
          right: bounds.right,
          viewportWidth: window.innerWidth,
          componentLeft,
          componentRight,
          clippedBy,
          portaled: description.parentElement === document.body };
      });
      assert.equal(tooltip.visible, "visible", `${tooltip.section} information tooltip does not open on hover`);
      assert.equal(tooltip.portaled, true,
        `${tooltip.section} information tooltip should escape authored overflow through the document-level overlay`);
      assert.equal(tooltip.whiteSpace, "normal", `${tooltip.section} information tooltip does not wrap`);
      assert.ok(tooltip.contentOverflow <= 1,
        `${tooltip.section} information tooltip content overflows by ${tooltip.contentOverflow}px`);
      assert.ok(tooltip.left >= -1 && tooltip.right <= tooltip.viewportWidth + 1,
        `${tooltip.section} information tooltip escapes the viewport: ${JSON.stringify(tooltip)}`);
      if (tooltip.componentRight - tooltip.componentLeft >= 180) {
        assert.ok(tooltip.left >= tooltip.componentLeft - 1 && tooltip.right <= tooltip.componentRight + 1,
          `${tooltip.section} information tooltip escapes its component: ${JSON.stringify(tooltip)}`);
      }
      assert.deepEqual(tooltip.clippedBy, [], `${tooltip.section} information tooltip is clipped by ${tooltip.clippedBy.join(", ")}`);
    }
  }

  for (const width of [820, 760, 390]) {
    await page.setViewportSize({ width, height: 1050 });
    const metrics = await page.locator(".metric-item").evaluateAll((items) => items.map((metric) => {
      const card = metric.getBoundingClientRect();
      const children = [...metric.querySelectorAll(".comparison, .metric-detail, .metric-sparkline, .data-metric-value, .data-metric-change, .data-metric-sparkline")].map((element) => {
        const bounds = element.getBoundingClientRect();
        return { className: element.className, left: bounds.left, right: bounds.right, top: bounds.top, bottom: bounds.bottom,
          contentOverflow: element.scrollWidth - element.clientWidth,
          text: element.classList.contains("metric-sparkline") ? element.innerText.trim() : undefined };
      });
      const value = metric.querySelector(".data-metric-value").getBoundingClientRect();
      const change = metric.querySelector(".data-metric-change").getBoundingClientRect();
      return { id: metric.dataset.componentId, left: card.left, right: card.right, children,
        separated: value.bottom <= change.top };
    }));
    assert.ok(metrics.length > 0, "The starter should demonstrate optional metric cards without prescribing their count");
    assert.equal(metrics.some(({ children }) => children.some(({ className }) => className === "metric-sparkline")),
      false, "The starter should not add metric sparklines by default");
    for (const metric of metrics) {
      const value = metric.children.find(child => child.className.includes("data-metric-value"));
      const change = metric.children.find(child => child.className.includes("data-metric-change"));
      if (value && change) assert.ok(value.right <= change.left + 1 || change.right <= value.left + 1 || value.bottom <= change.top + 1 || change.bottom <= value.top + 1,
        `${metric.id} value and comparison overlap at ${width}px: ${JSON.stringify({value, change})}`);
      assert.ok(metric.separated, `${metric.id} value and comparison overlap at ${width}px`);
      for (const child of metric.children) {
        assert.ok(child.left >= metric.left - 1 && child.right <= metric.right + 1,
          `${metric.id} ${child.className} escapes its metric at ${width}px: ${JSON.stringify({ metric, child })}`);
        assert.ok(child.contentOverflow <= 1,
          `${metric.id} ${child.className} content overflows by ${child.contentOverflow}px at ${width}px`);
        if (child.className === "metric-sparkline") {
          assert.equal(child.text, "", `${metric.id} sparkline renders a stray footer label at ${width}px`);
        }
      }
    }
    const pageOverflow = await page.evaluate(() => document.documentElement.scrollWidth - innerWidth);
    assert.ok(pageOverflow <= 1, `KPI content introduces ${pageOverflow}px of page overflow at ${width}px`);
  }
  await page.setViewportSize({ width: 1440, height: 1050 });
  const supportingChart = page.locator('[data-component-id="segment-breakdown"]');
  const rankedRows = supportingChart.locator(".chart-ranked-list-row");
  assert.equal(await rankedRows.count(), 5, "Ranked examples should start compact");
  const rankedLayout = await rankedRows.first().evaluate((row) => {
    const card = row.closest(".dashboard-component");
    const style = getComputedStyle(card);
    return { rowWidth: row.getBoundingClientRect().width, cardWidth: card.getBoundingClientRect().width,
      inset: parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + 2 };
  });
  assert.ok(Math.abs(rankedLayout.rowWidth - rankedLayout.cardWidth + rankedLayout.inset) <= 1,
    `Ranked rows should fill the panel: ${JSON.stringify(rankedLayout)}`);
  await supportingChart.getByRole("button", { name: "Show 7 more" }).click();
  assert.equal(await rankedRows.count(), 12);
  await supportingChart.getByRole("button", { name: "Show fewer" }).click();
  assert.equal(await rankedRows.count(), 5);
  assert.equal(await page.locator('[data-component-id="notes"]').count(), 0,
    "The dashboard should not include an unnecessary Notes block");
  assert.equal(await page.locator(".search").evaluate((input) => getComputedStyle(input).fontSize), "14px");
  const evidenceTable = page.locator('[data-component-id="usage-details"]');
  assert.equal(await evidenceTable.getByRole("columnheader", { name: "Net change" }).count(), 1);
  assert.ok(await evidenceTable.locator(".table-sparkline").count() >= 6,
    "Account rows should show compact trends derived entirely from reviewed account history");
  assert.ok(await evidenceTable.locator(".table-data-bar-track > span").count() >= 6,
    "Comparable engagement scores should show their position within the actual reviewed-account distribution");
  assert.ok(await evidenceTable.locator(".table-distribution-marker").count() >= 6,
    "Each reviewed-account distribution needs a marker for that specific account's engagement score");
  assert.ok(await evidenceTable.locator(".table-status[data-status]").count() >= 6,
    "Reviewed risk tiers should remain legible as colored text chips");
  assert.equal(await evidenceTable.locator(".table-status > span").count(), 0,
    "Risk chips should not include redundant colored dots");
  const usageTrendVisual = evidenceTable.locator(".table-cell-sparkline .table-visual-trigger").first();
  await usageTrendVisual.hover({ position: { x: 2, y: 10 } });
  const usageTooltip = page.getByRole("tooltip").filter({ hasText: "Usage trend" });
  const firstObservation = await usageTooltip.locator("strong").innerText();
  const usageBounds = await usageTrendVisual.boundingBox();
  await usageTrendVisual.hover({ position: { x: usageBounds.width - 2, y: 10 } });
  assert.notEqual(await usageTooltip.locator("strong").innerText(), firstObservation,
    "Reviewed usage sparklines should track the hovered observation");
  assert.doesNotMatch(await usageTooltip.innerText(), /→|\d+ of \d+/u,
    "Sparkline tooltips should not dump the entire history or expose meaningless point indexes");
  assert.equal(await usageTrendVisual.getAttribute("title"), null);
  const engagementVisual = evidenceTable.locator(".table-cell-bar .table-visual-trigger").first();
  await engagementVisual.hover();
  assert.match(await page.getByRole("tooltip").filter({ hasText: "Engagement" }).innerText(), /percentile/u,
    "Engagement distributions should explain the reviewed score and its percentile");
  const deltaCells = await evidenceTable.locator('td[data-delta]').evaluateAll((cells) => {
    const probe = document.createElement("span");
    document.body.append(probe);
    const expected = Object.fromEntries(["positive", "negative"].map((direction) => {
      probe.style.color = `var(--movement-${direction}, var(--${direction}))`;
      return [direction, getComputedStyle(probe).color];
    }));
    probe.remove();
    return cells.map((cell) => ({ direction: cell.dataset.delta, value: cell.innerText,
      color: getComputedStyle(cell).color, expected: expected[cell.dataset.delta] }));
  });
  assert.ok(deltaCells.some(({ direction, value }) => direction === "positive" && /^\+/.test(value)),
    "The example evidence table should include explicitly positive signed changes");
  assert.ok(deltaCells.some(({ direction, value }) => direction === "negative" && /^-/.test(value)),
    "The example evidence table should include explicitly negative signed changes");
  assert.ok(deltaCells.every(({ color, expected }) => color === expected),
    "Positive and negative table deltas must use their theme-aware semantic colors");
  const tableFilters = page.getByRole("region", { name: "Account health filters", exact: true });
  const tableHeader = page.locator('[data-sortable-row="dashboard:evidence"] .sortable-row-header');
  assert.ok((await tableHeader.boundingBox()).y < (await evidenceTable.boundingBox()).y,
    "Account health controls belong in the section header above their table");
  const tableProduct = tableFilters.getByRole("button", { name: "Product", exact: true });
  const tableRisk = tableFilters.getByRole("button", { name: "Risk", exact: true });
  assert.equal(await tableProduct.count(), 1, "Account health should offer a section-local product filter");
  assert.equal(await tableRisk.count(), 1, "Account health should offer a section-local risk filter");
  await tableProduct.click();
  await page.getByRole("menuitemradio", { name: "Studio" }).click();
  assert.equal(await evidenceTable.locator("tbody tr").count(), 3,
    "The table product filter should limit reviewed account rows to the selected product");
  await tableRisk.click();
  await page.getByRole("menuitemradio", { name: "Low" }).click();
  assert.equal(await evidenceTable.locator("tbody tr").count(), 2,
    "Product and risk filters should combine without changing other dashboard components");
  await tableRisk.click();
  await page.getByRole("menuitemradio", { name: "All" }).click();
  await tableProduct.click();
  await page.getByRole("menuitemradio", { name: "All" }).click();
  const scenarioPresets = page.getByRole("button", { name: "Scenario", exact: true });
  assert.equal(await scenarioPresets.count(), 0,
    "Direct scenario assumptions should not be duplicated by a redundant preset selector");
  assert.equal(await page.getByRole("button", { name: "Reset baseline" }).count(), 0,
    "Direct scenario assumptions must not introduce a redundant reset action");
  assert.equal(await page.locator('[data-component-id="adoption-scenario"] .recharts-line').count(), 3,
    "Scenario analysis should show only reviewed history, the baseline, and the modeled forecast");
  const activationLift = page.locator(".scenario-lever input").first();
  const scenarioHandleAlignment = await activationLift.locator("..").evaluate((lever) => {
    const handle = lever.querySelector(".data-slider-handle").getBoundingClientRect();
    const control = lever.getBoundingClientRect();
    return Math.abs(handle.top + handle.height / 2 - (control.top + control.height / 2));
  });
  assert.ok(scenarioHandleAlignment <= 1,
    "Scenario slider handles should remain vertically centered within the control");
  const initialProjection = await page.locator(".scenario-value").innerText();
  await activationLift.fill("7");
  assert.notEqual(await page.locator(".scenario-value").innerText(), initialProjection,
    "Changing a scenario assumption must update the modeled projection immediately");
  assert.equal(await page.locator(".scenario-change").getAttribute("data-direction"), "positive",
    "Positive modeled change should expose its semantic direction for theme-aware color treatment");
  await activationLift.fill("0");
  await page.evaluate(() => scrollTo(0, 0));
  for (const colorScheme of ["light", "dark"]) {
    await page.emulateMedia({ colorScheme });
    await page.getByRole("button", { name: "Ask ChatGPT" }).click();
    const item = page.getByRole("dialog", { name: "Ask ChatGPT about this dashboard" }).getByRole("link").first();
    await item.hover();
    const hover = await item.evaluate((element) => ({
      item: getComputedStyle(element).backgroundColor,
      surface: getComputedStyle(element.closest(".dashboard-ask-zero-state")).backgroundColor,
      outline: getComputedStyle(element).outlineStyle,
      scheme: document.documentElement.getAttribute("data-color-scheme"),
      active: getComputedStyle(document.documentElement).getPropertyValue("--interaction-active"),
    }));
    assert.notEqual(hover.item, hover.surface,
      `${colorScheme} menu hover is indistinguishable from its surface: ${JSON.stringify(hover)}`);
    assert.equal(hover.outline, "none", `${colorScheme} menu hover unexpectedly shows a blue focus outline`);
    await page.keyboard.press("Escape");
  }
  await page.emulateMedia({ colorScheme: "light" });

  return { dashboardContentTitles, initialDashboardTitles };
}


// Small, deterministic regression fixture for the reported dashboard polish defects.
export function createDashboardPolishProject(template, project) {
  cpSync(template, project, { recursive: true, filter: (path) => !path.includes("/node_modules") && !path.includes("/dist") });
  const snapshot = JSON.parse(readFileSync(join(template, "src/data.json"), "utf8"));
  snapshot.id = "dashboard-polish-regressions";
  snapshot.title = "Dashboard polish regression fixtures";
  snapshot.filters = [];
  const query = Object.values(snapshot.queries)[0];
  snapshot.queries = {
    weekdays: { ...query, rows: ["Fri", "Mon", "Sat", "Sun", "Thu", "Tue", "Wed"].map((day, i) => ({ day, energy: 100 + i * 20 })) },
    months: { ...query, rows: Array.from({ length: 26 }, (_, i) => ({ month: `${2024 + Math.floor((i + 6) / 12)}-${String((i + 6) % 12 + 1).padStart(2, "0")}`, energy: 50 + (i * 37 % 160) })) },
    scales: { ...query, rows: [{ city: "Alpha", energy: 3000, cost: 30 }, { city: "Beta", energy: 1200, cost: 20 }, { city: "Gamma", energy: 2400, cost: 10 }] },
    shares: { ...query, rows: [0.004321, .75, .06, .04].map((share, i) => ({ name: `Item ${i + 1}`, share })) },
  };
  writeFileSync(join(project, "src/data.json"), JSON.stringify(snapshot));
  writeFileSync(join(project, "src/content/dashboard/DashboardContent.jsx"), `
import React from "react";
import { ChartRenderer, DataComponent, DataTable, SortableItem, SortableRegion, useDataApp } from "../../data-app-public.jsx";
const definitions = [
  { id: "weekdays", title: "Energy by weekday", queryId: "weekdays", chart: { type: "bar", x: "day", y: "energy", yLabel: "kWh" } },
  { id: "scales", title: "Energy and cost by city", queryId: "scales", chart: { type: "bar", x: "city", y: "energy", fields: ["energy", "cost"], yLabel: "kWh" } },
  { id: "months", title: "Energy delivered by month", queryId: "months", chart: { type: "bar", x: "month", y: "energy", yLabel: "kWh" } },
  { id: "line", title: "Line chart without area", queryId: "scales", chart: { type: "line", x: "city", y: "energy", fields: ["energy", "cost"], showArea: true, showYAxisLabel: false } },
  { id: "shares", title: "Share sorting", queryId: "shares" },
  ...["horizontalStackedBar", "horizontalStackedBar100"].map((type) => ({
    id: type, title: type, queryId: "weekdays", chart: { type, x: "day", y: "energy" },
  })),
];
const rows = [{ id: "first", items: ["weekdays", "scales"] }, { id: "monthly", items: ["months"] }, { id: "last", items: ["line", "shares"] },
  { id: "horizontal", items: ["horizontalStackedBar", "horizontalStackedBar100"] }];
export function DashboardContent() {
  const shell = useDataApp();
  return <article><h1>Dashboard polish regression fixtures</h1>
    <SortableRegion id="polish" variant="canvas" columns={12} rows={rows}>
      {definitions.filter(({ id }) => shell.visible(id)).map((definition) => {
        const chart = shell.chartOverrides[definition.id] ?? definition.chart;
        const data = shell.reviewedRows(definition.queryId);
        return <SortableItem key={definition.id} id={definition.id} label={definition.title} kind={definition.chart ? "chart" : "table"} span={definition.id === "months" ? 12 : 6}>
          <DataComponent {...definition} kind={definition.chart ? "chart" : "table"} chart={chart} displayRows={data}>
            {definition.chart ? <ChartRenderer {...shell.chartProps(definition.id)} spec={chart} rows={data} height={240} /> : <DataTable rows={data} columns={[{ field: "name", label: "Name" }, { field: "share", label: "Share", presentation: "percent" }]} />}
          </DataComponent>
        </SortableItem>;
      })}
    </SortableRegion>
  </article>;
}
`);
  const build = runDataAppFixtureBuild(project, { pluginRoot: resolve(template, "../../..") });
  assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`);
  return join(project, "dist/index.html");
}

export async function verifyDashboardPolish({ browser, template, project, screenshots }) {
  const path = createDashboardPolishProject(template, project);
  const page = await browser.newPage({ viewport: { width: 1440, height: 1200 } });
  const errors = [];
  page.on("pageerror", (error) => errors.push(error.message));
  await page.goto(pathToFileURL(path).href);
  await page.getByRole("heading", { name: "Energy by weekday" }).waitFor();
  const component = (id) => page.locator(`[data-component-id="${id}"]`);
  await component("weekdays").locator("svg text[data-axis-layout] tspan").first().waitFor();
  assert.deepEqual(await component("weekdays").locator("svg text[data-axis-layout] tspan").allTextContents(), ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]);
  for (const type of ["horizontalStackedBar", "horizontalStackedBar100"]) {
    await component(type).scrollIntoViewIfNeeded();
    const ticks = component(type).locator(".recharts-yAxis-tick-labels text");
    await ticks.first().waitFor();
    assert.deepEqual(await ticks.evaluateAll((elements) => elements.map((element) => [...element.childNodes]
      .filter((node) => node.nodeType === Node.TEXT_NODE).map((node) => node.textContent).join(""))),
      ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], `${type} should use calendar order`);
  }
  const monthLabels = await component("months").locator('svg text[text-anchor="middle"]').allTextContents();
  assert.ok(monthLabels.includes("Jul 2024") && monthLabels.some(label=>label.endsWith("2026")), JSON.stringify(monthLabels));
  const monthTicks = await component("months").locator('[data-temporal-axis-tick]').evaluateAll(elements => elements.map(element=>({
    month:Number(element.dataset.axisValue.slice(0,4))*12+Number(element.dataset.axisValue.slice(5,7)),x:Number(element.getAttribute("x")),
  })));
  assert.ok(monthTicks.length>=3);
  for (let i=1;i<monthTicks.length;i++) {
    assert.equal(monthTicks[i].month-monthTicks[i-1].month,monthTicks[1].month-monthTicks[0].month,
      "Monthly bucket labels use a constant cadence, without forcing an uneven final interval");
    assert.ok(Math.abs(monthTicks[i].x-monthTicks[i-1].x-(monthTicks[1].x-monthTicks[0].x))<1);
  }
  assert.ok(monthLabels.every((label) => !label.includes("…")));
  assert.equal(await component("line").locator(".recharts-area").count(), 0);
  assert.ok(await component("scales").locator('text[orientation="right"]').count() >= 2);
  assert.deepEqual(await component("scales").locator(".recharts-label").allTextContents(), ["kWh", "Cost"]);
  assert.equal(await component("line").locator(".recharts-label").count(), 0,
    "Hiding Y-axis titles must apply to both axes without hiding ticks");
  assert.ok(await component("line").locator('text[orientation="right"]').count() >= 2);
  await component("shares").getByRole("button", { name: "Share", exact: true }).click();
  await component("shares").getByRole("button", { name: "Share ↑", exact: true }).click();
  assert.deepEqual(await component("shares").locator("tbody tr td:first-child").allTextContents(), ["Item 2", "Item 3", "Item 4", "Item 1"]);
  assert.deepEqual(await component("shares").locator("tbody tr td:last-child").allTextContents(), ["75%", "6%", "4%", "0%"]);
  await page.getByRole("button", { name: "Edit text and layout" }).click();
  const undo = page.getByRole("button", { name: "Undo dashboard change" });
  const redo = page.getByRole("button", { name: "Redo dashboard change" });
  async function checkHistoryTooltip(button, label, keyboard = false) {
    if (keyboard) {
      await page.mouse.move(0, 0);
      await button.focus();
      await page.keyboard.press("Tab");
      await page.keyboard.press("Shift+Tab");
    } else await button.hover();
    const tooltip = button.locator("..").getByRole("tooltip", { name: label, exact: true });
    await page.waitForFunction((element) => {
      for (let node = element; node; node = node.parentElement) {
        if (getComputedStyle(node).opacity !== "1") return false;
      }
      return true;
    }, await tooltip.elementHandle());
  }
  assert.equal(await undo.isEnabled(), false);
  await checkHistoryTooltip(undo, "Undo");
  await component("shares").getByRole("button", { name: "Share sorting actions" }).click();
  await page.getByRole("menuitem", { name: "Hide" }).click();
  await component("shares").waitFor({ state: "detached" });
  await checkHistoryTooltip(undo, "Undo", true);
  await undo.click();
  await component("shares").waitFor();
  await checkHistoryTooltip(redo, "Redo");
  await redo.click();
  await component("shares").waitFor({ state: "detached" });
  await undo.click();
  const handle = page.getByRole("separator", { name: "Resize Energy delivered by month", exact: true });
  const originalWidth = (await component("months").boundingBox()).width;
  await handle.press("ArrowLeft");
  await page.waitForFunction((width) => document.querySelector('[data-component-id="months"]').getBoundingClientRect().width < width - 50, originalWidth);
  const resizedWidth = (await component("months").boundingBox()).width;
  await undo.click();
  await page.waitForFunction((width) => Math.abs(document.querySelector('[data-component-id="months"]').getBoundingClientRect().width - width) < 2, originalWidth);
  await redo.click();
  await page.getByRole("button", { name: "Save", exact: true }).click();
  await page.getByRole("button", { name: "Edit text and layout", exact: true }).waitFor();
  await page.reload();
  await page.getByRole("heading", { name: "Energy by weekday" }).waitFor();
  assert.ok(Math.abs((await component("months").boundingBox()).width - resizedWidth) < 2);
  await page.getByRole("button", { name: "Edit text and layout" }).click();
  const grip = await handle.boundingBox();
  await page.mouse.move(grip.x + grip.width / 2, grip.y + grip.height / 2);
  await page.mouse.down();
  await page.mouse.move(grip.x + grip.width / 2 - originalWidth / 6, grip.y + grip.height / 2, { steps: 8 });
  await page.mouse.up();
  await page.waitForFunction((width) => document.querySelector('[data-component-id="months"]').getBoundingClientRect().width < width - 100, resizedWidth);
  await page.keyboard.press("ControlOrMeta+z");
  await page.waitForFunction((width) => Math.abs(document.querySelector('[data-component-id="months"]').getBoundingClientRect().width - width) < 2, resizedWidth);
  // Watch every resize, not only the final layout: a dropped preview field used to
  // make the standalone row expand during an unrelated reorder, then shrink again.
  await page.evaluate(() => {
    const chart = document.querySelector('[data-component-id="months"]');
    window.polishWidthSamples = [chart.getBoundingClientRect().width];
    window.polishWidthObserver = new ResizeObserver(() => window.polishWidthSamples.push(chart.getBoundingClientRect().width));
    window.polishWidthObserver.observe(chart);
  });
  const topOrder = () => page.locator('[data-sortable-row="first"] > [data-sortable-item-id]')
    .evaluateAll((items) => items.map((item) => item.dataset.sortableItemId));
  for (const reverse of [false, true]) {
    const header = component("weekdays").locator(".component-header");
    // A visible card can still have its header underneath the sticky toolbar.
    // Center the actual drag surface before sending raw pointer coordinates.
    await header.evaluate((element) => element.scrollIntoView({ block: "center", behavior: "instant" }));
    const origin = await header.boundingBox();
    const target = await component("scales").boundingBox();
    await page.mouse.move(origin.x + origin.width * .7, origin.y + origin.height / 2);
    await page.mouse.down();
    await page.mouse.move(target.x + target.width * (reverse ? .1 : .9), origin.y + origin.height / 2, { steps: 12 });
    const expected = reverse ? ["weekdays", "scales"] : ["scales", "weekdays"];
    await page.waitForFunction((ids) => JSON.stringify(Array.from(document.querySelectorAll('[data-sortable-row="first"] > [data-sortable-item-id]'), (item) => item.dataset.sortableItemId)) === JSON.stringify(ids), expected);
    assert.ok(Math.abs((await component("months").boundingBox()).width - resizedWidth) < 2, "Reorder preview must preserve the standalone width");
    await page.mouse.up();
    assert.deepEqual(await topOrder(), expected);
    await undo.click();
    await redo.click();
  }
  const moveWeekdays = page.getByRole("button", { name: "Move Energy by weekday", exact: true });
  for (const finish of ["Escape", "Space"]) {
    await moveWeekdays.press("Space");
    await moveWeekdays.press("ArrowRight");
    assert.deepEqual(await topOrder(), ["scales", "weekdays"]);
    assert.ok(Math.abs((await component("months").boundingBox()).width - resizedWidth) < 2);
    await moveWeekdays.press(finish);
  }
  const widthSamples = await page.evaluate(() => {
    window.polishWidthObserver.disconnect();
    return window.polishWidthSamples;
  });
  assert.ok(widthSamples.every((width) => Math.abs(width - resizedWidth) < 2), `Standalone width changed during reorder: ${widthSamples}`);
  await page.getByRole("button", { name: "Save", exact: true }).click();
  await page.getByRole("button", { name: "Edit text and layout", exact: true }).waitFor();
  await page.reload();
  await component("months").waitFor();
  assert.ok(Math.abs((await component("months").boundingBox()).width - resizedWidth) < 2);
  await page.getByRole("button", { name: "Edit text and layout" }).click();
  await component("scales").getByRole("button", { name: "Energy and cost by city actions" }).click();
  await page.getByRole("menuitem", { name: "Edit chart", exact: true }).click();
  await checkHistoryTooltip(page.getByRole("button", { name: "Undo chart change" }), "Undo");
  await checkHistoryTooltip(page.getByRole("button", { name: "Redo chart change" }), "Redo");
  await page.getByRole("button", { name: "Secondary axis", exact: true }).click();
  await page.getByRole("menuitemradio", { name: "Shared scale", exact: true }).click();
  await page.getByRole("button", { name: "Save", exact: true }).click();
  await page.getByRole("dialog").waitFor({ state: "detached" });
  assert.equal(await component("scales").locator('text[orientation="right"]').count(), 0);
  await undo.click();
  await component("scales").locator('text[orientation="right"]').first().waitFor();
  await component("shares").getByRole("button", { name: "Share", exact: true }).click();
  await component("shares").getByRole("button", { name: "Share ↑", exact: true }).click();
  await page.screenshot({ path: join(screenshots, "dashboard-polish.png"), fullPage: true });
  assert.deepEqual(errors, []);
  await page.close();
}

if (process.argv[2] === "--polish") {
  const { chromium } = await import("playwright-core");
  const { installDashboardBrowserMocks, resolveChromiumExecutable } = await import("./browser-helpers.mjs");
  const { verifyEditorAskChatGPTSelectionModes } = await import("./data-app-browser-ask-chatgpt.mjs");
  const screenshots = mkdtempSync(join(tmpdir(), "data-dashboard-polish-"));
  const project = join(screenshots, "app");
  const template = resolve(dirname(fileURLToPath(import.meta.url)), "../templates/data-app/base");
  const browser = await chromium.launch({
    executablePath: resolveChromiumExecutable(),
    headless: true,
    ignoreDefaultArgs: ['--hide-scrollbars'],
  });
  try {
    // Exercise the real example as well as isolated regressions, using the same
    // assertions as the broader delivery suite rather than duplicating them.
    cpSync(template, project, { recursive: true,
      filter: (path) => !path.includes("/node_modules") && !path.includes("/dist") });
    const build = runDataAppFixtureBuild(project, { pluginRoot: resolve(template, "../../..") });
    assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`);
    const page = await browser.newPage({ viewport: { width: 1440, height: 1050 } });
    const failures = [];
    page.on("pageerror", (error) => failures.push(error.message));
    await installDashboardBrowserMocks(page);
    await page.goto(pathToFileURL(join(project, "dist/index.html")).href);
    await verifyDashboardComposition({ page, failures, screenshots });
    await verifyEditorAskChatGPTSelectionModes(page);
    assert.deepEqual(failures, []);
    await page.close();
    rmSync(project, { recursive: true, force: true });
    await verifyDashboardPolish({ browser, template, project, screenshots });
    console.log(JSON.stringify({ status: "passed", screenshots,
      interactions: ["numeric percentage sorting", "calendar order and month labels", "line without area",
        "separate axes and editor override", "hide/undo/redo", "single-block keyboard and pointer resize",
        "stable standalone width during pointer and keyboard reordering", "undo keyboard shortcut",
        "reload persistence", "undo chart changes"] }, null, 2));
  } finally {
    await browser.close();
    rmSync(project, { recursive: true, force: true });
  }
}

SHA-256: 0a1b072d8c0ba67ba2b9c1f9f12fa8fd61807f4bb42b0fd7d315f55d4b995b7a