Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
105 lines
9.9 KiB
Plaintext
105 lines
9.9 KiB
Plaintext
/* ── what the device had never been asked ──────────────────────────
|
|
Every step below was proven in the mock and nowhere else: the
|
|
library's own shape and its sort, a library TRACK's sheet (the
|
|
phone had only ever held a CARD), Search's memory, a queue row's
|
|
sheet, Home's feed, the sleep timer, the stream cache's chip, the
|
|
Wi-Fi-only guard and a port the core has to refuse.
|
|
|
|
It navigates itself from Home, so it needs no particular screen in
|
|
front of it, and every write it makes it takes back inside itself:
|
|
the Library sort is nav state (the entry's own `c.sort`), the sleep
|
|
timer is put out by the step after the one that reads it, the cache
|
|
chip and the Wi-Fi switch are read first and restored last, and the
|
|
refused port writes nothing at all. Nothing here downloads, joins,
|
|
sends, signs in, or touches Kyle's library. */
|
|
{ do: `__settle()`, wait: 900 },
|
|
{ do: `document.querySelectorAll('.nav button')[2].click()`, wait: 700 },
|
|
{ do: `document.querySelectorAll('.segtabs button')[0].click()`, name: "library", wait: 900 },
|
|
{ do: `__click('.chips .chip', 'By format')`, name: "library-by-format", wait: 700 },
|
|
{ do: `__click('.chips .chip', 'Recently added')`, wait: 600 },
|
|
{ do: `document.querySelectorAll('.segtabs button')[1].click()`, name: "artists", wait: 900 },
|
|
/* Gaps is tolerant on purpose: follows live in the library folder and
|
|
the phone's own folder starts without any, so the tab can only be
|
|
asked to render and to stay quiet. Opening it asks the network for
|
|
nothing — each shelf's lookup is behind that shelf's own tap. */
|
|
{ do: `__click('.segtabs button', 'Gaps')`, name: "gaps", wait: 1200 },
|
|
{ do: `document.querySelectorAll('.segtabs button')[0].click()`, wait: 600 },
|
|
{ do: `document.querySelector('.grid2 .release').click()`, wait: 1600 },
|
|
/* a LOCAL track's own sheet, off a real path: Play next, Add to queue,
|
|
Add to playlist, and the Format/File/Folder block underneath them.
|
|
None of it is tapped — the scrim closes it, the page is popped. */
|
|
{ do: `document.querySelector('.trk .r').click()`, name: "track-sheet", wait: 900 },
|
|
{ do: `(function () { var s = document.querySelector('.scrim'); if (s) s.click(); return true; })()`, wait: 600 },
|
|
{ do: `history.back()`, wait: 800 },
|
|
/* Search's memory, which on the device is this WebView's localStorage.
|
|
The chips are the walk's OWN earlier query: the field commits on
|
|
Enter or when the screen is left, and the walk left it several
|
|
screens ago. Clearing the field is what brings the empty page —
|
|
and its recents — back up. */
|
|
{ do: `document.querySelectorAll('.nav button')[1].click()`, wait: 800 },
|
|
{ do: `(function () { var b = document.querySelector('.sfield .iconbtn'); if (b) b.click(); return true; })()`, name: "search-recent", wait: 900 },
|
|
{ do: `__click('.recent-chips .chip', null)`, name: "search-recent-pick", wait: 1600 },
|
|
{ do: `(function () { var b = document.querySelector('.sfield .iconbtn'); if (b) b.click(); return true; })()`, wait: 700 },
|
|
/* a queue row's sheet. The walk has dragged these rows for two rounds
|
|
and never opened one. Tolerant: the walked track can be the last
|
|
queueable row, and then there is no row to press. Nothing in the
|
|
sheet is tapped — its rows all mutate the queue. */
|
|
{ do: `(function () { var m = document.querySelector('.mini'); if (m) m.click(); return true; })()`, wait: 1200 },
|
|
{ do: `document.querySelectorAll('.fbtn')[2].click()`, name: "queue-face", wait: 900 },
|
|
{ do: `(function () { var r = document.querySelector('.q-face .row'); if (r) r.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true })); return !!r; })()`, name: "queue-row-sheet", wait: 900 },
|
|
{ do: `(function () { var s = document.querySelector('.scrim'); if (s) s.click(); return true; })()`, wait: 600 },
|
|
{ do: `document.querySelectorAll('.fbtn')[0].click()`, wait: 500 },
|
|
{ do: `__ss.closeSheet()`, wait: 700 },
|
|
/* Home's own feed on the real core, and then the sleep timer — the one
|
|
thing in this block with a life of its own. It is set, read back off
|
|
the More sheet's Sleep-timer subtitle (which IS `sleepLabel()`, and
|
|
the only proof the phone can give that the timer took), and put out
|
|
by the very next step, with nothing between the two: a walk that
|
|
died in that gap would leave a pause waiting on his playback — so
|
|
it is the SHORTEST chip in the sheet, not a comfortable one. */
|
|
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "home-feed", wait: 1200 },
|
|
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "home-more", wait: 900 },
|
|
{ do: `__click('.halfsheet .row', 'Sleep timer')`, name: "sleep-sheet", wait: 800 },
|
|
{ do: `__click('.halfsheet .chip', '15 min')`, name: "sleep-set", wait: 800 },
|
|
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "sleep-armed", wait: 900 },
|
|
{ do: `(function () { function pick(sel, re) { return [].slice.call(document.querySelectorAll(sel)).find(function (x) { return re.test((x.textContent || '').trim()); }); } var r = pick('.halfsheet .row', /Sleep timer/); if (r) r.click(); return new Promise(function (res) { setTimeout(function () { var c = pick('.halfsheet .chip', /^Off$/); if (c) c.click(); res(!!c); }, 400); }); })()`, name: "sleep-off", wait: 900 },
|
|
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "sleep-cleared", wait: 900 },
|
|
{ do: `(function () { var s = document.querySelector('.scrim'); if (s) s.click(); return true; })()`, wait: 600 },
|
|
/* You: the stream cache's retention chip, read first and put back
|
|
last. The value is stashed IN THE PAGE as well as in the harness,
|
|
because a step's script is fixed before the walk starts and cannot
|
|
carry the harness's copy. The last chip is never clicked — "Forever"
|
|
does not lengthen the cache, it saves streamed tracks into the
|
|
library — so the move is to the neighbour above, or below it when
|
|
there is no room above. And when Forever is the chip that is ON,
|
|
nothing is touched at all: setRetention("forever") sends the FLAG
|
|
and not the number, so a move away and back would put the switch
|
|
back and leave `stream_retention_days` on whatever the move wrote.
|
|
CONDITIONAL, then, on his own setting — and the expectation knows
|
|
which case it is in from the label `you-keep-arm` stashed. */
|
|
{ do: `document.querySelectorAll('.nav button')[4].click()`, wait: 900 },
|
|
{ name: "you-keep-arm", wait: 500 },
|
|
{ do: `(function () { var g = document.querySelector('.prefs[data-mode="streamcache"] .pref'); var cs = [].slice.call(g ? g.querySelectorAll('.chip') : []); var at = cs.findIndex(function (c) { return c.classList.contains('on'); }); if (at < 0) return ''; window.__keep0 = cs[at].textContent.trim(); if (at === cs.length - 1) return window.__keep0; var pick = (at + 1 < cs.length - 1) ? cs[at + 1] : cs[at - 1]; if (pick) pick.click(); return window.__keep0; })()`, name: "you-keep", wait: 900 },
|
|
{ do: `(function () { var g = document.querySelector('.prefs[data-mode="streamcache"] .pref'); var cs = [].slice.call(g ? g.querySelectorAll('.chip') : []); var back = cs.find(function (c) { return c.textContent.trim() === window.__keep0; }); if (back && !back.classList.contains('on')) back.click(); return window.__keep0; })()`, name: "you-keep-back", wait: 900 },
|
|
/* the one guard that exists only because this is a phone. The switch
|
|
is this device's localStorage, not the config; `cellular()` reads
|
|
`navigator.connection`, so a page-level shadow is the whole fake and
|
|
a delete is the whole restore — set and removed one step apart, and
|
|
a reload would drop it anyway. If defineProperty throws in the
|
|
WebView the catch swallows it, the download is allowed, and the step
|
|
fails loudly rather than doing anything. The switch goes back to
|
|
whatever Kyle had it on. */
|
|
{ name: "wifi-arm", wait: 500 },
|
|
{ do: `(function () { var r = [].slice.call(document.querySelectorAll('.screen .row')).find(function (x) { return /Wi-Fi only/.test(x.textContent); }); if (!r) throw new Error('no Wi-Fi row'); window.__wifi0 = r.querySelector('.sw.on') ? 'on' : 'off'; if (window.__wifi0 === 'off') r.click(); return window.__wifi0; })()`, name: "wifi-on", wait: 800 },
|
|
{ do: `(function () { try { Object.defineProperty(navigator, 'connection', { value: { type: 'cellular' }, configurable: true }); } catch (e) {} return __ss.wifi.downloadsAllowed(); })()`, name: "wifi-refused", wait: 800 },
|
|
{ do: `(function () { try { delete navigator.connection; } catch (e) {} var t = document.querySelector('.toast:not(.notice)'); if (t) t.click(); __ss.player.error = ''; return true; })()`, name: "wifi-cleared", wait: 800 },
|
|
{ do: `(function () { var r = [].slice.call(document.querySelectorAll('.screen .row')).find(function (x) { return /Wi-Fi only/.test(x.textContent); }); if (!r) throw new Error('no Wi-Fi row'); var now = r.querySelector('.sw.on') ? 'on' : 'off'; if (now !== window.__wifi0) r.click(); return window.__wifi0; })()`, name: "wifi-restored", wait: 800 },
|
|
/* a port the core has to refuse. Nothing is written, and 50300 — the
|
|
client's own default, which the port steps above already left
|
|
standing — is still standing afterwards. */
|
|
{ do: `__click('.screen .row', 'Listening port')`, wait: 800 },
|
|
{ do: `(function () { var i = document.querySelector('.halfsheet .prompt .field'); i.value = '80'; i.dispatchEvent(new Event('input', { bubbles: true })); })()`, wait: 400 },
|
|
{ do: `__click('.halfsheet .prompt-acts .btn-fill', 'Save')`, name: "you-port-refused", wait: 1000 },
|
|
{ do: `(function () { var t = document.querySelector('.toast:not(.notice)'); if (t) t.click(); return true; })()`, wait: 600 },
|
|
{ do: `__settle()`, wait: 900 },
|