Writing tests

Maestro flows

Maestro flows are YAML files with step-by-step instructions. SootSim supports all standard Maestro commands plus custom extensions.

Supported commands

commanddescription
tapOntap an element by text, id, or index
assertVisibleverify an element is visible
assertNotVisibleverify an element is not visible
inputTexttype text into the focused input
swipeswipe in a direction
scrollscroll in a direction
takeScreenshotcapture a screenshot
waitForwait for an element to appear
waitForAnimationToEndwait for animations to settle
backnavigate back
hideKeyboarddismiss the keyboard
runFlowrun a sub-flow file
repeatrepeat commands N times
scrollUntilVisiblescroll until an element appears
extendedWaitUntilwait with complex conditions

SootSim extensions

commanddescription
waitwait N milliseconds
dumpTreedump the node tree to console
assertTreeContainsassert a node exists in the tree

Templates

SootSim ships with flow templates you can copy and customize:

terminal

# copy a template
cp node_modules/sootsim/test/maestro-driver/templates/auth.yaml flows/auth.yaml

available templates: onboarding.yaml, auth.yaml, crud.yaml, navigation.yaml

Auto-generating flows

SootSim can crawl your app and generate smoke test flows:

terminal

sootsim test --flows --generate

This discovers screens, taps buttons, and generates YAML flows that verify the app doesn’t crash.

Detox-style API

The Detox-style API wraps Playwright with familiar Detox semantics:

import { test } from '@playwright/test'
import { sootsim } from 'sootsim-engine/test/kitchen-sink/sootsim-detox'
test('login flow', async ({ page }) => {
const { element, by, expect, waitFor } = await sootsim(page)
// find and interact
await element(by.id('email-input')).typeText('user@example.com')
await element(by.id('password-input')).typeText('password123')
await element(by.text('Sign In')).tap()
// wait for navigation
await waitFor(element(by.text('Dashboard')))
.toBeVisible()
.withTimeout(5000)
// assertions
await expect(element(by.id('welcome-text'))).toHaveText('Welcome')
await expect(element(by.id('logout-btn'))).toExist()
})

Available matchers

  • by.id(id) — match by testID
  • by.text(text) — match by text content
  • by.label(label) — match by accessibility label
  • by.role(role) — match by accessibility role
  • by.type(type) — match by component type

Available actions

  • tap() — tap the element
  • longPress() — long press the element
  • typeText(text) — type text
  • replaceText(text) — replace input text
  • clearText() — clear input text
  • scroll(pixels, direction) — scroll
  • swipe(direction) — swipe
  • takeScreenshot(name) — capture screenshot

Available assertions

  • toExist() / not.toExist()
  • toBeVisible() / not.toBeVisible()
  • toHaveText(text) / not.toHaveText(text)
  • toHaveId(id)
  • toHaveLabel(label)
  • toHaveRole(role)
  • toBeEnabled() / not.toBeEnabled()

Reporters

SootSim supports multiple output formats:

  • console — colored terminal output (default)
  • json — machine-readable JSON
  • junit — JUnit XML for CI systems
  • html — visual report with screenshots

terminal

sootsim test --flows --reporter junit
sootsim test --flows --reporter html