name: revyl-cli-create description: Create robust Revyl E2E tests using CLI commands from app source analysis or exploratory sessions.
Revyl CLI Test Authoring Skill
Native Agent Behavior
- Ask at most 1-3 concise clarification questions only when the target app, platform, session, URL, or sensitive action cannot be inferred from the repo or Revyl CLI.
- Prefer safe defaults and keep moving when
revyl init --detect,revyl app list,revyl test list, screenshots, or reports can answer the question. - When Revyl prints an editor, report, viewer, or local app URL, open it in the native browser/tool surface when available: Codex Browser/in-app browser for local URLs, Revyl editor/report URLs, screenshots, and page checks; Claude Code
.claude/skillsslash-command discovery plus WebFetch/WebSearch or configured MCP/browser tools; Cursor.cursor/skillsplus.cursor/rules/revyl-skills.mdcand available MCP/browser tools. - If no browser tool is exposed, report the URL and verify through
revyl test report,revyl device screenshot, orrevyl device reportinstead of claiming browser access. - Confirm before entering sensitive data, submitting forms, uploading files, accepting browser permissions, changing sharing/access, or deleting data.
End-to-End Authoring Loop
# 1) Confirm auth and target app/build
revyl auth status
revyl app list --platform <ios|android>
# 2) Create from YAML (bootstraps .revyl/tests/ and config)
revyl test create <test-name> --from-file ./<test-name>.yaml
# 3) Iterate on .revyl/tests/<test-name>.yaml, then push and run
revyl test push <test-name> --force
revyl test run <test-name>
# 4) Inspect results and refine
revyl test status <test-name>
revyl test report <test-name> --json
YAML-first bootstrap works without an existing .revyl/config.yaml:
revyl test create <test-name> --from-file ./test.yaml
The CLI checks the YAML with backend validation, copies it into .revyl/tests/, pushes it, and writes .revyl/config.yaml after the remote test is created.
If you prefer to scaffold first:
revyl test create <test-name> --platform ios --no-open
# edit .revyl/tests/<test-name>.yaml
revyl test push <test-name> --force
If you have a completed exploratory device session:
revyl test create --from-session <session-id> <test-name> --app <app-id>
revyl test run <test-name>
The name can be omitted; the compiler will use its suggested session title.
For full examples and troubleshooting, see docs/tests/creating-tests.md.
If this test comes from a running revyl dev session:
revyl device screenshot --out /tmp/revyl-current.png
revyl device report --session-id <session-id> --json
Then author YAML explicitly and create it with revyl test create --from-file. Put repeated setup such as login, onboarding, or seed data in a reusable module/setup block so feature tests do not duplicate it.
Tool Map
- Tests:
revyl test create,push,run,report,status, andhistory. - Modules:
revyl module create/list/get/update/usage/insertfor reusable block groups. - Scripts:
revyl script create/list/get/update/usage/insertforcode_executionblocks. - Variables:
test.variables,revyl test var,revyl global var,extraction.variable_name, andcode_execution.variable_name. - Launch env:
revyl global launch-varplus repeated--launch-varonly when app startup needs environment config. - Grouping:
revyl workflowandrevyl tagafter individual tests are stable.
YAML Building Blocks
Start from the smallest complete test:
test:
metadata:
name: smoke-login-ios
platform: ios
tags:
- smoke
build:
name: ios-test
variables:
email: "{{global.login-email}}"
blocks:
- type: instructions
step_description: "Sign in with {{email}}."
- type: validation
step_description: "The home screen is visible."
Use these block types:
instructions: one meaningful user intent.validation: a durable assertion about user-visible state.manual: framework actions such aswait,go_home,navigate,set_location,kill_app, andopen_app.extraction: read screen data intovariable_name.code_execution: run a saved script or lightweight inline code.module_import: import a reusable module by name or ID.if/while: conditional branches and loops with nested blocks.
Variables and Secrets
- Local YAML variables go under
test.variablesand are referenced as{{variable-name}}or{{variable_name}}. - Extracted values and code execution output become variables when the block has
variable_name. - Test-scoped variables can be managed after creation with
revyl test var set/list/get/delete. - Org-level secrets use
revyl global var set name=value --secretand are referenced as{{global.name}}. - Define or extract variables before use. Never hardcode secrets in reusable YAML or modules.
revyl test var set <test-name> email=test@example.com
revyl global var set login-password='secret' --secret
revyl global launch-var create API_URL=https://staging.example.com
Code Execution
Prefer saved scripts for setup, API seeding, backend assertions, or reusable logic:
revyl script create seed-user --file scripts/seed_user.py --runtime python
revyl script insert seed-user
revyl script usage seed-user
Use the snippet from revyl script insert, or write the block directly:
- type: code_execution
script: "seed-user"
variable_name: seeded_user_id
Legacy YAML may use step_description to hold an internal script UUID, but new authored YAML should use script.
Use inline code only for small one-offs when a saved script would be unnecessary:
- type: code_execution
step_description: |
print("ready")
code_execution_runtime: python
Reusable Modules
Use modules for stable shared setup like login, onboarding, account creation, or checkout prep.
# modules/login.yaml
blocks:
- type: instructions
step_description: "Sign in with {{email}} and {{global.login-password}}."
- type: validation
step_description: "The home screen is visible."
revyl module create login-flow --from-file modules/login.yaml --description "Standard login"
revyl module insert login-flow
revyl module usage login-flow
Import with the snippet from revyl module insert:
- type: module_import
module: "login-flow"
Legacy YAML may use module_id to hold an internal module UUID, but new authored YAML should use module.
Full Flow Example
test:
metadata:
name: checkout-e2e-ios
platform: ios
tags:
- checkout
- e2e
build:
name: ios-test
variables:
email: checkout-user@example.com
product-name: Orchid Mantis
blocks:
- type: code_execution
script: "seed-checkout-user"
variable_name: seeded_user_id
- type: module_import
module: "login-flow"
- type: instructions
step_description: "Complete checkout for {{product-name}} using the saved shipping address."
- type: extraction
step_description: "Extract the order confirmation number."
variable_name: order_number
- type: validation
step_description: "The confirmation page shows order {{order_number}}."
Conversion Rules
- One action per instruction step.
- Keep validation in separate validation steps.
- Validate user-facing outcomes, not transient loading text.
- Replace secrets with variables or global variables.
- Use
module_importblocks for reusable setup like login or onboarding. - Preserve the exact target language that worked in the device report when it is more specific than a generic tap.
- Use
code_executionfor API setup, data seeding, backend checks, and deterministic helper logic.
Good:
- type: instructions
step_description: "Complete checkout for Orchid Mantis using the saved shipping address."
- type: validation
step_description: "The confirmation screen shows an order number."
Bad:
- type: instructions
step_description: "Tap Cart, verify the total, tap Checkout, verify the shipping form, enter the address, tap Continue, verify payment, then place the order."
- type: validation
step_description: "The Cart tab is visible."
- type: validation
step_description: "The Checkout button is visible."
- type: validation
step_description: "The payment form is visible."
Definition of Done
- Test name communicates intent.
- Test passes on correct behavior.
- Test fails on intended regression.
- Validations are stable across expected data variation.
- Variables, scripts, modules, launch vars, tags, and workflows are created only when the test actually needs them.