← Files DataARCHIVED FILE

tests/data-app-complete-view-browser.smoke.mjs

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

↓ Download file

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

const pluginRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const root = mkdtempSync(join(tmpdir(), "data-complete-view-"));
const errors = [], writes = [];
const browser = await chromium.launch({ executablePath: resolveChromiumExecutable(), headless: true });
try {
  for (const surface of ["dashboard", "report"]) {
    const project = join(root, surface);
    cpSync(join(pluginRoot, "templates/data-app/base"), project, {recursive:true,
      filter:path=>!path.includes("/node_modules") && !path.includes("/dist")});
    const snapshot = JSON.parse(readFileSync(join(project,"src/data.json")));
    snapshot.surface=surface;
    snapshot.id=`${surface}:complete-view-qa`;
    snapshot.filters=[{id:"cut",label:"Category",field:"category",defaultValue:"all",shareInUrl:false,visible:true}];
    snapshot.queries={q:{rows:[{category:"A",day:"2026-09-01",value:10},{category:"B",day:"2026-09-01",value:30}],source:{label:"Synthetic reviewed QA rows",kind:"synthetic"}}};
    writeFileSync(join(project,"src/data.json"),JSON.stringify(snapshot));
    writeFileSync(join(project,`src/content/${surface}/${surface === "report" ? "Report" : "Dashboard"}Content.jsx`),`
import React from "react";
import { Filters, useDataApp, useSectionFilters, useDashboardTabs, EvidenceChart } from "../../data-app-public.jsx";
const tabs=[{id:"dashboard",label:"Overview",filterIds:["cut"],focusFields:["category"]},{id:"detail",label:"Detail",filterIds:["cut"],defaultFilters:{cut:"B"},focusFields:["category"]}];
const sectionDefinitions=[{id:"cut",label:"Section category",field:"category",defaultValue:"all"}];
export function ${surface === "report" ? "Report" : "Dashboard"}Content(){
  const shell=useDataApp();
  ${surface === "dashboard" ? "useDashboardTabs(tabs);" : ""}
  const left=useSectionFilters(sectionDefinitions,{}, {id:"left",label:"Left cuts"});
  const right=useSectionFilters(sectionDefinitions,{}, {id:"right",label:"Right cuts"});
  return <section><Filters filters={shell.snapshot.filters} queries={shell.queries} values={shell.filters} onChange={shell.setFilter}/>
    <Filters {...left.filterProps}/><Filters {...right.filterProps}/>
    <button onClick={()=>shell.setAssumptions({activationLift:12,retentionLift:3})}>Set scenario</button>
    <button onClick={()=>shell.updateChartState("qa-chart",{zoomRange:{start:"2026-09-01",end:"2026-09-01"},inlineFilters:{category:"A"}})}>Choose chart view</button>
    <button onClick={()=>shell.setDashboardFocus({category:"B"})}>Focus category</button>
    <button onClick={()=>shell.setAssumptions(Object.fromEntries(Array.from({length:2049},(_,i)=>["input"+i,1])))}>Oversized scenario</button>
    <output data-view-proof>{JSON.stringify({filters:shell.filters,left:left.reviewedRows("q"),right:right.reviewedRows("q"),assumptions:shell.assumptions,charts:shell.chartStates,focus:shell.viewFocus,tab:shell.activeTabId})}</output>
    <EvidenceChart id="qa-chart" queryId="q" title="Reviewed category values" spec={{type:"bar",x:"day",y:"value",series:"category"}} rows={shell.reviewedRows("q")} sourceRows={shell.reviewedRows("q")}/>
  </section>;
}`);
    const build=runDataAppFixtureBuild(project,{pluginRoot});
    assert.equal(build.status,0,build.stdout+build.stderr);
    const html=readFileSync(join(project,"dist/index.html"),"utf8");
    const origin=`https://complete-view-${surface}.chatgpt.site`;
    async function open(url) {
      const context=await browser.newContext();
      await context.addInitScript(() => Object.defineProperty(navigator, "clipboard", {
        configurable: true, value: { writeText: async text => { window.__copiedViewLink = text; } },
      }));
      const page=await context.newPage();
      page.on("pageerror",e=>errors.push(e.message));
      await page.route(`${origin}/**`,route=>{
        const req=route.request(), path=new URL(req.url()).pathname;
        if (!["GET","HEAD"].includes(req.method())) writes.push(req.method()+" "+path);
        const payload=path==="/api/snapshot"?snapshot:path==="/api/presentation"?{canEdit:false,presentation:{},revision:0}:null;
        return route.fulfill(payload?{contentType:"application/json",body:JSON.stringify(payload)}:{contentType:"text/html",body:html});
      });
      await page.goto(url);
      await page.locator("[data-view-proof]").waitFor();
      return page;
    }
    const proof=async page=>JSON.parse(await page.locator("[data-view-proof]").innerText());
    const sender=await open(origin+"/?view=1");
    await sender.getByRole("button",{name:"Set scenario",exact:true}).click();
    const legend=sender.locator('[data-component-id="qa-chart"]').getByRole("button",{name:"Toggle B",exact:true});
    assert.equal(await legend.getAttribute("aria-pressed"),"true");
    await legend.click();
    assert.equal(await legend.getAttribute("aria-pressed"),"false");
    await sender.getByRole("button",{name:"Choose chart view",exact:true}).click();
    if(surface==="dashboard") await sender.getByRole("button",{name:"Focus category",exact:true}).click();
    await sender.locator('[aria-label="Left cuts"]').getByRole("button",{name:"Section category",exact:true}).click();
    await sender.getByRole("menuitemradio",{name:"A",exact:true}).click();
    await sender.waitForURL(url=>url.searchParams.has("s") && url.searchParams.has("a") && url.searchParams.has("c"));
    const selected=await proof(sender);
    assert.equal(selected.left.length,1);
    assert.equal(selected.right.length,2,"Sections with identical filter IDs must stay independent.");
    await sender.getByRole("button", { name: "More", exact: true }).click();
    await sender.getByRole("menuitem", { name: "Copy link", exact: true }).click();
    const shared=await sender.evaluate(() => window.__copiedViewLink);
    assert.equal(shared, sender.url(), "Copy link must preserve every selected view parameter.");
    await sender.getByRole("button", { name: "Ask ChatGPT", exact: true }).click();
    const summaryHref = new URL(await sender.getByRole("link", { name: "Share key insights" }).getAttribute("href"));
    const summaryPrompt = summaryHref.searchParams.get("q") ?? summaryHref.searchParams.get("prompt");
    const summaryViewUrl = /\]\(<(https?:\/\/[^>]+)>\)/u.exec(summaryPrompt)?.[1];
    assert.equal(summaryViewUrl, shared, "The summary workflow must receive the complete selected view for its outgoing link.");
    await sender.keyboard.press("Escape");
    const recipient=await open(summaryViewUrl);
    assert.deepEqual(await proof(recipient),selected,"Fresh recipients must reproduce all declared view controls.");
    assert.equal(await recipient.locator('[data-component-id="qa-chart"]').getByRole("button",{name:"Toggle B",exact:true}).getAttribute("aria-pressed"),"false",
      "The restored chart legend must reflect the sender's actual UI selection.");
    await recipient.reload();
    assert.deepEqual(await proof(recipient),selected);
    if(surface==="dashboard") {
      await sender.getByRole("tab",{name:"Detail",exact:true}).click();
      assert.equal((await proof(sender)).filters.cut,"B");
      const detail=sender.url();
      const reader=await open(detail);
      await reader.getByRole("tab",{name:"Overview",exact:true}).click();
      assert.deepEqual(await proof(reader),selected,"Returning to an inactive tab preserves its sender-selected cuts/focus.");
      await reader.goBack();
      assert.equal((await proof(reader)).filters.cut,"B");
      await reader.context().close();
    }
    await recipient.goto(origin+"/?view=1");
    const reset=await proof(recipient);
    assert.deepEqual(reset.assumptions,{activationLift:0,retentionLift:0});
    assert.deepEqual(reset.charts,{});
    assert.equal(reset.left.length,2);
    assert.equal(reset.filters.cut,"all");
    if(surface==="dashboard") {
      await recipient.getByRole("button",{name:"Oversized scenario",exact:true}).click();
      await recipient.getByText("This view has too many selections to fit in a shareable link. Reduce the selections before sharing",{exact:true}).waitFor();
      await recipient.getByRole("tab",{name:"Detail",exact:true}).click();
      assert.equal((await proof(recipient)).tab,"dashboard","Unrepresentable views must not navigate with partial state.");
    }
    await sender.context().close();await recipient.context().close();
  }
  assert.deepEqual(errors,[]);assert.deepEqual(writes,[]);
  process.stdout.write("Complete-view dashboard/report roundtrips, independent sections, scenarios, chart state, tab history and zero shared writes passed.\n");
} finally { await browser.close();rmSync(root,{recursive:true,force:true}); }

SHA-256: 1366c88416d91aa0a09ef3949af6987d4bacb13cd78fe6d653237fa6ef74662f