@app-studio/qa-studio

中文文档
0.11.6发布于 2026/11/06UNLICENSEDTypeScriptES Module近 30 天下载 1903

State Fixture data shared by component tests and Debug Studio, plus live browser adapters compiled from original App Studio feature specs.

原始使用文档
文档目录6 节
  1. State Fixture v3
  2. React Native and other non-DOM hosts
  3. State Fixtures over local HTTP
  4. Original App Studio feature flows
  5. Targeted state browser adapter
  6. Agent-driven page debugging (local 0.11.4-rc.3)

@app-studio/qa-studio

Debug Studio and browser adapters for two explicit contracts.

The CLI's qa demos run uses this same API renderer and canonical browser compiler/runner to generate Chromium screenshots/video and complete reports automatically. See Automatic demos for incremental selection and temporary provenance-checked proof; /playwright also exports runScenarioCase.

/api exports the framework-free real HTTP runner and mountApiStudio; /react also exports ApiStudio. The workbench edits requests/checks, shows redacted evidence, resumes from native DB checkpoints and records a selected tab/window. The CLI serves it without an application browser build. See API Studio and pnpm api:demo in the workspace.

State Fixture v3

A render-only component or screen state:

{
  "schemaVersion": 3,
  "kind": "state-fixture",
  "screen": "content/notes:NotesListPage",
  "outcome": "worst",
  "states": [{
    "id": "content/notes:empty",
    "summary": "A new workspace has no notes yet",
    "routes": [{ "match": "/notes", "method": "GET", "answer": { "items": [] } }],
    "persona": "connected",
    "viewport": { "preset": "desktop" }
  }]
}

The same fixture feeds component tests, Debug Studio, and qa state run --id. It contains no product actions and makes no end-to-end claim.

import { installState, statesFrom, type StateFixtureFile } from '@app-studio/qa-studio';

The React host supplies StateEntry[], renderState, persona setup, and optional store bootstrap. Deep links use ?state=<id> and the rendered boundary exposes data-state=<id>.

Browser adapters add qa-studio=agent by default. In that presentation, Debug Studio mounts only the selected application pane: no toolbar, state picker, catalog navigation, or human seed warning. Opening the Studio directly without that parameter keeps the full interactive shell. An application-owned adapter can opt back into it with studioPresentation: 'interactive'.

React Native and other non-DOM hosts

@app-studio/qa-studio/fetch exports only installState, statesFrom, validateStateFixtureFile and the State Fixture types. Nothing it reaches touches the DOM or a Node built-in at import, so Metro can bundle it:

import { installState, statesFrom } from '@app-studio/qa-studio/fetch';
import notes from './states/notes.state.json';

const [populated] = statesFrom(notes);
installState(populated); // replaces globalThis.fetch until restore()

node scripts/check-portable-entry.mjs dist/fetch.js (pnpm check:portable) walks the built entry's import graph and fails on Node built-ins, DOM globals at module scope, or a DOM driver / evidence module.

State Fixtures over local HTTP

The Node entrypoint can answer the same State Fixture through a real loopback listener. Each world owns a fresh fixture and a private installState fetch target; it never replaces global fetch or falls back to a real API.

import { randomUUID } from 'node:crypto';
import { createStateFixtureHttpServer } from '@app-studio/qa-studio/node';
import { createFixture } from './fixtures.js'; // Host-owned, browser-free factory.

const token = randomUUID();
const world = await createStateFixtureHttpServer({
  createFixture,
  browserOrigin: 'http://127.0.0.1:4003', // Exact browser origin of this run.
  token,
  tokenHeader: 'x-qa-world-token',
});
try {
  // Host configures its generated SDK with world.origin and the transport header.
  // Keep this world alive across navigation, reloads and new browser sessions.
} finally {
  await world.close();
}

tokenHeader defaults to authorization with Bearer <token>. Prefer x-qa-world-token when the host supports anonymous/login states: add it centrally to SDK requests without changing business Authorization or opening a fake user session. The fixture's persona still determines session setup. This token isolates the mock transport; it does not simulate an ACL or prove real authentication.

Set StudioConfig.mockTransport: 'http', or render a keyed StateFrame with dataMode="mock" mockTransport="http". The host must activate the matching world and await readiness before any session reads or screen mount, using its existing preparation/bootstrap seam. Changing the picker needs a new matching world; it cannot keep another fixture's routes. prepare(state) is awaited before mounting a StateFrame's children. HTTP mode installs no in-page interceptor and Debug Studio skips its unused seed capture. The boundary exposes data-mode="mock", data-mock-transport="http" and data-qa-data-source="state-fixture-http"; live mode keeps data-mode="live" and data-qa-data-source="real-http".

The returned world exposes origin, fixtureId, requests, observations, unmatched and diagnostics metadata (method, path, status, source and failure code; no headers or bodies). reset() drains current requests and invokes the factory again for the same fixture ID. Factories must return fresh mutable state. Reloading must not call reset. close() stops acceptance, closes listener/connections and restores the private target. The host owns its browser/Vite processes, temporary files, signal handling and bounded teardown.

Only GET/POST/PATCH/PUT/DELETE and JSON bodies up to 64 KiB are supported; HEAD, multipart/uploads, SSE and WebSockets are outside this transport. Reuse existing adapters for those capabilities or report the gap. CORS allows the exact loopback browser origin and declared transport/SDK headers. Invalid JSON, wrong origins, missing tokens and undeclared routes fail explicitly; unmapped always fails, regardless of the fixture default. Responses carry x-qa-data-source and x-qa-state-fixture. Production Node execution is refused. The host must also guard its dev browser configuration against production builds and prevent other network egress. Node factories must not import React story barrels.

This API is a source addition: verify the actual installed /node exports and React types before adopting it. A version string alone does not prove the addition is available; consumers need an authorized package/commit/artifact containing it.

Original App Studio feature flows

The original specs/features/*.json files are executable. The core compiler joins feature.userExperience to optional specs/forms/*.json, separates each if condition from the nominal path, and materializes a portable flow for Playwright or wdio/Appium:

import { defineScenarioSuite } from '@app-studio/qa-studio/playwright';
defineScenarioSuite({
  test,
  expect,
  rootDir: process.cwd(),
  pattern: 'specs/features/*.json',
  forms: 'specs/forms/*.json',
  prepare: async ({ scenario }) => prepareFeatureWorld(scenario),
});

User actions operate the control named by the original eventId (or the form's exact data-testid). App actions assert routes, real requests and modal state. Explicit Scenario v3 files remain supported for non-generated repositories, but App Studio projects do not copy their flow into one.

QA_BROWSER_PROFILE=fast runs the same compiled feature flow and writes its machine-readable report without checkpoint media by default. Lightpanda hosts can also request a final diagnostic PNG with qa scenario run --engine lightpanda --profile fast --screenshots. The default proof profile keeps the strict visual evidence. Browser engine selection remains application-owned and is addressed through qa scenario run --engine ....

Targeted state browser adapter

defineStatesSuite({
  test,
  expect,
  rootDir: process.cwd(),
  pattern: 'src/features/**/states/*.state.json',
});

Lightpanda hosts should use @app-studio/qa-studio/puppeteer. Its runPuppeteerScenarioSuite and runPuppeteerStateSuite functions preserve the same JSON graph, request evidence and verdicts without Playwright Test's Chromium-only context emulation. The application still owns server startup, the CDP process, and its State Fixture preparation callback.

Both suites install Lightpanda CSSOM compatibility before preparation or navigation when the page exposes Puppeteer's evaluateOnNewDocument. Minimal custom adapters without that capability remain supported and own their document initialization. The repairs make indexed stylesheets iterable and supply missing media-rule grouping operations needed by libraries such as Stitches. Native implementations stay intact; this enables DOM execution, not CSS layout or visual fidelity. Request interception and application-specific setup remain host-owned.

Custom runners can use the same preparation in their shared page factory:

import { prepareLightpandaPage } from '@app-studio/qa-studio/puppeteer';

async function createPage() {
  const page = await browser.newPage();
  await prepareLightpandaPage(page); // Before the first goto, for states and scenarios.
  return page;
}

Compatibility stays installed for the document lifetime, including later lazy imports and reloads. It is independent of mock-request adoption or release.

Both Puppeteer suites support screenshots: true, or the CLI's QA_BROWSER_SCREENSHOTS=true, to capture the current page before it closes, including failed runs. They write screenshot.png and screenshot.json per case, and emit a JSON line with absolute paths, case status and fidelity: 'markdown'. These are Lightpanda text-layout diagnostics; Chromium remains the engine for strict visual proof. Known historical placeholder PNGs are refused with an upgrade instruction. Capture errors fail the requested run and preserve its original product error.

The suites accept an explicit outputDir, then QA_SCENARIO_OUTPUT_DIR or QA_STATE_OUTPUT_DIR, then <rootDir>/test-results/qa-scenarios or <rootDir>/test-results/qa-states. The CLI derives those environment paths from scenario.evidence and state.evidence in the bindings. A custom page factory only needs Puppeteer's native screenshot method; minimal adapters can omit it when screenshots are disabled. Custom runners can capture a PNG directly:

import { captureLightpandaScreenshot } from '@app-studio/qa-studio/puppeteer';

const path = await captureLightpandaScreenshot(page, 'artifacts/current-page.png');

QA_STATE_ID narrows execution to one fixture. The CLI also sends QA_BROWSER_HEADLESS=true and QA_STUDIO_PRESENTATION=agent. The adapter waits for the real rendered boundary, checks non-empty DOM unless rendersNothing is declared, collects page errors, and reports fabricated API answers. It adds no dwell time, captions, or video requirement.

Agent-driven page debugging (local 0.11.4-rc.3)

BrowserDebugSession from @app-studio/qa-studio/puppeteer joins an existing scenario page and exposes session, observe, reproduce, inspect, control and cursor-based recording operations. The application host retains process startup and server Inspector orchestration. See examples/agent-debug for the six-tool MCP integration, ownership rules, source-map verification and native validation. The native package must contain a verified debugger build; the local host RC can be selected through a package override without an executable environment variable. Older engines can report pausing as unavailable. Inspection defaults to side-effect checks; code execution requires explicit opt-in. Targeted captures pause and resume the live page and affect its timing; they do not provide deterministic replay.

exposeBrowserDebugPage(page, {pageId, baseUrl, build, expiresMs?}) exports the host's existing, already navigated Page through a one-client, opaque loopback WebSocket capability. It returns {pageId, baseUrl, pageDebugEndpoint, close}; never log the endpoint or publish it in diagnostics. The client first sends QA.describePage with the exact pageId, origin and exclusiveControl: true. The additive response scriptInventory.supported advertises compact-v1. Only when advertised, a client can opt in through qaScriptInventory: 'compact-v1' on its existing Debugger.enable request. The relay strips this extension before native CDP and acknowledges the selected mode in that response. Negotiated clients receive bounded QA.scriptParsed metadata instead of full Debugger.scriptParsed events for this lease; legacy clients and direct native CDP retain standard events. Inline map declarations become SHA256/UTF8 length metadata (sourceMapDeferred), with no retained map body. source(id) retrieves only that generated source, recovers its final line/block sourceMappingURL declaration and verifies both digest and length. Missing, stale or overbound declarations refuse resolution; loadedScripts() remains lightweight. The inventory remains capped at1024, retaining mapped/application scripts ahead of helpers, with explicit dropped/unavailable counts in recording. Generated source is limited to2MiB and its encoded relay response to3MiB; oversized source results fail the operation without closing debug control. The relay permits only SDK debugger commands with side effects forbidden and QA.pageAction ({action: 'click', selector} or {action: 'navigate', path} on the registered origin). Close the relay in the host's finally block. Relay cleanup releases only its debugging resources and never closes the page or browser. Initial capability lifetime defaults to five minutes; accepted clients cannot reconnect. Native request IDs and SDK action IDs do not imply server correlation.

The SDK records bounded incident events from real Runtime console warnings/errors and exceptions, and Page dialogs. onEvent(listener) returns an unsubscribe function; it does not wake a background model. Remote values are projected from protocol primitives/descriptions without evaluation or getters. Exception policy is isolate-global: exceptions(policy) requires the actual owner's exceptionPolicyOwner: {baseline, exclusiveIsolate: true} contract. Attachment does not set a policy. Exact exception and promiseRejection pause reasons share exception ownership/incident handling under the same acknowledged policy lease; the raw reason and actual uncaught flag are retained. exceptionStatus().supportedModes comes from actual native capabilities; legacy engines do not implicitly support uncaught prediction. The corrected native callback boundary declares all three modes. Acknowledged leases expire after five minutes; cleanup restores the declared baseline before releasing an owned exception pause. Unknown external baselines refuse mutation; restoration/uncertainty is explicit in exceptionStatus(). The relay can return bounded actual owner Page state from an afterDetach callback through its typed release operation.

reproduce, control, and waitForAction accept an optional host AbortSignal for cancelling their waits. Cancellation removes wait listeners/timers; it does not cancel an already dispatched page action, resume a pause, or stop the session.

版本与发布记录

@app-studio/qa-studio 共发布 37 个版本,首次发布于 2026/08/09;过去 12 个月发布了 37 个版本。

版本发布日期距今
0.11.6latest2026/11/062 天前
0.11.32026/11/017 天前
0.11.22026/10/2217 天前
0.11.12026/10/1622 天前
0.10.42026/10/081 个月前
0.10.3next2026/10/081 个月前

关于 @app-studio/qa-studio 的常见问题

@app-studio/qa-studio 是什么?

State Fixture data shared by component tests and Debug Studio, plus live browser adapters compiled from original App Studio feature specs.

如何安装 @app-studio/qa-studio?

在项目目录运行 npm install @app-studio/qa-studio,或使用 pnpm add / yarn add / bun add。

@app-studio/qa-studio 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@app-studio/qa-studio 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@app-studio/qa-studio 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@app-studio/qa-studio 可以商用吗?

@app-studio/qa-studio 使用 UNLICENSED 许可。作者声明为 UNLICENSED,即不授予公开使用许可(与公共领域的 Unlicense 不同)。使用前需获得作者授权。以上为概括,不构成法律意见,请以许可原文为准。

@app-studio/qa-studio 的下载量有多少?

根据 npm 官方下载统计,@app-studio/qa-studio 在 2026/10/07 – 2026/11/05 这 30 天内被下载 1,903 次,日均约 63 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@app-studio/qa-studio 还在维护吗?

最新版本 0.11.6 发布于 2026/11/06(2 天前);至今共发布 37 个版本,其中过去 12 个月发布了 37 个;首次发布于 2026/08/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。