Files
artemis_control/recipes/spotify_play.recipe
Sam Rolfe c178f92851 feat: confirm playback from the media session, and stop demoting the top row
Phase 3 DoD reached end to end: `artemis run spotify_play query="Rolling
Stones Honky Tonk"` played the right song in 13 steps / 35.3s, with playback
paused first so the pass proves the row tap started it.

Two things stopped it before that, both found on the real phone:

- The playback check asserted on an on-screen "Pause" label that Spotify
  never shows: its transport button keeps the label "Play" while music is
  playing. The recipe now ends with a `playing` step that reads the app's
  media session (state PLAYING) and confirms the track title contains the
  query words. A failure reports the state and the track it found.
- tap_row pressed the wrong row. Once a track is loaded its row carries a
  `Paused` state indicator, and counting that content description as row text
  dropped the top hit below an identical duplicate further down, so the tap
  went to y=1443 instead of y=878. Rows are now scored on their title and
  subtitle only (state lives in content descriptions), and scores within
  rowScoreTolerance are treated as equal so the higher row wins, because
  Spotify already ranks by relevance.

internal/ui/rows_test.go reproduces that results screen, including the state
indicator and the duplicate row.

docs/phase-3-spotify-recipe.md records the successful run, the independent
media-session and audio-output reads, and the failure that preceded it.
2026-10-16 14:32:24 +11:00

78 lines
3.4 KiB
Plaintext

# spotify_play — search Spotify for a song and play it.
#
# Edit this file by hand. Steps name the text Artemis must find on screen; it
# taps the centre of the element it matched. There are no coordinates here on
# purpose, so this recipe survives a Spotify redesign (FR-4).
#
# Run it with:
# artemis run spotify_play query="Rolling Stones Honky Tonk"
task: spotify_play
description: Search Spotify for a song and play it.
app: com.spotify.music
# The caller must supply these.
param: query
# Spotify's list rows: title + subtitle inside one clickable container. Naming
# the container keeps the tap on the row itself and off the row's buttons.
row_id: com.spotify.music:id/row_root
step_timeout: 20s
total_timeout: 420s
# Screens Artemis must never tap on. Each one fails the run with the reason, so
# a wrong tap is impossible (FR-8).
guard "Log in" => Spotify is signed out; sign in on the phone and retry
guard "Sign up free" => Spotify is signed out; sign in on the phone and retry
guard "Log in to Spotify" => Spotify is signed out; sign in on the phone and retry
guard "Continue with Facebook" => Spotify is showing its welcome screen; sign in on the phone and retry
guard "Continue with Google" => Spotify is showing its welcome screen; sign in on the phone and retry
guard "Update Spotify" => Spotify wants an update; update it on the phone and retry
guard "What's new" => Spotify is showing an announcement screen; dismiss it on the phone and retry
steps:
# 1. Make sure Spotify is up. Launching an app that is already open is a
# no-op, so this is safe to repeat.
- launch
- foreground 30s
- wait "Search" 30s retry 2
# 2. Open the search tab. Spotify remembers the last tab, so a search screen
# with the previous query can come back: clear that box only when the
# clear button is actually on screen.
- tap "Search"
- tap_if "Clear search query"
# retry: the clear tap above can be dropped while the phone is busy, so press
# it again before giving up on the empty search box.
- wait "What do you want to listen to?" 30s retry 3
- tap "What do you want to listen to?"
# 3. Type the query. The clear button proves the text landed.
- type "{query}"
- wait "Clear search query" 30s
# 4. Wait for the results to replace the "Recents" list, so the next step can
# never match something from the history instead of a search result.
- wait_gone "Recents" 30s
# 5. Give the app a moment. Spotify renders the results list after the query
# lands, and the tap below must land on the settled list.
- sleep 1s
# 6. Play the result that matches the query. tap_row scores every visible
# result row against the query words and taps the best one; equal matches
# go to the highest row, which is how Spotify ranks them.
- tap_row "{query}" 40s
# 7. Confirm playback by asking Android, not by reading the screen. Spotify's
# transport button keeps the label "Play" while music is playing, so a
# label check fails on a phone that is happily playing the track. "playing"
# waits until the app's media session says PLAYING and its track title
# matches the query.
#
# This is the phone's slowest moment, and a tap can be dropped, so "retry 3"
# presses the row again and checks again, up to three more times. It never
# taps anything it did not match by text.
- playing "{query}" 30s retry 3