looktap v0.7.6

Automation

Tests & automation

What worked with your agent runs on its own afterwards. Every command is a plain shell call with an exit code, so a shell script or a CI job can run the same steps: as flow files that looktap test checks, or as plain scripts.

Test flows in CI

A flow file is a run script: one looktap command per line, # for comments. Use wait where the app takes time and expect where the flow should check something:

# flows/sign-in.txt
launch com.example.myapp
wait "Sign in"
fill Email [email protected]
tap "Continue"
wait Welcome
expect "Sign in" --gone
$ looktap test flows/ -o results --junit results/junit.xml
✓ flows/browse.txt  9 steps · 6.1 s
✗ flows/checkout.txt  3.4 s
    step 5 (expect Total = €4.20): expect failed: "Total" is "€0.00", expected "€4.20" (waited 3 s)
    │ $ tap "Add to bag"
    │ tapped "Add to bag" at 195,612
    │ …
    evidence: results/failures/flows-checkout
✓ flows/sign-in.txt  6 steps · 4.8 s
looktap: 3 flows: 2 passed, 1 failed · 14.3 s · results/junit.xml

Flows run in order on one device, each from wherever the last one left the app, so start each with launch (or reset <app> and launch for a fresh install). Steps wait for their targets, but a flow that depends on a backend can still be slow: raise --wait rather than adding sleeps.

A web page with no accessibility labels (many sign-in pages) gives wait nothing to match: wait --settle waits until the screen, its picture included, holds still, and a blank page that's still loading doesn't count. Where a person has to act (a consent screen, a second factor), handoff makes that a step: it tells them, then carries on once the next screen shows. pause is the fixed wait for what's left. In a React Native dev build, "dismissLogBox": true in looktap.config.json closes the LogBox toast before every tap:

# flows/google-sign-in.txt
tap "Continue with Google"
wait --settle
handoff "Sign in with Google and approve access" --until Home
expect Home

Flow files live in your repo, so never put real credentials in one. Sign in with a test-only account, and keep its password out of the file: test replaces ${NAME} with that environment variable (a CI secret), e.g. fill Password "${PASSWORD}".

In GitHub Actions, iOS runs on a macOS runner and Android on a Linux runner with KVM:

jobs:
  ios:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install -g @looktap/cli
      - run: xcodebuild -scheme MyApp -sdk iphonesimulator -derivedDataPath build build
      - run: looktap boot "iPhone 16" --headless
      - run: looktap install build/Build/Products/Debug-iphonesimulator/MyApp.app
      - run: looktap test flows/ -o results --junit results/junit.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: ios-results, path: results }

  android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm install -g @looktap/cli
      - run: ./gradlew assembleDebug
      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm
      - uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 34
          arch: x86_64
          script: |
            looktap -p android install app/build/outputs/apk/debug/app-debug.apk --grant
            looktap -p android test flows/ -o results --junit results/junit.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: android-results, path: results }

Upload results/ even when the job fails: that's where the failed flows' screenshots and logs are.

Record a flow (codegen)

On Android you can write a flow by using the app. codegen restarts the app, then turns each touch into the step that replays it:

$ looktap codegen flows/about.txt com.android.settings
+ stop com.android.settings
+ launch com.android.settings
+ wait Settings
recording com.android.settings to flows/about.txt — use the phone. …
+ scrollto "About phone"
+ tap "About phone"
+ back
+ tap "Search settings"
+ fill Search wifi
expect "Wi-Fi"
ok: "Wi-Fi" at 107,148
+ expect Wi-Fi
saved 9 steps to flows/about.txt — run it: looktap test flows/about.txt

It reads the touchscreen through adb shell getevent, so it records real fingers (not taps sent by looktap itself). It isn't available on iOS yet: write the flow by hand there, or record it on Android if the app is the same.

A repeatable screenshot sweep

#!/bin/sh
set -e
looktap launch
for route in home search messages profile settings; do
  looktap open "myapp://$route" --ls > /dev/null
  looktap shot "$route"
  looktap swipe 200 700 200 250
  looktap shot "${route}__scrolled"
done

Same screen on several devices

looktap boot "iPhone 14" --headless && looktap boot "iPhone 16 Pro" --headless
looktap devices
# ios      42E41FA7-…  iPhone 14 · simulator · iOS 26.0 · headless
# ios      F9213EDC-…  iPhone 16 Pro · simulator · iOS 26.0 · headless
# android  emulator-5554  Pixel_9a · emulator · Android 16 · window

for d in 42E41FA7-EA40-45C1-8C6E-13A70BAF1FD6 F9213EDC-180D-412B-B24C-2FD69A37D3AC; do
  looktap -d $d open myapp://home
  looktap -d $d wait "Home"
  looktap -d $d shot -o shots/$d home
done

A command that names no device uses the one the last command used, for 10 minutes and while it's still up, without listing devices again (0.3–0.5 s saved on each command). boot and every command that names a device (-d, LOOKTAP_DEVICE) set it. When two people or agents drive two devices at the same time, each should name its device on every command.

Generated from README.md. Edit it there.