5 min read

Midscene.js: Write UI Tests in Plain English With AI Vision (GitHub, Scanned)

AI-driven UI automation and testing for web, mobile and desktop, written in natural language.

Midscene.js logo
✅
Scan: safe. Nothing malicious. Know that screenshots of what you test go to the model provider you configure, and that the desktop package ships three prebuilt macOS helper binaries. Scanned Oct 26, 2026; the full report is below.

Midscene.js lets you describe UI actions and checks in plain language and has a vision model carry them out. Instead of CSS selectors you write 'search for headphones and filter to under $100', then assert 'every result costs less than $100', and Midscene works from screenshots to click, type, scroll and verify. That means it can handle canvas elements, icon-only buttons, cross-origin iframes and native apps that selector-based tools struggle with.

The same API covers web (through Playwright, Puppeteer or your desktop Chrome), Android, iOS, HarmonyOS and desktop apps on macOS, Windows and Linux. Tests can be written as YAML cases with Midscene Test or added to existing Playwright code, and every run produces an HTML report with screenshots and the model's reasoning. It works with a range of vision models, including open ones you can host yourself.

It comes from the web-infra-dev organisation, the team behind Rspack and Rsbuild, and is MIT-licensed with about 15,400 stars and 90 contributors, at v1.17.1 when scanned. Each step is a model call, so tests are slower and cost more than classic selectors, and results depend on the model you choose.

Who it is for

QA engineers and developers whose selector-based tests keep breaking, teams testing mobile or desktop apps as well as the web, and anyone automating a UI they do not control.

Getting started

1. Create a Midscene Test project (needs Node.js 20.19+ and pnpm)

pnpm dlx @midscene/test create

2. Or add it to an existing Playwright project

npm install @midscene/web dotenv --save-dev

Configure a vision model first, with an API key or a self-hosted endpoint, as described in the model setup guide on midscenejs.com. To try it without code, install the Midscene Chrome extension from the quick start page.

Safety scan

We cloned web-infra-dev/midscene at commit 56bdbd0 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • No secrets. The 8 pattern hits are a regular expression that splits base64 image data URLs in tests, which the scanner reads as decode-then-run, and a code comment about PowerShell's -EncodedCommand in packages/computer/src/windows-dpi.ts.
  • 3 committed Mach-O binaries in packages/computer/bin/darwin (rdp-helper, 467 KB; display-info and phased-scroll, 50 KB each) are helpers for desktop control on macOS. Their source is in packages/computer/native, though the scan cannot prove the binaries were built from it.
  • Install hooks: the example projects run midscene-test nodes after install, and the Android packages download scrcpy-server and YADB from those projects' GitHub releases when Midscene publishes, pinned by version rather than by hash.
  • 518 known advisories (19 critical, 246 high) in the monorepo's single pnpm-lock.yaml of 2,448 packages, which covers the documentation site, examples, test apps and build tooling as well as the libraries. Installing @midscene/web resolves its own, smaller dependency tree.
  • Screenshots and page content go to the model provider you choose; its data privacy page says Midscene itself receives nothing. An optional Sign in with ChatGPT mode, OpenAI's sanctioned token-sharing flow, stores tokens in ~/.midscene/siwc.json and uses your ChatGPT plan's quota.
  • 15 workflows, none using pull_request_target, and all 13 third-party actions are pinned to commits. Licence and contributing guide present; no security policy, Dependabot or CodeQL.

What the scanner counted

CheckResult
SecretsNone found.
Suspicious code8 pattern hits found and read; every one is listed under the raw findings.
Install-time code8 npm lifecycle scripts. 2 installer scripts
Committed binaries3 executable or compiled objects committed; listed under the raw findings.
CI workflows15 workflows. None use pull_request_target. 0 of 13 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often github.com, midscenejs.com, gateway.example, todomvc.com. No URLs to bare IP addresses.
Known vulnerabilities518 advisories across 2,448 pinned packages: 19 critical, 246 high, 215 moderate, 38 low. pnpm-lock.yaml: 2,448 packages, 518 advisories.
Project hygieneHas licence file, contributing guide. Missing security policy, automated dependency updates, CodeQL.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Pattern hits (8)
WhereRuleMatch
packages/android/tests/ai/android-emulator-smoke.test.ts:289decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/android/tests/ai/todo.test.ts:55decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/computer/src/windows-dpi.ts:87powershell-encoded* Node, `powershell.exe -EncodedCommand` exited with status 0 but produced no
packages/computer/tests/ai/ai-auto-todo.test.ts:44decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/computer/tests/ai/macos-desktop-smoke.test.ts:355decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/ios/tests/ai/ios-app-switcher.test.ts:24decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/ios/tests/ai/ios-simulator-smoke.test.ts:87decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
packages/ios/tests/ai/todo.test.ts:81decode-then-eval (test/example)const match = /^data:image\/\w+;base64,(.+)$/s.exec(base64);
npm lifecycle scripts (8)
  • examples/catalog/test/android/package.json postinstall: midscene-test nodes
  • examples/catalog/test/computer/package.json postinstall: midscene-test nodes
  • examples/catalog/test/harmony/package.json postinstall: midscene-test nodes
  • examples/catalog/test/ios/package.json postinstall: midscene-test nodes
  • examples/catalog/test/web/package.json postinstall: midscene-test nodes
  • package.json prepare: husky && pnpm run build
  • packages/android-playground/package.json prepack: node ../android/scripts/download-scrcpy-server.mjs --target=bin/scrcpy-server
  • packages/android/package.json prepack: node scripts/download-scrcpy-server.mjs && node scripts/download-yadb.mjs
Installer scripts (2)
Committed binaries (3)
  • packages/computer/bin/darwin/rdp-helper: Mach-O, 467 KB
  • packages/computer/bin/darwin/display-info: Mach-O, 50 KB
  • packages/computer/bin/darwin/phased-scroll: Mach-O, 50 KB
Worst known vulnerabilities (24 of 518)
AdvisorySeverityPackageSummary
GHSA-5rq4-664w-9x2ccriticalbasic-ftp@5.0.5Basic FTP has Path Traversal Vulnerability in its downloadToDir() method
GHSA-cpq7-6gpm-g9rccriticalcipher-base@1.0.4cipher-base is missing type checks, leading to hash rewind and passing on crafted data
GHSA-vjh7-7g9h-fjfhcriticalelliptic@6.6.0Elliptic's private key extraction in ECDSA upon signing a malformed input (e.g. a string)
GHSA-fjxv-7rqg-78g4criticalform-data@4.0.1form-data uses unsafe random function in form-data for choosing boundary
GHSA-fjxv-7rqg-78g4criticalform-data@4.0.2form-data uses unsafe random function in form-data for choosing boundary
GHSA-xvch-5gv4-984hcriticalminimist@1.2.5Prototype Pollution in minimist
GHSA-h7cp-r72f-jxh6criticalpbkdf2@3.1.2pbkdf2 returns predictable uninitialized/zero-filled memory for non-normalized or unimplemented algos
GHSA-v62p-rq8g-8h59criticalpbkdf2@3.1.2pbkdf2 silently disregards Uint8Array input, returning static keys
GHSA-xq3m-2v4x-88ggcriticalprotobufjs@8.0.0Arbitrary code execution in protobufjs
GHSA-jqcg-44mw-7w3hcriticalproxy-addr@2.0.7proxy-addr vulnerable to IP spoofing via IPv4-mapped IPv6 trust subnet
GHSA-95m3-7q98-8xr5criticalsha.js@2.4.11sha.js is missing type checks leading to hash rewind and passing on crafted data
GHSA-w7jw-789q-3m8pcriticalshell-quote@1.7.3shell-quote quote() does not escape newlines in object .op values
GHSA-w7jw-789q-3m8pcriticalshell-quote@1.8.2shell-quote quote() does not escape newlines in object .op values
GHSA-w7jw-789q-3m8pcriticalshell-quote@1.8.3shell-quote quote() does not escape newlines in object .op values
GHSA-23hp-3jrh-7fpwcriticaltar@6.2.1node-tar: Decompression/parse DoS via unlimited input
GHSA-23hp-3jrh-7fpwcriticaltar@7.4.3node-tar: Decompression/parse DoS via unlimited input
GHSA-5gmw-xhrv-c9v3criticaltinypool@1.1.1Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution
GHSA-85c8-ppgw-ccprcriticaltinypool@1.1.1Tinypool: Prototype Pollution Gadget to RCE in run() options
GHSA-5xrq-8626-4rwpcriticalvitest@3.0.5When Vitest UI server is listening, arbitrary file can be read and executed
GHSA-jmg2-rcxh-w8q3high@rsdoctor/rspack-plugin@1.0.2@rsdoctor/rspack-plugin has Unauthenticated HTTP API that Exposes Project Source Code and Build Metadata
GHSA-27p8-2357-5qqvhigh@xmldom/xmldom@0.8.10xmldom: DocType `name` Injection Bypasses requireWellFormed
GHSA-2v35-w6hq-6mfwhigh@xmldom/xmldom@0.8.10xmldom: Uncontrolled recursion in XML serialization leads to DoS
GHSA-4w3w-2rp5-g8jmhigh@xmldom/xmldom@0.8.10xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
GHSA-8344-3jmq-59r6high@xmldom/xmldom@0.8.10xmldom: Quadratic-time attribute deduplication

By the numbers

Stars15.4K
Forks1,206
Contributors90
Commits2,411
Open issues44
Open pull requests72
Releases222
Latest releasev1.17.1
LicenceMIT
Main languageTypeScript
Project age2 years
Last pushOct 26, 2026
Tracked files2,931
Lines of code496.8K
Checkout size112 MB

Lines by language: TypeScript 385K, JSON 41K, Markdown 33.2K, JavaScript 13.1K, CSS 7,884, YAML 5,856.

Questions

Is Midscene.js free?

Yes. Midscene.js is free and open source under the MIT licence. You pay for the vision model it calls, either per token from an API provider, from a ChatGPT plan, or nothing beyond hardware if you host an open model yourself.

Which models work with Midscene?

It needs a vision model that can locate elements in screenshots. The project documents Qwen, Doubao Seed, GLM-4.6V, Gemini Flash, GPT models, DeepSeek and UI-TARS, including open models you can self-host; its model selection guide compares them.

Does Midscene replace Playwright?

No, it works alongside it. In a Playwright test you create a Midscene agent on the page and mix AI steps with normal Playwright code, using natural language where selectors are brittle and plain code where they are not.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.