Ctrl K
ComunidadGuías…

scripts/generate-visual-regression-baseline.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SHA="$(git -C "$ROOT" rev-parse --short HEAD)"
OUT="${1:-$ROOT/flinttrade-design/baselines/visual-regression/$SHA}"
TERM_PORT="${FLINTTRADE_TERMINAL_PORT:-5173}"
SITE_PORT="${FLINTTRADE_SITE_PORT:-3000}"
export OUT TERM_PORT SITE_PORT
mkdir -p "$OUT"

cleanup() {
  if [[ -n "${TERM_PID:-}" ]]; then kill "$TERM_PID" 2>/dev/null || true; fi
  if [[ -n "${SITE_PID:-}" ]]; then kill "$SITE_PID" 2>/dev/null || true; fi
}
trap cleanup EXIT

(
  cd "$ROOT/packages/apps/terminal"
  npm run dev -- --host 127.0.0.1 --port "$TERM_PORT"
) >/tmp/flinttrade-terminal-visual-baseline.log 2>&1 &
TERM_PID=$!

(
  cd "$ROOT/packages/apps/site"
  npm run dev -- --hostname 127.0.0.1 --port "$SITE_PORT"
) >/tmp/flinttrade-site-visual-baseline.log 2>&1 &
SITE_PID=$!

node <<'NODE'
const fs = require("node:fs");
const path = require("node:path");
const { chromium } = require("./packages/apps/terminal/node_modules/playwright");

const root = process.cwd();
const out = process.env.OUT;
const termPort = process.env.TERM_PORT;
const sitePort = process.env.SITE_PORT;
const terminalRoutes = [
  "/", "/welcome", "/explore", "/setup", "/setup-account", "/home",
  "/trade", "/terminal", "/invest", "/learn", "/lab", "/automate",
  "/ai", "/ditto", "/settings", "/admin", "/login", "/missing-route-for-404"
];
const siteRoutes = ["/", "/docs", "/api-reference", "/mcp", "/contribute", "/api/mcp", "/api/search", "/missing-route-for-404"];
const terminalRouteText = {
  "/": "Home",
  "/welcome": "Explore FlintTrade",
  "/explore": "Explore FlintTrade",
  "/setup": "Welcome to FlintTrade",
  "/setup-account": "Set up FlintTrade",
  "/home": "Home",
  "/trade": "Trading Workspace",
  "/terminal": "Trading Workspace",
  "/invest": "Investment Dashboard",
  "/learn": "Learning Centre",
  "/lab": "Strategy Lab",
  "/automate": "Automation Hub",
  "/ai": "AI Centre",
  "/ditto": "Multi-Account Manager",
  "/settings": "Settings",
  "/admin": "Admin Dashboard",
  "/login": "Page not found",
  "/missing-route-for-404": "Page not found",
};
const terminalAppRoutes = new Set([
  "/home", "/trade", "/terminal", "/invest", "/learn", "/lab",
  "/automate", "/ai", "/ditto", "/settings", "/admin"
]);
const viewports = [
  { name: "1920x1080", width: 1920, height: 1080 },
  { name: "1366x768", width: 1366, height: 768 },
  { name: "768x1024", width: 768, height: 1024 },
];
const themes = ["dark", "light"];
const densities = ["compact", "comfortable"];

async function waitFor(url) {
  const started = Date.now();
  while (Date.now() - started < 90000) {
    try {
      const res = await fetch(url);
      if (res.status < 500) return;
    } catch {}
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  throw new Error(`timed out waiting for ${url}`);
}

function slug(route) {
  if (route === "/") return "root";
  return route.replace(/^\//, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "") || "root";
}

function settingsState(density) {
  return {
    persona: "trader",
    density,
    fontSize: "normal",
    defaultExchange: "NFO",
    defaultProduct: "MIS",
    defaultQty: 1,
    defaultOrderType: "MARKET",
    riskLimits: {
      maxPositionLots: 10,
      mtmStoploss: 5000,
      mtmTarget: 10000,
      maxOrdersPerMinute: 30,
    },
    llm: { provider: "", model: "", host: "", apiKey: "" },
    telegram: { enabled: false, botToken: "", chatId: "" },
    dataPaths: { fastStoragePath: "", archiveStoragePath: "" },
    name: "Visual Baseline",
    interests: [],
    experience: "intermediate",
    lastOpenTimestamp: 0,
    tickerMode: "marquee",
    tickerSymbols: [
      "NSE_INDEX:NIFTY",
      "BSE_INDEX:SENSEX",
      "NSE_INDEX:BANKNIFTY",
      "NSE_INDEX:INDIAVIX",
      "MCX:GOLD",
      "MCX:SILVER",
      "MCX:CRUDEOIL",
      "MCX:NATURALGAS",
    ],
    tickerSpeed: 30,
  };
}

function terminalInitState(theme, density) {
  return {
    theme: {
      state: {
        activeThemeId: "graphite",
        mode: theme,
        customThemes: [],
        glass: true,
        reduceMotion: true,
      },
      version: 4,
    },
    settings: {
      state: settingsState(density),
      version: 7,
    },
    mode: {
      state: { mode: "explore" },
      version: 2,
    },
  };
}

async function waitForTerminalRoute(page, route) {
  const expected = terminalRouteText[route] ?? "FlintTrade";
  await page.waitForLoadState("domcontentloaded", { timeout: 30000 });

  if (terminalAppRoutes.has(route)) {
    await page.waitForSelector("main", { timeout: 30000 });
  }

  await page.waitForFunction(
    ({ expected, allowLandmarkMatch }) => {
      const text = document.body?.innerText ?? "";
      const title = allowLandmarkMatch ? document.title ?? "" : "";
      const mainLabels = allowLandmarkMatch
        ? Array.from(document.querySelectorAll("main"))
            .map((node) => node.getAttribute("aria-label") ?? "")
            .join(" ")
        : "";
      return (
        (text.includes(expected) || title.includes(expected) || mainLabels.includes(expected)) &&
        !text.includes("Loading FlintTrade...")
      );
    },
    { expected, allowLandmarkMatch: route === "/" || terminalAppRoutes.has(route) },
    { timeout: 30000 },
  );
  await page.waitForTimeout(route === "/welcome" || route === "/" ? 600 : 250);
}

async function capture(browser, app, baseUrl, route) {
  for (const viewport of viewports) {
    for (const theme of themes) {
      for (const density of densities) {
        const page = await browser.newPage({ viewport });
        if (app === "terminal") {
          await page.addInitScript(({ theme, density, state }) => {
            localStorage.setItem("flinttrade-theme", JSON.stringify(state.theme));
            localStorage.setItem("flinttrade:settings", JSON.stringify(state.settings));
            localStorage.setItem("flinttrade:mode", JSON.stringify(state.mode));
            localStorage.setItem("flinttrade:demoChoice", "explore");
            localStorage.setItem("flinttrade:demo-session", "active");
            localStorage.setItem("flinttrade:tourComplete", "true");
            sessionStorage.setItem("flinttrade:dailyWelcomeDismissed", "true");
            sessionStorage.setItem("flinttrade:smallScreenDismissed", "true");
            sessionStorage.setItem("flinttrade:greeted-today", new Date().toDateString());
            document.documentElement.dataset.theme = "graphite";
            document.documentElement.dataset.mode = theme;
            document.documentElement.dataset.density = density;
            document.documentElement.classList.toggle("dark", theme === "dark");
            document.documentElement.classList.toggle("theme-light", theme === "light");
            document.documentElement.classList.toggle("density-compact", density === "compact");
            document.documentElement.classList.toggle("density-comfortable", density === "comfortable");
          }, { theme, density, state: terminalInitState(theme, density) });
        } else {
          await page.addInitScript(({ theme, density }) => {
            localStorage.setItem("flinttrade.theme", theme);
            localStorage.setItem("flinttrade.density", density);
            document.documentElement.dataset.theme = theme;
            document.documentElement.dataset.density = density;
          }, { theme, density });
        }
        await page.goto(`${baseUrl}${route}`, { waitUntil: "networkidle", timeout: 30000 });
        if (app === "terminal") {
          await waitForTerminalRoute(page, route);
        }
        const dir = path.join(out, app, slug(route), viewport.name, theme);
        fs.mkdirSync(dir, { recursive: true });
        await page.screenshot({ path: path.join(dir, `${density}.png`), fullPage: false });
        await page.close();
      }
    }
  }
}

(async () => {
  await waitFor(`http://127.0.0.1:${termPort}`);
  await waitFor(`http://127.0.0.1:${sitePort}`);
  const browser = await chromium.launch();
  try {
    for (const route of terminalRoutes) {
      await capture(browser, "terminal", `http://127.0.0.1:${termPort}`, route);
    }
    for (const route of siteRoutes) {
      await capture(browser, "site", `http://127.0.0.1:${sitePort}`, route);
    }
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exit(1);
});
NODE