mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-18 00:29:42 +03:00
refactor(web-test): этап A.1 — выделить module-level state в core/state.mjs
Состояние движка (browser, page, sessionPrefix, seanceId, recorder, контексты, константы, normYo, isConnected/ensureConnected/getPage) переехало в core/state.mjs. Импортируется как live-binding; присваивания в browser.mjs конвертированы в setX(...) — ESM imports read-only. Публичный API не меняется (56 экспортов). Регресс 19/19 зелёный. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
// web-test core/state v1.16 — module-level state for the web-test engine.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
//
|
||||
// Holds the single browser/page/recorder slot plus the multi-context registry,
|
||||
// constants, and small state-only utilities (ensureConnected, getPage,
|
||||
// resolveProjectPath, normYo). Mutable values are exported as `let` bindings
|
||||
// for live read access from consumer modules; writes go through setters so
|
||||
// imported bindings stay read-only at the import site.
|
||||
|
||||
import { dirname, resolve as pathResolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// Project root: 4 levels up from .claude/skills/web-test/scripts/core/state.mjs
|
||||
const __fn_state = fileURLToPath(import.meta.url);
|
||||
export const projectRoot = pathResolve(dirname(__fn_state), '..', '..', '..', '..', '..');
|
||||
|
||||
/** Resolve a user-provided path relative to the project root (not cwd). */
|
||||
export const resolveProjectPath = (p) => pathResolve(projectRoot, p);
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Mutable single-session state. Importers read via the live binding; writes
|
||||
// must go through the corresponding setter (ESM imports are read-only).
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export let browser = null;
|
||||
export let page = null;
|
||||
export let sessionPrefix = null; // e.g. "http://localhost:8081/bpdemo/ru_RU"
|
||||
export let seanceId = null;
|
||||
export let recorder = null; // { cdp, ffmpeg, startTime, outputPath, ffmpegError, captions }
|
||||
export let lastCaptions = []; // captions from the last completed recording (for addNarration)
|
||||
export let lastRecordingDuration = null; // wall-clock duration of the last recording (seconds)
|
||||
export let highlightMode = false;
|
||||
export let persistentUserDataDir = null; // temp dir for launchPersistentContext, cleaned on disconnect
|
||||
|
||||
// Clipboard preservation: save full clipboard contents (all MIME types) right
|
||||
// before each writeText+Ctrl+V pair, restore right after. Toggled via
|
||||
// setPreserveClipboard() from run.mjs.
|
||||
export let preserveClipboard = true;
|
||||
export let clipboardWarnLogged = false;
|
||||
|
||||
export const setBrowser = (v) => { browser = v; };
|
||||
export const setPage = (v) => { page = v; };
|
||||
export const setSessionPrefix = (v) => { sessionPrefix = v; };
|
||||
export const setSeanceId = (v) => { seanceId = v; };
|
||||
export const setRecorder = (v) => { recorder = v; };
|
||||
export const setLastCaptions = (v) => { lastCaptions = v; };
|
||||
export const setLastRecordingDuration = (v) => { lastRecordingDuration = v; };
|
||||
export const setHighlightMode = (v) => { highlightMode = !!v; };
|
||||
export const setPersistentUserDataDir = (v) => { persistentUserDataDir = v; };
|
||||
export const setPreserveClipboard = (v) => { preserveClipboard = !!v; };
|
||||
export const setClipboardWarnLogged = (v) => { clipboardWarnLogged = !!v; };
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Multi-context registry: name → { context, page, sessionPrefix, seanceId,
|
||||
// recorder, lastCaptions, lastRecordingDuration, highlightMode }.
|
||||
// Populated by createContext(); module-level vars above mirror the active
|
||||
// slot. connect() does NOT use this Map — it preserves legacy single-session
|
||||
// behavior for exec/run/start.
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const contexts = new Map();
|
||||
export let activeContextName = null;
|
||||
// Isolation mode for the current cmdTest session — set by the first
|
||||
// createContext call. 'tab': all contexts share one persistent context
|
||||
// (one window, multiple tabs, extension loads reliably). 'window': each
|
||||
// context gets its own BrowserContext (separate window per context, full
|
||||
// cookie isolation, extension may not load).
|
||||
export let activeMode = null;
|
||||
|
||||
export const setActiveContextName = (v) => { activeContextName = v; };
|
||||
export const setActiveMode = (v) => { activeMode = v; };
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Constants.
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const LOAD_TIMEOUT = 60000;
|
||||
export const INIT_TIMEOUT = 60000;
|
||||
export const ACTION_WAIT = 2000; // fallback minimum wait
|
||||
export const MAX_WAIT = 10000; // max wait for stability
|
||||
export const POLL_INTERVAL = 200; // polling interval
|
||||
export const STABLE_CYCLES = 3; // consecutive stable cycles needed
|
||||
|
||||
// 1C browser extension ID (stable across versions, defined by key in manifest.json)
|
||||
export const EXT_ID = 'pbhelknnhilelbnhfpcjlcabhmfangik';
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// Utilities that only depend on state.
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Normalize ё→е and →space for fuzzy matching. */
|
||||
export const normYo = (s) => s.replace(/ё/gi, 'е').replace(/ /g, ' ');
|
||||
|
||||
/** Check if browser is connected and page is usable. */
|
||||
export function isConnected() {
|
||||
if (!browser || !page || page.isClosed()) return false;
|
||||
// launchPersistentContext returns BrowserContext (no isConnected), launch returns Browser
|
||||
if (typeof browser.isConnected === 'function') return browser.isConnected();
|
||||
// For persistent context, check via context's browser()
|
||||
return browser.browser()?.isConnected() ?? false;
|
||||
}
|
||||
|
||||
export function ensureConnected() {
|
||||
if (!isConnected()) {
|
||||
throw new Error('Browser not connected. Call web_connect first.');
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the raw Playwright page object (for advanced scripting in skill mode). */
|
||||
export function getPage() {
|
||||
ensureConnected();
|
||||
return page;
|
||||
}
|
||||
Reference in New Issue
Block a user