Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 242 additions & 0 deletions packages/app/maestro/harness/native-terminal-maestro-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
#!/usr/bin/env bash
# Common helpers for native-terminal Maestro flows. Each per-flow harness
# sources this file and calls run_flow_with_setup.
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
MAESTRO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
APP_ID="${PASEO_MAESTRO_APP_ID:-sh.paseo.debug}"
SIMULATOR_UDID="${PASEO_MAESTRO_UDID:-47FB40E1-3304-4516-B8BC-D75853EF1B47}"

log() {
echo "[native-terminal-harness] $*" >&2
}

terminal_ids() {
cd "$REPO_ROOT"
npm run --silent cli -- terminal ls --all --json 2>/dev/null \
| node -e 'const data=[]; process.stdin.on("data", c=>data.push(c)); process.stdin.on("end", ()=>{ const list=JSON.parse(data.join("")); for (const terminal of list) console.log(terminal.id); });'
}

send_to_terminal() {
local terminal_id="$1"
shift
cd "$REPO_ROOT"
npm run --silent cli -- terminal send-keys "$terminal_id" "$@"
}

set_simulator_clipboard() {
local text="$1"
if xcrun simctl pbcopy "$SIMULATOR_UDID" <<< "$text" 2>/dev/null; then
log "Set simulator clipboard"
else
log "WARNING: could not set simulator clipboard; flow may fail"
fi
}

read_simulator_clipboard() {
xcrun simctl pbpaste "$SIMULATOR_UDID" 2>/dev/null || true
}

capture_terminal() {
local terminal_id="$1"
cd "$REPO_ROOT"
npm run --silent cli -- terminal capture --scrollback "$terminal_id"
}

write_temp_flow() {
local name="$1"
local flow_dir="${TMPDIR:-/tmp}/paseo-native-terminal-maestro-flows"
mkdir -p "$flow_dir"
local flow_file
flow_file="$(mktemp "$flow_dir/$name.XXXXXX")"
cat >"$flow_file"
printf '%s\n' "$flow_file"
}

maestro_bin() {
local bin
bin="$(command -v maestro || true)"
if [[ -z "$bin" ]]; then
log "ERROR: maestro not found on PATH"
return 1
fi
printf '%s\n' "$bin"
}

run_maestro_flow() {
local flow_file="$1"
local bin
bin="$(maestro_bin)"
local output_dir
output_dir="$(mktemp -d "${TMPDIR:-/tmp}/paseo-native-terminal-maestro-output.XXXXXX")"
log "Running Maestro flow $(basename "$flow_file") with screenshots in $output_dir"
(cd "$output_dir" && "$bin" test --udid "$SIMULATOR_UDID" "$flow_file") >&2
}

run_maestro_flow_allow_failure() {
local flow_file="$1"
local bin
bin="$(maestro_bin)"
local output_dir
output_dir="$(mktemp -d "${TMPDIR:-/tmp}/paseo-native-terminal-maestro-output.XXXXXX")"
log "Running Maestro probe $(basename "$flow_file") with screenshots in $output_dir"
set +e
(cd "$output_dir" && "$bin" test --udid "$SIMULATOR_UDID" "$flow_file") >&2
local status=$?
set -e
return "$status"
}

assert_terminal_surface_visible() {
local flow_file
flow_file="$(write_temp_flow terminal-visible <<YAML
appId: $APP_ID
---
- assertVisible:
id: "terminal-virtual-keyboard"
YAML
)"
run_maestro_flow_allow_failure "$flow_file"
}

ensure_terminal_surface_visible() {
if assert_terminal_surface_visible; then
return
fi

log "Terminal surface is not visible; trying to create/open a terminal from the workspace header"
local flow_file
flow_file="$(write_temp_flow ensure-terminal <<YAML
appId: $APP_ID
---
- runFlow:
when:
visible:
id: "sidebar-sessions"
commands:
- tapOn:
id: "sidebar-close"
- waitForAnimationToEnd
- tapOn:
id: "workspace-header-menu-trigger"
- tapOn:
id: "workspace-header-new-terminal"
- extendedWaitUntil:
visible:
id: "terminal-virtual-keyboard"
timeout: 30000
YAML
)"
run_maestro_flow "$flow_file"
}

assert_text_visible() {
local text="$1"
local flow_file
flow_file="$(write_temp_flow text-visible <<YAML
appId: $APP_ID
---
- extendedWaitUntil:
visible: "$text"
timeout: 8000
YAML
)"
run_maestro_flow_allow_failure "$flow_file"
}

visible_terminal_id() {
ensure_terminal_surface_visible
local ids
ids="$(terminal_ids)"
if [[ -z "$ids" ]]; then
log "ERROR: no terminals found via 'paseo terminal ls --all'"
return 1
fi

local terminal_id
while IFS= read -r terminal_id; do
[[ -n "$terminal_id" ]] || continue
local marker="VT_$RANDOM"
log "Probing visible terminal candidate $terminal_id"
send_to_terminal "$terminal_id" "clear" Enter >/dev/null
send_to_terminal "$terminal_id" "echo $marker" Enter >/dev/null
if assert_text_visible "$marker"; then
printf '%s\n' "$terminal_id"
return
fi
done <<< "$ids"

log "ERROR: no listed terminal matched the visible terminal surface"
return 1
}

assert_terminal_output_contains() {
local terminal_id="$1"
local marker="$2"
local output
output="$(capture_terminal "$terminal_id")"
if [[ "$output" == *"$marker"* ]]; then
log "PASS: terminal output contains $marker"
return
fi
log "FAIL: terminal output did not contain $marker"
return 1
}

assert_terminal_output_count_at_least() {
local terminal_id="$1"
local marker="$2"
local minimum="$3"
local output
output="$(capture_terminal "$terminal_id")"
local count
count="$(MARKER="$marker" OUTPUT="$output" node -e 'const marker = process.env.MARKER ?? ""; const output = process.env.OUTPUT ?? ""; process.stdout.write(String(marker ? output.split(marker).length - 1 : 0));')"
if (( count >= minimum )); then
log "PASS: terminal output contains $marker $count times"
return
fi
log "FAIL: terminal output contained $marker $count times, expected at least $minimum"
return 1
}

assert_maestro_input_does_not_reach_terminal() {
local terminal_id="$1"
local marker="$2"
local flow_file
flow_file="$(write_temp_flow no-focus-input-probe <<YAML
appId: $APP_ID
---
- inputText: "$marker"
YAML
)"

if run_maestro_flow_allow_failure "$flow_file"; then
log "Maestro inputText completed; checking that terminal did not receive $marker"
else
log "Maestro inputText had no focused terminal target; checking output anyway"
fi

local output
output="$(capture_terminal "$terminal_id")"
if [[ "$output" == *"$marker"* ]]; then
log "FAIL: terminal received no-focus probe $marker"
return 1
fi
log "PASS: terminal did not receive no-focus probe $marker"
}

# Default per-flow setup: find the visible terminal and optionally seed it.
# Callers can override NATIVE_TERMINAL_ID before sourcing.
: "${NATIVE_TERMINAL_ID:=}"

require_terminal_id() {
if [[ -z "$NATIVE_TERMINAL_ID" ]]; then
NATIVE_TERMINAL_ID="$(visible_terminal_id)"
fi
if [[ -z "$NATIVE_TERMINAL_ID" ]]; then
log "ERROR: no visible active terminal found"
return 1
fi
log "Using terminal $NATIVE_TERMINAL_ID"
}
14 changes: 14 additions & 0 deletions packages/app/maestro/harness/run-native-terminal-history-arrows.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Seeding shell history for history-arrows flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter
send_to_terminal "$NATIVE_TERMINAL_ID" "echo MAESTRO_HISTORY_OK-1" Enter

run_maestro_flow "$MAESTRO_DIR/native-terminal-history-arrows.yaml"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Seeding scrollback for scroll-no-focus flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter

# Emit a top marker, a screenful of filler, and a bottom marker so scrolling
# up reveals the top marker while the bottom marker is initially visible.
seed_command='{ echo SCROLL_TOP_MARKER; for i in $(seq 1 80); do echo filler-$i; done; echo SCROLL_BOTTOM_MARKER; }'
send_to_terminal "$NATIVE_TERMINAL_ID" "$seed_command" Enter

run_maestro_flow "$MAESTRO_DIR/native-terminal-scroll-does-not-focus.yaml"
assert_maestro_input_does_not_reach_terminal \
"$NATIVE_TERMINAL_ID" \
"SCROLL_SHOULD_NOT_TYPE_$RANDOM"
26 changes: 26 additions & 0 deletions packages/app/maestro/harness/run-native-terminal-selection-drag.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Seeding selectable text for selection-drag flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter
send_to_terminal "$NATIVE_TERMINAL_ID" "echo SELECT_DRAG_OK" Enter

run_maestro_flow "$MAESTRO_DIR/native-terminal-selection-drag-does-not-focus.yaml"
assert_maestro_input_does_not_reach_terminal \
"$NATIVE_TERMINAL_ID" \
"SELECTION_DRAG_SHOULD_NOT_TYPE_$RANDOM"

log "Reading simulator clipboard"
CLIPBOARD="$(read_simulator_clipboard)"
if [[ -n "$CLIPBOARD" && ("SELECT_DRAG_OK" == "$CLIPBOARD"* || "$CLIPBOARD" == *"SELECT_DRAG_OK"*) ]]; then
log "PASS: Copy wrote selected terminal marker text: $CLIPBOARD"
else
log "FAIL: Copy did not write selected terminal marker text: $CLIPBOARD"
exit 1
fi
17 changes: 17 additions & 0 deletions packages/app/maestro/harness/run-native-terminal-sidebar-swipe.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Ensuring terminal is visible for sidebar-swipe flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter
send_to_terminal "$NATIVE_TERMINAL_ID" "echo SIDEBAR_SWIPE_OK" Enter

run_maestro_flow "$MAESTRO_DIR/native-terminal-sidebar-swipe-does-not-focus.yaml"
assert_maestro_input_does_not_reach_terminal \
"$NATIVE_TERMINAL_ID" \
"SIDEBAR_SWIPE_SHOULD_NOT_TYPE_$RANDOM"
14 changes: 14 additions & 0 deletions packages/app/maestro/harness/run-native-terminal-tap-focus.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Clearing terminal for tap-focus flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter

run_maestro_flow "$MAESTRO_DIR/native-terminal-tap-focus-keyboard.yaml"
assert_terminal_output_contains "$NATIVE_TERMINAL_ID" "MAESTRO_TAP_FOCUS_OK"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./native-terminal-maestro-common.sh
source "$SCRIPT_DIR/native-terminal-maestro-common.sh"

require_terminal_id

log "Seeding terminal virtual keyboard UX flow"
send_to_terminal "$NATIVE_TERMINAL_ID" "clear" Enter
send_to_terminal "$NATIVE_TERMINAL_ID" "printf '\n\n\n\nMAESTRO_COPY_OK\n'" Enter
send_to_terminal "$NATIVE_TERMINAL_ID" "echo MAESTRO_VK_HISTORY_OK" Enter
set_simulator_clipboard "echo MAESTRO_PASTE_OK"

run_maestro_flow "$MAESTRO_DIR/native-terminal-virtual-keyboard-ux.yaml"

assert_terminal_output_count_at_least "$NATIVE_TERMINAL_ID" "MAESTRO_VK_HISTORY_OK" 2
assert_terminal_output_contains "$NATIVE_TERMINAL_ID" "MAESTRO_TOGGLE_INPUT_OK"
assert_terminal_output_contains "$NATIVE_TERMINAL_ID" "MAESTRO_PASTE_OK"

log "Reading simulator clipboard after Copy"
CLIPBOARD="$(read_simulator_clipboard)"
if [[ -n "$CLIPBOARD" && ("MAESTRO_COPY_OK" == "$CLIPBOARD"* || "$CLIPBOARD" == *"MAESTRO_COPY_OK"*) ]]; then
log "PASS: Copy wrote selected terminal text: $CLIPBOARD"
else
log "FAIL: Copy did not write selected terminal text: $CLIPBOARD"
exit 1
fi
29 changes: 29 additions & 0 deletions packages/app/maestro/native-terminal-history-arrows.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
appId: sh.paseo.debug
---
# Self-contained history/arrows flow.
# Precondition: the harness has run `echo MAESTRO_HISTORY_OK-1` in the
# terminal, so shell history contains the command.

- assertVisible:
id: "terminal-virtual-keyboard"
- hideKeyboard
- waitForAnimationToEnd

# The seeded command output should already be visible.
- assertVisible:
text: "MAESTRO_HISTORY_OK-1"

# Tap the virtual up arrow to recall the last command.
- tapOn:
id: "terminal-key-up"

# Submit it again.
- tapOn:
id: "terminal-key-enter"

# The command ran a second time, so the marker is still visible.
- extendedWaitUntil:
visible: "MAESTRO_HISTORY_OK-1"
timeout: 15000

- takeScreenshot: native-terminal-history-arrows
Loading
Loading