Open Source Project #247: wxa-runner — Run WeChat Mini Programs in a Plain Browser on the Official Base Library, with Headless Playwright E2E

wxa-runner, from open-wxa, is a browser runner for WeChat Mini Programs. It implements only the WeChat client's native layer (message bridge, page stack, native APIs) and reuses the official base library, compiler, and bridge protocol, so mini programs run in an ordinary browser for local debugging and Playwright-based headless E2E testing — no developer tools needed on Linux CI. Supports native mini programs and uni-app / Taro build output. MIT, 556 Stars.

·13 min read·AI Engineering

Introduction

E2E testing for mini programs has long been stuck on one fact: the developer tool is desktop-only.

This is #247 in the "One Open Source Project a Day" series. Today's project is wxa-runner (open-wxa/wxa-runner).

Anyone who has automated WeChat Mini Program tests has likely hit this wall: the official developer tool exists only for Windows and macOS, so it can't go into Linux CI. And if you want a modern tool like Playwright, there's no mini-program runtime that lives in a browser. H5-style approaches run, but their rendering and components are reimplemented by a framework, so they always drift from a real device.

wxa-runner's idea is blunt: don't rewrite the base library — move the official WeChat base library into the browser as-is, and only supply the "native shell" of the WeChat client around it.

What You'll Learn

  • What problem wxa-runner solves, and what it does not
  • Its core idea: implement only the native layer, reuse the official base library and compiler
  • The layered architecture and each package's role
  • How the logic and render layers run in a browser, and how WXML/WXSS are compiled
  • Its relationship to the developer tool, and how it runs on Linux CI
  • Known limitations and where it fits

Prerequisites

  • Familiarity with the basic structure of WeChat Mini Programs (logic layer / render layer, WXML / WXSS, setData)
  • Experience with Playwright or similar browser automation tools
  • Some knowledge of uni-app or Taro helps

Project Background

Overview

In one sentence: run mini programs in an ordinary browser on top of the official WeChat base library, for local debugging and E2E automation.

Its boundary first: it does not replace the WeChat developer tool. Preview, upload, and on-device debugging still need the developer tool.

Team

  • Organization account: open-wxa
  • Individual authors and contributors: not listed on the project page
  • Acknowledgements: the WeChat team (official base library and compiler); WEPT (chemzqm/wept), which first proved that running mini programs in a browser is feasible
  • Creation date: not stated

Project Stats

  • ⭐ GitHub Stars: 556
  • 🍴 Forks: 73
  • 📝 Commits: 184
  • 📦 Version: v0.8.0
  • 📄 License: MIT (note: the bundled base library wxa-baselib is official WeChat code, copyright Tencent, and is not covered by MIT)
  • 📚 Docs: open-wxa.github.io/wxa-runner
  • 🏷️ Topics: e2e-testing, mini-program, playwright, simulator, taro, uni-app, weapp

Main Features

Core Purpose

Run a mini program's build output directly in a browser, with rendering, components, setData, and lifecycles all executed by the official base library rather than reimplemented by a framework as in H5. The behavior is closer to a real device, yet it can be driven by browser tooling.

Use Cases

  1. Headless E2E testing

    • Test inside the browser with Playwright, with no need to remote-control the developer tool; runs headless in CI.
  2. Mini-program tests on Linux CI

    • Runs on GitHub Actions Linux machines without installing the developer tool.
  3. Local debugging

    • Use Chrome DevTools for breakpoints, network requests, and the DOM; hot reload on save keeps the page stack and global state.
  4. Getting past obstacles like login

    • In setup, seed a login state, mock native APIs, and intercept requests (such as wx.request) without touching business code.
  5. Testing uni-app / Taro projects

    • Supports the mp-weixin directory they compile to.

Quick Start

The launch command:

npx wxa-runner dev ./dist/dev/mp-weixin   # native mini program dir, or uni-app / Taro build output
# → http://localhost:3000

You can jump straight to a page with URL parameters, for example:

http://localhost:3000/?path=pages/detail/detail&id=1

The docs home page doesn't give complete install steps or code samples for setup and Playwright. See the docs site's sub-pages — "setup before launch", "testing in Playwright", "running in CI", and "commands and environment variables". I won't invent sample code for them here.

Core Features

  1. Device-faithful execution

    • Runs build output directly; rendering, components, setData, and lifecycles are executed by the official base library.
  2. Headless E2E

    • Built on Playwright; can assert on page data and outgoing requests.
  3. Chrome DevTools debugging

    • Breakpoints, network, and DOM are all visible, with hot reload on save.
  4. Pre-launch setup

    • Seed a login state, mock native APIs, intercept requests.
  5. Borrowing the developer tool's login state

    • On a machine with the developer tool installed, borrow its login state to get real codes and phone numbers and reach the service market and plugin pages.
  6. Mainstream cross-platform output

    • Native mini programs, uni-app, Taro.

Advantages

The docs site has a page on choosing between wxa-runner and miniprogram-automator, which the home page doesn't expand on. The table below is my own qualitative comparison based on its design, for reference only:

Dimensionwxa-runnerDeveloper-tool-based approaches (e.g. miniprogram-automator)H5 / framework reimplementation
RuntimeOrdinary browserNeeds the desktop developer toolBrowser
Linux CIRuns headless, no developer tool neededLimited by the tool being Windows / macOS onlyYes
Rendering fidelityExecuted by the official base libraryOfficialFramework reimplementation may differ
DriverPlaywrightRemote control of the developer toolVaries

Why it's worth watching

  • It fills a practical gap in mini-program engineering: a runtime that fits in CI and stays close to a real device.
  • The "reuse the official stack, add only the native layer" trade-off means it doesn't chase every base-library version with a rewrite.
  • It is candid about its boundaries; the limitation list is very specific.

Deep Dive

Core idea: implement only the "native layer"

A mini-program runtime splits roughly in two: the base library, compiler, and bridge protocol WeChat provides, and the native layer the WeChat client provides (message bridge, page stack, native APIs, and native UI such as the navigation bar).

wxa-runner's choice: reuse the former entirely from the official source, and implement only the latter.

Layered architecture

Application: cli | playwright fixture
Service:     core/server (dev server, proxy, watcher, hot-update routing)
Build:       project / compiler / extractor
Runtime:     core/runtime (host + injected frame scripts)
             └─ official base library (WAService.js / WAWebview.js)

What each package does

PackageResponsibility
extractorFinds the developer tool directory, parses .wxvpkg, reads app.asar, unpacks and caches the base library to ~/.wxa-runner/baselib
compilerWraps wcc (WXML / WXS) and wcsc (WXSS); prefers the developer tool's own, falls back to miniprogram-compiler
coreproject (parses the project, produces artifacts, __wxConfig), server (DevServer, watching, hot update, proxy, plugins), runtime (browser side)
cliThe wxa-runner command line
playwrightWraps the Playwright fixture, matchers, and Node-side mock replay

The repo also has examples (showcase, hello-world, custom-tabbar, subpackages, worker, etc.) and tests (unit, integration, e2e, playwright). The stack includes TypeScript, pnpm workspace, Express, ws, chokidar, esbuild, tsup, Vitest, and Playwright.

How a mini program runs inside the browser

Logic and render layers. Each is a same-origin iframe: the logic layer is hidden, and there's one render layer per page, with a URL matching the page path so relative resource paths resolve correctly.

frame.js, injected before the base library, does the following:

  • Presets global variables
  • Provides WeixinJSBridge (invoke / publish / subscribe / on)
  • Simulates device size and portrait orientation
  • Converts mouse events into touch events
  • Injects built-in component styles early

Iframes rather than Web Workers because the base library needs window / document, and a same-origin iframe can call the host directly, so synchronous APIs like getStorageSync can return synchronously.

The host only maintains the page stack and dispatches onAppRoute; page lifecycles (onLoad / onShow / onReady) are all driven by the base library. The page stack is capped at 10, routing operations are serialized, and tabBar page instances are cached.

Compiling WXML / WXSS.

  • wcc runs as a subprocess, because the native module wcc.node randomly crashes with std::bad_alloc when called repeatedly in one process.
  • All wxml and wxs compile at once into a single $gwx: the render layer uses the full build, the logic layer a trimmed one; omitting the trimmed build produces a node-creation mismatch error.
  • In wcsc output, comm is the shared runtime, and each page or component gets one setCssToHead; rpx conversion is done by the runtime wcsc generates.
  • Incremental builds: editing only JS doesn't recompile templates or styles, the three compilations run in parallel, and concurrent requests share a single build.
  • Each JS file in the logic layer becomes one define script with line numbers matching the source; ES modules go through esbuild to CommonJS with source maps, so breakpoints line up with your source.

Simulating wx APIs and native components.

  • Native APIs are managed by a registry covering storage, system, network, routes, ui, media, audio, map, picker, webView, fs, subpackages, misc, and more. Calls that can produce a result synchronously call back synchronously; unimplemented APIs return ok by default and warn once.
  • The render layer reports its platform as devtools, so most native components directly use the web implementations the base library prepares for the developer tool: canvas, video, map, camera, audio, and input work. The host pops up pickers, and web-view embeds via iframe.
  • Companion SDKs such as ads, cloud development, and payments have no loading mechanism; only stubs on the wx layer make the business logic take its failure branch.
  • Workers use the browser's Web Worker, with the host implementing both ends of WeixinWorker.

Hot update. Editing only wxss, or a page or component's own wxml / js, hot-updates; other changes refresh the whole page and restore the page stack. Page() pages swap new methods onto the live instance, while Component() pages and components are remounted by the base library.

Startup order, for testing

The host mounts window.__wxa_test_api__, with this startup order: test API → setup → __wxa_before_launch__ → launch the mini program. Setup runs before the logic layer is created and passes through only environment variables starting with WXA_. This is the underlying mechanism behind "seed a login state and mock native APIs without touching business code".

Relationship to the developer tool

wxa-runner reuses the developer tool's base library (.wxvpkg), compilers (wcc / wcsc), and administrative-division data, and discards the desktop shell.

Without the developer tool (Linux, CI):

  • The base library is the bundled @open-wxa/wxa-baselib (3.7.0)
  • The compiler is miniprogram-compiler, which supports x86-64 only, has an older core, and shows small template-runtime differences
  • The region picker is unavailable

For the same base-library version, the developer tool's copy is preferred. Paths can be overridden with WXA_DEVTOOLS_PATH, WXA_VENDOR_DIR, and WXA_WCC / WXA_WCSC.

On base-library versions: by default the latest compatible version up to 3.7.0 is used; from 3.7.1, business code runs in an isolated sub-context created by the native WeixinJSContext, so you must pass a version explicitly with --baselib or WXA_BASELIB; from 3.8, the framework splits into WAServiceMainContext.js / WASubContext.js.

Key design trade-offs

Trade-offReason
Not using the developer tool directlyWindows / macOS only; hard to put into CI or a headless browser, and Playwright can't drive it
iframes instead of Web WorkersThe base library needs window / document, and same-origin iframes also support synchronous APIs
Not rewriting the base libraryLarge effort and hard to keep consistent; the earlier self-simulated version supported very limited capabilities
Bundled base library in a separate repoCopyright is Tencent's and not under MIT; installed as an optional dependency, and failure doesn't affect the core
Compiler in a subprocessAvoids random crashes of the native module
Worker sync interfaces fail outrightThe developer tool blocks on SharedArrayBuffer, which needs COOP / COEP cross-origin isolation and would break cross-origin resource loading

Known limitations

The more specific this part is, the more honest the project's boundary is:

  • Base-library versions above 3.7.0 are unverified
  • Components not covered or with limited capability: live-player (can't play), live-pusher, voip-room, open-data (renders empty), ad (takes the failure branch), xr-frame
  • web-view: sites that set X-Frame-Options or CSP frame-ancestors can't be embedded; wx.miniProgram APIs inside the page are unavailable; binderror doesn't fire
  • map: needs network access; some methods (toScreenLocation, ground overlays, indoor maps, etc.) don't work
  • File system: saveFile with a filePath fails, saveFileSync is unimplemented; temp files live on the dev server and vanish on restart
  • The network proxy is for local development only; the base library's own requests return failure directly
  • Without the developer tool, the compiler supports x86-64 only and differs slightly from the real-device compiler

How to judge its value

  • It tests logic and interaction, not real-device pixels. Browser rendering and native components will always differ from a real device; it suits verifying business flows, page data, and requests, not visual regression.
  • Coverage depends on which APIs your mini program uses. Pages that lean heavily on live streaming, AR, ads, or cloud development need your own assessment.
  • Tracking versions is a long-term cost. Base-library versions above 3.7.0 are unverified, so the project must keep up as WeChat's base library evolves.
  • The project page gives no roadmap, configuration details, or formal comparison with similar projects; deeper use relies on the docs site and PROJECT_DESIGN.md.

Official

  • WEPT: first proved that running mini programs in a browser is feasible
  • PROJECT_DESIGN.md in the repo: details on architecture and design trade-offs
  • The docs page on choosing between wxa-runner and miniprogram-automator

Summary and Outlook

Key Takeaways

  1. A clear idea: reuse the official base library, compiler, and bridge protocol, and implement only the native layer — far steadier than rewriting the base library.
  2. Built for CI: driven by Playwright, runs headless on Linux without the developer tool.
  3. Close to a real device: rendering, setData, and lifecycles are executed by the official base library.
  4. Candid boundaries: a specific limitation list, and an explicit statement that it doesn't replace the developer tool.

Who It's For

  • Teams that want mini-program E2E tests in Linux CI
  • Developers building mini programs with uni-app or Taro who need automated regression
  • Readers interested in engineering problems like "how to reuse an official runtime inside a browser"

Suggested Path

Run npx wxa-runner dev on examples/hello-world first and get used to debugging in Chrome DevTools. Then read PROJECT_DESIGN.md to understand the division of labor among host, frame, and base library. Finally, following the docs site's setup and Playwright pages, write your project's first E2E case with a mocked login state and get it running headless in CI.


Explore PrimeSkills — a curated marketplace of AI agents and skills, each validated against real enterprise workflows. No hype, just what actually works.

Visit my personal site for more insights and interesting products.