/* ── round forty: the four steps that could not fail ─────────────── `library-ambience`, `queue`, `lyrics` and `community` have been named phone steps with no key in this map since the rounds that wrote them. The driver reads `expect[s.name] ? expect[s.name](info) : true`, so each one was a screenshot that always passed — four of eighty-eight. All four have a mock expectation; none had a phone one. Each is written to hold against Kyle's own library rather than a fixture. */ "library-ambience": (i) => i.title.startsWith("Your library") && i.amb.includes("on:img"), queue: (i) => i.sheet.includes("up") && i.qFace, /* the walked track may genuinely have no words on the device, so this asks for the face and any one of its three honest states */ lyrics: (i) => i.sheet.includes("up") && (i.lyrLines >= 1 || !!i.lyrNote || i.lyrTools), /* tolerant of the sign-in card, which is what a signed-out phone shows here — but NOT of a toast: a sign-in that failed raises one on this screen, and that is a finding rather than a step to loosen */ community: (i) => i.title.startsWith("Community") && !i.toast, /* ── the walk's own playlist, on real SQLite ───────────────────── The row count is stashed by the sheet step rather than assumed: the playlist is made out of whichever record the walk picked, so its length is his library's business — and a probe that read nothing would otherwise pass every clause below it. */ "playlist-row-sheet": (i) => { globalThis.__plN = i.plrows; return i.half.includes("up") && /Remove from playlist/.test(i.halfText); }, "playlist-removed": (i) => i.plrows === globalThis.__plN - 1 && /Removed/.test(i.notice) && /Undo/.test(i.announce), /* the undo stash round-tripped through the real backend for the first time: the row is back, in the place it was taken from. `__pl0` is the order `playlist-drag-arm` stashed before the two drags, which `playlist-dragged-back` has already put back. */ "playlist-undone": (i) => i.plrows === globalThis.__plN && !i.notice && (globalThis.__plN < 2 || i.plOrder === globalThis.__pl0), /* CONDITIONAL: `lift()` refuses a list of fewer than two rows, so a one-track record — which makes a one-row playlist — has no press to release. The two drag steps above tolerate exactly the same thing. */ "playlist-drag-held": (i) => globalThis.__plN < 2 || (i.half.includes("up") && /Remove from playlist/.test(i.halfText)), /* CONDITIONAL in its last clause: the drag-refusal note needs more than one row to drag. The note itself hangs off the UNFILTERED list, so a filter that matched nothing still shows it. */ "playlist-filter": (i) => i.plrows === 0 && /Nothing here matches/.test(i.stubText) && (globalThis.__plN < 2 || /Clear the search/.test(i.plNote)), "playlist-filter-cleared": (i) => i.plrows === globalThis.__plN && !i.plNote, /* ── the review's Keep switch ──────────────────────────────────── CONDITIONAL: the row renders only when something did not resolve, and a paste that resolved completely leaves `keepOn` false anyway. The second step is what guarantees `import-created` below still sees the three rows it has always seen. */ "import-keep-off": (i) => !i.keepOn && /Create playlist \(\d+ tracks?\)/.test(i.impBtn), "import-keep-on": (i) => /Create playlist \(3 tracks\)/.test(i.impBtn) && i.imp === 3, /* ── the library's own shape, on the device for the first time ──── */ /* `a11yBad` is left out for the reason `track-sheet` leaves it out: a card's name is [title, artist, badge] off his own tags, and `junk()` calls a name of nothing but digits bad — "1999, 311" is a real record. The mock asserts it against a fixture, where it belongs. The structural three stay: nameless, ghost and mis-stated. */ library: (i) => i.title.startsWith("Your library") && i.fmtBadges >= 1 && /hours|min/.test(i.countline) && !i.a11yAnon && !i.a11yGhost && !i.a11yState, /* the sort lives on the Library entry (`c.sort`), not in the config, so it needs no putting back — the chip after it does that anyway. What a real library's top format IS cannot be asserted; that the grid re-sorted and still badges every card can be. */ "library-by-format": (i) => !!i.firstBadge && i.fmtBadges >= 1 && !i.toast && !i.a11yState, artists: (i) => i.title.startsWith("Your library") && i.rowArt >= 1 && !i.toast, /* CONDITIONAL: follows live in the library folder and the phone's own folder starts without any, so a phone with no follows can only be asked for the stub */ gaps: (i) => i.title.startsWith("Your library") && !i.toast && (i.gaplines >= 1 || !!i.stubText), /* a LOCAL track's sheet: the three things it offers, the file block underneath it off a real path, and the layer named and holding focus. `a11yBad` is left out deliberately — it reads the names of real rows, and a record of his is allowed to be called anything. */ "track-sheet": (i) => i.half.includes("up") && /Play next/.test(i.halfText) && /Add to queue/.test(i.halfText) && /Add to playlist/.test(i.halfText) && /Format/.test(i.halfText) && /Folder/.test(i.halfText) && /^dialog\/true\/.+\/focus$/.test(i.dlg) && !i.a11yAnon && !i.a11yGhost, /* the recents are localStorage on the device, written by the walk's own earlier search when that screen was left */ "search-recent": (i) => i.title.startsWith("Search") && i.sq === "" && i.recents >= 1, "search-recent-pick": (i) => i.title.startsWith("Search") && !!i.sq, "queue-face": (i) => i.sheet.includes("up") && i.qFace, /* CONDITIONAL: the walked track can be the last queueable row, and then there is nothing to press. Asserted exactly when there is. */ "queue-row-sheet": (i) => !i.half.includes("up") || (/Remove from queue/.test(i.halfText) && /Play now/.test(i.halfText)), /* Home's feed on a phone whose library folder holds no follows: the store answered, and not one of its slots came back with an error */ "home-feed": (i) => /^(Good |Still)/.test(i.title) && !i.toast && i.homeSlots >= 0 && i.homeErr === 0, "home-more": (i) => i.half.includes("up") && /Sleep timer/.test(i.halfText), "sleep-sheet": (i) => i.half.includes("up") && /Pause after/.test(i.halfText) && /1½ hours/.test(i.halfText), /* every chip in that sheet goes through `act()`, which closes it — so this says nothing at all about the timer. The step after it does. */ "sleep-set": (i) => !i.half.includes("up") && /^(Good |Still)/.test(i.title), /* the proof: the More sheet's Sleep-timer subtitle IS `sleepLabel()`, and the only thing that can make it read "N min left" is a `player.sleepAt` in the future */ "sleep-armed": (i) => i.half.includes("up") && /min left/.test(i.halfText), "sleep-off": (i) => !i.half.includes("up") && /^(Good |Still)/.test(i.title), "sleep-cleared": (i) => i.half.includes("up") && /Sleep timer/.test(i.halfText) && !/min left/.test(i.halfText), /* the retention chip: read, moved, put back. The harness's stash and the page's own (`window.__keep0`) hold the same value for the same reason the drag steps keep two — a step's script is fixed before the walk starts and cannot carry this one. */ "you-keep-arm": (i) => { globalThis.__keepWas = i.keep; return !!i.keep; }, /* CONDITIONAL: the step moves nothing when "Forever" is the chip that is on, because the way back writes the flag and not the number. The label is what says which case this is. */ "you-keep": (i) => !!i.keep && (/^Forever/.test(globalThis.__keepWas) ? i.keep === globalThis.__keepWas : i.keep !== globalThis.__keepWas), "you-keep-back": (i) => i.keep === globalThis.__keepWas, /* the Wi-Fi-only guard, which exists only because this is a phone */ "wifi-arm": (i) => { globalThis.__wifiWas = i.wifiOnly; return !!i.wifiOnly; }, "wifi-on": (i) => i.wifiOnly === "on", "wifi-refused": (i) => /Wi-Fi/.test(i.toast), /* the shadow removed and the sentence taken off the bar, with the switch still where the step above put it */ "wifi-cleared": (i) => !i.toast && i.wifiOnly === "on", "wifi-restored": (i) => i.wifiOnly === globalThis.__wifiWas, /* a port the core refuses: nothing written, and the number the walk already left standing still standing */ "you-port-refused": (i) => !i.half.includes("up") && /between 1024 and 65535/.test(i.toast) && /^50300 /.test(i.port),