Web Application Testing
anthropics/skills
Tests local web applications with Python Playwright scripts, checking frontend behavior, capturing screenshots and reading browser console logs.
Verify a running Mendix app in a browser: mxcli playwright check for a one-call text verdict per page (renders?
$ npx skills add mendixlabs/mxcli --skill test-app -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install mendixlabs/mxcli test-app --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/mendix/test-app .claude/skills/test-app && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .claude/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-appType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add mendixlabs/mxcli --skill test-app -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install mendixlabs/mxcli test-app --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/mendix/test-app .agents/skills/test-app && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .agents/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill test-app -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install mendixlabs/mxcli test-app --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/mendix/test-app .cursor/skills/test-app && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .cursor/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/mendixlabs/mxcli.git --path .claude/skills/mendix/test-app--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add mendixlabs/mxcli --skill test-app -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install mendixlabs/mxcli test-app --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/mendix/test-app .gemini/skills/test-app && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .gemini/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install mendixlabs/mxcli test-appInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add mendixlabs/mxcli --skill test-app -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/mendix/test-app .github/skills/test-app && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .github/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add mendixlabs/mxcli --skill test-app -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install mendixlabs/mxcli test-app --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/mendixlabs/mxcli.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/mendix/test-app .opencode/skills/test-app && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "test-app" agent skill from https://github.com/mendixlabs/mxcli/tree/main/.claude/skills/mendix/test-app into .opencode/skills/test-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "test-app", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
test-appVerify a running Mendix app in a browser: mxcli playwright check for a one-call text verdict per page (renders?
Test App is an agent skill from mendixlabs/mxcli. Verify a running Mendix app in a browser: mxcli playwright check for a one-call text verdict per page (renders? error banner? console errors? rows?), playwright-cli scripts for interaction, OQL for data. Use when asked to check a page, see whether it renders, verify in the browser, test the app end to end, or confirm generated pages work — before reaching for a screenshot.
Its SKILL.md is about 5.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA, covering Browser testing. It works with Playwright. The repository describes itself as: Mendix cli tool, a headless way to work with Mendix projects. Enables Mendix projects for use with 3rd party agentic coding tools like Claude Code and Copilot. Includes a… The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 20a6c89. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npmnodebashplaywrightnpxFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use npm and npx, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Test App loads about 5.4k tokens when it runs. Until then it costs about 97 tokens; SKILL.md has 1,525 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
on, else `APP_PORT` from `.docker/.env`, else `:8080`.(URL: arg, else --base-url, else .docker/.env, else :8080)Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from mendixlabs/mxcli at commit 20a6c89, republished under its Apache-2.0 licence (© mendixlabs). 1,525 words, ~5,440 tokens.
.claude/skills/test-app/SKILL.md (or your agent's skills folder).This skill guides you through verifying a running Mendix application using playwright-cli for browser automation and mxcli oql for data assertions.
Use this when:
For microflow logic testing (business rules, calculations, entity operations — no browser needed), use the test-microflows skill and mxcli test instead.
mxcli playwright check (one call, text, no screenshot)"Does the page render?", "is there an error?", "did the list get rows?" are answered by one command against the running app — not by a playwright-cli session:
mxcli playwright check /p/customers /p/orders -p app.mprpage /p/customers title="Customers" h="Customer overview" rows=12 text=812 console-errors=0
page /p/orders title="Orders" text=254 console-errors=1
ALERT An error occurred, please contact your system administrator.
ERR [Client] An error occurred while executing microflow data source for widget …
HTTP 560 POST /xas/
FAIL 1 error banner(s)
FAIL 1 console error(s)
FAIL 1 failed request(s)
FAIL 1 of 2 page(s)--user U --password P, or --role R (the project's demo user
holding user role R), or nothing but -p (a demo user, an administrator first).
The session is saved under .mxcli/playwright-check/ and reused by the next check;
after a runtime restart it is renewed automatically. --fresh-login forces it.--assert-text 'Customer overview' (body text contains),
--assert-count '.mx-name-dgCustomers [role=row]>=2' (ops >= <= == != > <; a
bare selector means at least one). Both repeatable, applied to every page.--screenshot out.png writes it and prints the path. Read it once.--base-url, else (with -p) the port a live mxcli run --local serves
on, else APP_PORT from .docker/.env, else :8080.Never hand-roll playwright-cli open …/login.html; fill …; click …; goto …; sleep 6; eval …; screenshot … followed by reading the PNG to see whether a page
works. That sequence is a dozen tool calls and an image every later model call pays
for; playwright check is one call and about a hundred tokens. Use playwright-cli
(below) when the test needs an interaction — clicking, filling, submitting —
and capture that in a tests/verify-*.test.sh script for mxcli playwright verify.
The devcontainer created by mxcli init installs:
npm install -g @playwright/cli@0.1.15; the package's CLI surface shifts between releases, so it is deliberately not @latest)@playwright/cli's bundled playwright-core, into a shared PLAYWRIGHT_BROWSERS_PATH, and exposed at the stable path /usr/local/bin/mx-headless-shell. The generated .playwright/cli.config.json pins executablePath to that symlink.mxcli docker runIf the app calls an external REST API, that endpoint is a prerequisite too — a verification run that depends on a live third party is not repeatable. See mock-rest-apis.
The app must be running before verification:
mxcli docker run -p app.mpr --waitrun-code vs eval — read this first@playwright/cli has two evaluation commands with different contexts:
| Command | Runs in | Use for |
|---|---|---|
playwright-cli eval "() => ..." | browser page (document, window exist) | DOM assertions, clicks, filling fields, reading .mx-name-* |
playwright-cli run-code "..." | Node (Playwright API; document is undefined) | Playwright-level scripting, not page DOM |
eval takes a function ("() => ...") and prints its return value under ### Result. If it returns a Promise, the CLI awaits it. Do not use run-code "document.querySelector(...)" — it throws ReferenceError: document is not defined. Every page assertion below uses eval.
If you are provisioning manually (outside mxcli init) or debugging a browser-launch failure, know these:
playwright-cli install initializes the workspace — it does not install a browser. The browser command is playwright-cli install-browser.open --browser only accepts chrome | firefox | webkit | msedge (no chromium), and the default is the chrome channel — which has no distribution on Linux arm64, and neither does msedge. npx playwright install chrome fails with ERROR: not supported on Linux Arm64.@playwright/cli's own playwright-core:node "$(npm root -g)/@playwright/cli/node_modules/playwright-core/cli.js" install chromium chromium-headless-shell.playwright/cli.config.json at the headless-shell binary (headless mode needs the chromium_headless_shell-* build, not the full chromium-* one):"browser": {
"browserName": "chromium",
"launchOptions": {
"headless": true,
"executablePath": "/usr/local/bin/mx-headless-shell"
}
}mxcli init does all of this for you (the Dockerfile installs the headless shell and creates the /usr/local/bin/mx-headless-shell symlink). This path is the devcontainer symlink; if you run playwright-cli natively outside the container, point executablePath at your own install (or drop it and let a working default resolve).# open browser session (headless by default)
playwright-cli open http://localhost:8080
# Take a snapshot to see the page structure and element refs
playwright-cli snapshot
# Interact with elements using refs from snapshot
playwright-cli click e12
playwright-cli fill e15 "some text"
# Verify widget presence (page context -> use eval with a function)
playwright-cli eval "() => document.querySelector('.mx-name-dgCustomers') !== null"
# Take a screenshot for visual inspection
playwright-cli screenshot
# close browser when done
playwright-cli closeMendix renders each widget's name property as a CSS class on the DOM element:
<div class="mx-name-submitButton form-group">This maps directly to MDL widget names. When you generate a widget in MDL:
actionbutton submitButton (caption: 'Submit', action: save changes)The stable CSS selector is .mx-name-submitButton. Use this with eval for reliable assertions:
playwright-cli eval "() => document.querySelector('.mx-name-submitButton') !== null"For checking pages, mxcli playwright check signs in for you (see Start Here) —
including the input-event quirk below, since it types into the fields rather than
setting .value. The manual sequence is for playwright-cli sessions that go on to
interact with the app.
The Mendix login page uses standard HTML IDs:
Always dispatch an
inputevent after setting.value. Mendix (and React) inputs track their state from theinputevent, not the raw.valueproperty. Setting.valuealone can leave the field "empty" as far as the app is concerned, so the login — or any form fill — silently fails. Every field-settingevalbelow follows theset value → dispatchEvent('input')pattern.
playwright-cli open http://localhost:8080
playwright-cli snapshot
playwright-cli eval "() => { const el = document.querySelector('#usernameInput'); el.value = 'MxAdmin'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
playwright-cli eval "() => { const el = document.querySelector('#passwordInput'); el.value = 'AdminPassword1!'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
playwright-cli eval "() => document.querySelector('#loginButton').click()"
# wait for home page to load
playwright-cli eval "() => new Promise(r => setTimeout(r, 3000))"
playwright-cli snapshot
# Save auth state for reuse
playwright-cli state-save mendix-authTo reuse saved auth in a later session:
playwright-cli open http://localhost:8080
playwright-cli state-load mendix-auth
playwright-cli goto http://localhost:8080/p/Customer_OverviewWhen security is OFF: Skip login entirely. Navigate directly to /.
After navigating to a page, verify that all expected widgets are present:
playwright-cli goto http://localhost:8080/p/Customer_Overview
# check multiple widgets
playwright-cli eval "() => document.querySelector('.mx-name-dgCustomers') !== null"
playwright-cli eval "() => document.querySelector('.mx-name-btnNew') !== null"
playwright-cli eval "() => document.querySelector('.mx-name-btnEdit') !== null"
playwright-cli eval "() => document.querySelector('.mx-name-btnDelete') !== null"playwright-cli goto http://localhost:8080/p/Customer_Edit
# Take snapshot to discover element refs
playwright-cli snapshot
# Fill form fields using .mx-name-* selectors (page context -> eval)
playwright-cli eval "() => { const el = document.querySelector('.mx-name-txtName input'); el.value = 'Test Customer'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
playwright-cli eval "() => { const el = document.querySelector('.mx-name-txtEmail input'); el.value = 'test@example.com'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
# or use fill with snapshot refs (simpler when refs are known)
playwright-cli fill e42 "Test Customer"
playwright-cli fill e45 "test@example.com"
# Click save
playwright-cli eval "() => document.querySelector('.mx-name-btnSave').click()"When security is OFF, direct /p/PageName URLs do not work — Mendix redirects to the home page. Navigate through your own named widgets instead:
playwright-cli open http://localhost:8080
# wait for Mendix to load
playwright-cli eval "() => new Promise(r => { const check = () => document.querySelector('.mx-page') ? r() : setTimeout(check, 500); check(); })"
# Click navigation button (from your MDL-defined NavigationMenu snippet)
playwright-cli eval "() => document.querySelector('.mx-name-btnCustomers').click()"
# wait and verify target page
playwright-cli eval "() => new Promise(r => setTimeout(r, 2000))"
playwright-cli eval "() => document.querySelector('.mx-name-dgCustomers') !== null"Direct URLs work after login:
playwright-cli state-load mendix-auth
playwright-cli goto http://localhost:8080/p/Customer_Overview
playwright-cli eval "() => document.querySelector('.mx-name-dgCustomers') !== null"After a UI interaction, verify data persistence using mxcli oql (no pg package needed):
# after creating a customer through the UI...
mxcli oql -p app.mpr --json "SELECT Name, Email FROM MyModule.Customer WHERE Name = 'Test Customer'"This returns JSON that you can inspect directly. No npm dependencies required.
For regression testing in CI/CD, capture playwright-cli commands as shell scripts. These are the same commands used interactively — readable without TypeScript knowledge.
#!/usr/bin/env bash
# tests/verify-customers.sh
set -euo pipefail
# Setup
playwright-cli open http://localhost:8080
playwright-cli eval "() => { const el = document.querySelector('#usernameInput'); el.value = 'MxAdmin'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
playwright-cli eval "() => { const el = document.querySelector('#passwordInput'); el.value = 'AdminPassword1!'; el.dispatchEvent(new Event('input', {bubbles: true})) }"
playwright-cli eval "() => document.querySelector('#loginButton').click()"
playwright-cli eval "() => new Promise(r => setTimeout(r, 3000))"
# Verify Customer overview
playwright-cli goto http://localhost:8080/p/Customer_Overview
playwright-cli eval "() => { if (!document.querySelector('.mx-name-dgCustomers')) throw new Error('dgCustomers not found') }"
playwright-cli eval "() => { if (!document.querySelector('.mx-name-btnNew')) throw new Error('btnNew not found') }"
# create a customer
playwright-cli eval "() => document.querySelector('.mx-name-btnNew').click()"
playwright-cli eval "() => new Promise(r => setTimeout(r, 2000))"
playwright-cli fill txtName "CI Test Customer"
playwright-cli fill txtEmail "ci@test.com"
playwright-cli eval "() => document.querySelector('.mx-name-btnSave').click()"
playwright-cli eval "() => new Promise(r => setTimeout(r, 2000))"
# Verify data persistence
mxcli oql -p app.mpr --json "SELECT Name FROM MyModule.Customer WHERE Name = 'CI Test Customer'" \
| grep -q "CI Test Customer"
# Cleanup
playwright-cli close
echo "PASS: verify-customers"# run directly
bash tests/verify-customers.sh
# run all test scripts
for f in tests/verify-*.sh; do bash "$f" || exit 1; done
# via mxcli (auto-detects app port, captures a screenshot on failure)
mxcli playwright verify tests/ -p app.mpr
# in the edit -> rebuild -> re-verify loop, keep the browser warm so the next
# run reuses the live, still-logged-in session instead of cold-launching
# Chromium (reuse re-navigates, so a rebuilt app is loaded fresh)
mxcli playwright verify tests/ -p app.mpr --keep-openWhen reusing across runs (
--keep-open), drop any trailingplaywright-cli closefrom the scripts — a script that closes the session tears it down for the next run regardless of--keep-open. Omit--keep-openfor CI so the browser is torn down at the end.
For set -e scripts, eval a function that throws to trigger a non-zero exit. The throw must use JavaScript's Error constructor (capital E):
# This exits non-zero if widget is missing
playwright-cli eval "() => { if (!document.querySelector('.mx-name-widgetName')) throw new Error('missing widgetName') }"playwright-cli maintains browser sessions across commands. The devcontainer sets PLAYWRIGHT_CLI_SESSION=mendix-app by default, so every command shares one browser — state, cookies, and login persist between invocations.
mxcli playwright wraps the session so you manage it explicitly across turns, with the project's port/browser resolution built in:
# open or attach to the session (URL: arg, else --base-url, else .docker/.env, else :8080)
mxcli playwright open -p app.mpr
# is a session live, and what page is it on?
mxcli playwright status
# tear down
mxcli playwright close # current session
mxcli playwright close --all # every sessionAgentic loop pattern — open once, log in once, then iterate cheaply:
mxcli playwright open -p app.mpr # 1. warm the browser
# ... log in (script or the login snippet above), state-save mendix-auth ...
mxcli playwright verify tests/ -p app.mpr --keep-open # 2. verify, keep it warm
# ... edit MDL, mxcli exec, mxcli docker run --fresh --wait ...
mxcli playwright verify tests/ -p app.mpr --keep-open # 3. reuses the warm, logged-in session
mxcli playwright status # check it's still up before deciding to reopenopen and verify share the same open-or-reuse behavior: attach to a live same-origin session (re-navigating so a rebuilt app loads fresh), or open a new one.
playwright-cli list # list active sessions
playwright-cli close # close current session
playwright-cli close-all # close all sessions
playwright-cli -s=test2 open http://localhost:8080 # named session (parallel testing)# Take screenshot
playwright-cli screenshot
# Take screenshot of specific element
playwright-cli screenshot e42
# show console messages
playwright-cli console
# show network requests
playwright-cli network
# Start/stop tracing
playwright-cli tracing-start
# ... do interactions ...
playwright-cli tracing-stop
# Visual monitoring dashboard
playwright-cli showHeaded mode (
open --headed) needs the full Chromium build and a display; the devcontainer ships only the headless shell and has no display, so use screenshots/tracing for visual debugging instead.
Use .mx-name-* selectors from your own MDL widgets. These are reliable and predictable because you control the widget names:
-- MDL: names you define become test hooks
actionbutton btnDrivers (caption: 'Drivers', action: show page Module.Drivers_Overview)
datagrid dgOrders (datasource: database Module.Order) { ... }# Tests: use .mx-name-* selectors for those names
playwright-cli eval "() => document.querySelector('.mx-name-btnDrivers').click()"
playwright-cli eval "() => document.querySelector('.mx-name-dgOrders') !== null"Do NOT guess CSS selectors for Mendix built-in layout widgets. The top navigation bar, sidebar, header, and other platform UI elements have unpredictable class names.
NavigationList items need text_ prefix. The <li> container does NOT get an mx-name-* class. The inner <span> gets mx-name-text_<itemName>:
# use text_ prefix for navigationlist items
playwright-cli eval "() => document.querySelector('.mx-name-text_itemDrivers').click()"DataGrid2 rows: Both header and data rows share role="row". Filter with :has([role="gridcell"]):
playwright-cli eval "() => document.querySelector('.mx-name-dgCustomers [role=\"row\"]:has([role=\"gridcell\"])').textContent"waitForLoadState('networkidle')Mendix maintains a permanent long-polling XHR connection. networkidle never fires. Use element-based waits via eval instead.
Clicking top nav items may fail due to div.mx-placeholder overlay. Dispatch the event directly (note the capital Event):
playwright-cli eval "() => document.querySelector('.mx-name-navigationTree1-1').dispatchEvent(new Event('click', {bubbles: true}))"The Mendix login page (/login.html) uses fixed IDs: #usernameInput, #passwordInput, #loginButton. These are stable across Mendix versions.
The key workflow: generate MDL → build → verify → fix → repeat.
# 1. generate and apply MDL
mxcli exec changes.mdl -p app.mpr
# 2. build and start
mxcli docker run -p app.mpr --fresh --wait
# 3. check the pages render (text verdict, exit 1 on failure)
mxcli playwright check /p/Customer_Overview /p/Order_Overview -p app.mpr
# 3b. only for interactions: drive playwright-cli / a verify script
mxcli playwright verify tests/ -p app.mpr --keep-open
# 4. Fix any issues in MDL, rebuild, re-verify| Failure Type | What It Means | MDL Fix |
|---|---|---|
.mx-name-X not found | Widget X missing from DOM | Check widget nesting, container visibility, BSON structure |
ReferenceError: document is not defined | Used run-code for a page assertion | Use eval "() => ..." (page context), not run-code (Node) |
Chromium distribution 'chrome' is not found | Browser not provisioned / chrome channel on arm64 | See "Browser setup gotchas" — install bundled Chromium + pin executablePath |
| Page returns 500 | Runtime error on page load | Check page layout, datasource, parameter bindings |
| Page returns 404 | Page doesn't exist or wrong URL | Verify page qualified name and navigation |
| OQL returns empty | Microflow didn't commit | Check COMMIT statement, error handling in microflow |
| Console error | JavaScript error in widget | Check widget template, pluggable widget config |
© mendixlabs, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/mendix/test-app of mendixlabs/mxcli.
Open the folder on GitHubat commit 20a6c89
Test App next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Test App this skillmendixlabs/mxcli | 128 | — | ~5.4k | Automated safety check: Notes | Apache-2.0 | |
| Web Application Testinganthropics/skills | 180k | 51 repos | ~966 | Automated safety check: Pass | Apache-2.0 | |
| Playwright CLIsanity-io/sanity | 6.4k | 18 repos | ~1.9k | Automated safety check: Pass | MIT | |
| playwright-cli Browser Automationgithub/gh-aw | 5.4k | 24 repos | ~2.8k | Automated safety check: Pass | MIT | |
| Write and Verify Playwright Testsappsmithorg/appsmith | 41k | — | ~2.9k | Automated safety check: Notes | Apache-2.0 | |
| Cucumber and Playwright E2E Testslanggenius/dify | 158k | — | ~682 | Automated safety check: Pass | Custom licence |
anthropics/skills
Tests local web applications with Python Playwright scripts, checking frontend behavior, capturing screenshots and reading browser console logs.
sanity-io/sanity
Automates browser interactions for web testing, form filling, screenshots, and data extraction.
github/gh-aw
Drives a real browser from the command line with playwright-cli to open pages, interact, mock requests, save state and work with Playwright tests.
appsmithorg/appsmith
Writes a Playwright end-to-end test from a prompt, runs it against a live Appsmith deployment and retries with fixes up to three times until it passes.
langgenius/dify
Guides changes and reviews of the Cucumber and Playwright end-to-end suite under `e2e/`: feature files, step definitions, support code, tags, locators and assertions.
langflow-ai/langflow
Write and review Playwright E2E tests for Langflow. An agent skill from langflow-ai/langflow.
mendixlabs/mxcli
Push OData query options into the SQL of a Mendix resource served by a read microflow, so $filter, $orderby, $top, $skip, $count and the key lookup reach the database instead of being silently…
mendixlabs/mxcli
Chart a Mendix app with Vega-Lite through a pluggable widget that takes the specification and the data as separate properties, so the model emits rows and never assembles a chart payload.
mendixlabs/mxcli
Author Mendix AI agent documents in MDL — Model, Knowledge Base, Consumed MCP Service and Agent, with variables, tools and multi-line prompts.
mendixlabs/mxcli
Run set-based INSERT, UPDATE and DELETE against Mendix entities through OQL statements, which the runtime supports and Studio Pro cannot author.
mendixlabs/mxcli
Define event-driven APIs over Kafka with Mendix business event services — publish and subscribe contracts, CREATE/DROP/DESCRIBE.
mendixlabs/mxcli
Search the Mendix Catalog platform service registry (catalog.mendix.com) from the CLI to find services published across an organisation.
Works with
Categories
Verify a running Mendix app in a browser: mxcli playwright check for a one-call text verdict per page (renders? Test App is an agent skill from mendixlabs/mxcli. Verify a running Mendix app in a browser: mxcli playwright check for a one-call text verdict per page (renders?
Test App fits situations like: asked to check a page; see whether it renders; verify in the browser; test the app end to end.
Run `npx skills add mendixlabs/mxcli --skill test-app -a claude-code`. Or copy the skill folder (.claude/skills/mendix/test-app in mendixlabs/mxcli) into .claude/skills/test-app in your project. Claude Code loads it when a task matches its description.
Run `npx skills add mendixlabs/mxcli --skill test-app -a codex`. Or copy the skill folder (.claude/skills/mendix/test-app in mendixlabs/mxcli) into .agents/skills/test-app in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add mendixlabs/mxcli --skill test-app -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/test-app, .gemini/skills/test-app, .github/skills/test-app and .opencode/skills/test-app in your project.
Going by SKILL.md and its folder, Test App needs the command-line tools its instructions call (npm, node, bash, playwright and npx). Our summary lists: Node.js; Docker.
SKILL.md contains no URLs. Its commands use npm and npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Test App is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.4k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Test App: Web Application Testing (anthropics/skills, 180k stars), Playwright CLI (sanity-io/sanity, 6.4k stars), playwright-cli Browser Automation (github/gh-aw, 5.4k stars) and Write and Verify Playwright Tests (appsmithorg/appsmith, 41k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
mendixlabs (a GitHub organization) maintains it in mendixlabs/mxcli, which has 128 GitHub stars. The repository holds 72 skills in this directory. The repository was last updated on October 7, 2026.
Source: mendixlabs/mxcli on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.