android: the faces the walk had never seen — six failure states, read back in their own words
Round thirty-one gave the phone honest error states and shipped six of them unobserved, because neither `loadHome` nor `loadLyrics` was reachable from the `__ss` handle and both answer a second ask with nothing. The handle carries the two stores and their two loaders now, and a block of mock steps arms each failure with the mock's own weather, drives the face, and asserts the sentence a person reads as well as the absence of the Rust string behind it: Home's catalog line, the Gaps line and its second tap, a catalog record's stub, the artist page's two different answers, a playlist that would not read, the lyrics lookup that failed, a refused transfer, and the library toast's Try again, pressed for the first time. It sits before the first-run block, which reloads and empties the mock's follows; every failure is disarmed by the step that proves the face heals, and a last step asserts nothing was left armed. The phone walk gets a safe step of its own — a terminal transfer row written into the store and taken away, nothing fetched and nothing reaching the shade. ANDROID.md now names what the walk still cannot see: three screens that draw a raw backend string, six error faces with no expectation, and the history mirror printed on every step and asserted on none.
This commit is contained in:
1 parent
f2cde4ec7c
commit
537bfdd5af
3 files changed
+235
-5
No files matched your search
+47
-3
@@ -940,6 +940,39 @@ merely not there yet (a fresh install still works), and refuse with
|
||||
into a row under Library folder with the path and what to do about it, and the
|
||||
toast says it in English.
|
||||
|
||||
**What the walk has seen.** The round that wrote all of the above was straight
|
||||
about what it had not proved: six of these fixes shipped with no expectation at
|
||||
all, because neither `loadHome` nor `loadLyrics` was reachable from the `__ss`
|
||||
handle the walk drives the app through, and both answer a second ask with
|
||||
nothing — `loadHome` is gated on `started`, `loadLyrics` on the track's own
|
||||
`key` — so no amount of tapping re-runs them. They are driven now: Home's
|
||||
`catalogDown`, the Gaps line, a catalog record's stub, the artist page's two
|
||||
different sentences, a playlist that would not read, the lyrics `failed` face,
|
||||
and `failed · <sentence>` on a transfer row. Each is armed with
|
||||
`__ssMock.fail`, asserted both by the words a person reads and by the Rust
|
||||
string being absent from them, and disarmed by the step that proves the face
|
||||
heals. The handle grew four names for it and no more — `home` and `loadHome`,
|
||||
`lyr` and `loadLyrics` — because a test seam that exports the internals
|
||||
wholesale stops being a seam. The toast's own **Try again** is pressed for the
|
||||
first time too, through You → **Rescan library**: `lib.error` is written by
|
||||
`loadLibrary` alone, and that row is the only door in the app that reaches it
|
||||
a second time.
|
||||
|
||||
What is still unseen, honestly. **Three sites draw a raw backend string**, so
|
||||
the rule above has holes: `screens/Browse.svelte` — a peer's shares that would
|
||||
not read, which is a full error face, Try again and all, that was simply missed
|
||||
— and `screens/Import.svelte`, twice. `SyncRoom.svelte` is a fourth, by way of
|
||||
`saveTapSync`'s catch. **Six error faces have no expectation either**: Browse's,
|
||||
Community's "The server did not answer.", Room's "Not in this room.", Album's
|
||||
"That record is gone.", Playlist's "Playlist not found." and Peer's "Not
|
||||
compared yet." — all reachable, the first two through the mock's weather and
|
||||
the rest by pushing a nav entry with a dead id. And of the probe's fields, six
|
||||
are still read out on every step and asserted on none: `accent`, `xlive`, `hd`,
|
||||
`hwant`, `navMs` and `a11yWho`. The cheapest real win left in the harness is a
|
||||
global `hd === hwant` check on every named step — the history mirror is how the
|
||||
blank page after a confirmed delete was found — but it would fire during the
|
||||
first-run reloads and needs its own thinking about the suppress window first.
|
||||
|
||||
## The downloads in the shade
|
||||
|
||||
Transfers is a screen inside the app; the desktop says what a Save is doing
|
||||
@@ -1255,7 +1288,14 @@ weather: a library index that will not read and its Try again, the client taken
|
||||
offline, a refused password and a server that cannot be reached, Search with
|
||||
nowhere to search, a room list that never arrives, and a rescan that refuses
|
||||
because the folder is gone — then all of it cleared and the app left as it was
|
||||
found. The mock answers every command the phone
|
||||
found. After the weather come the faces that failure actually draws, each one
|
||||
armed by the mock and read back by its own words: Home with every follow's
|
||||
catalog page refused, the Gaps line and its second tap, a catalog record's stub
|
||||
and its Try again, an artist page whose lookup failed beside one that was
|
||||
answered with nothing, a playlist that would not read, the lyrics face on a
|
||||
lookup that never came back, a transfer that was refused, and the library
|
||||
toast's own Try again pressed for the first time — then all of that disarmed
|
||||
too, and asserted disarmed. The mock answers every command the phone
|
||||
uses; one it does not know is named in the report (`[mock] no mock for X`)
|
||||
rather than surfacing as a TypeError toast, console warnings carry their top
|
||||
frames (the smoke bundle is built unminified for that), and every probe
|
||||
@@ -1269,8 +1309,12 @@ backend funnels through `with_client()` answers "not connected to Soulseek",
|
||||
the two connects answer the server string, and `soulseek:state` goes out — and
|
||||
`clear()` puts all of it back. They reject with a plain string because that is
|
||||
what Tauri rejects with. Fourteen mock steps drive them, and the phone walk carries
|
||||
one safe step of its own (`trouble-words`: eleven real backend strings through
|
||||
`__ss.say` on the device, every output asserted free of Rust). A mock step can
|
||||
two safe steps of its own (`trouble-words`: eleven real backend strings through
|
||||
`__ss.say` on the device, every output asserted free of Rust; and
|
||||
`trouble-xfer`: one already-failed transfer row written into the store and
|
||||
taken away again — nothing fetched, and a terminal row is not in flight, so
|
||||
nothing reaches the shade). Fifteen more mock steps drive the failure faces
|
||||
themselves, which are described under **When things go wrong**. A mock step can
|
||||
also set the
|
||||
viewport — `{ view: [915, 411] }` sends `Emulation.setDeviceMetricsOverride`
|
||||
through the same client, which is how the walk turns the phone over and back;
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
import Diagnostics from "./screens/Diagnostics.svelte";
|
||||
import { transfers, bindTransfers, active as activeTransfers } from "./transfers.svelte.js";
|
||||
import { xfernote, syncShade, bindXferNote } from "./xfernote.svelte.js";
|
||||
import { bindHome } from "./home.svelte.js";
|
||||
import { home, bindHome, loadHome } from "./home.svelte.js";
|
||||
import { lyr, loadLyrics } from "./lyrics.svelte.js";
|
||||
import Stub from "./screens/Stub.svelte";
|
||||
import { lib, loadLibrary, refreshRecents, albumById, albumsBy, watchLibrary } from "./lib.svelte.js";
|
||||
import { art, wantArt, accentFor, applyAccent } from "./art.svelte.js";
|
||||
@@ -316,7 +317,7 @@
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => { clearTimeout(sealTimer); sealBoot(); }));
|
||||
/* the probe handle — the desktop has __soulstream for the same reason:
|
||||
tests/mobile/smoke.mjs drives the real app through it */
|
||||
window.__ss = { nav, top, go, push, back, openSheet, closeSheet, openHalf, closeHalf, player, lib, art, catalog, slsk, boot, net, resolver, streamSong, streamCatalogAlbum, sources, api, transfers, xfn: xfernote, pls, notice, importer, imp: { startFolder, startFile }, setup, openSetup, setupState, wifi: { downloadsAllowed, cellular }, next, prev, toggle, seekSecs, playQueueAt, patch, contacts, loadContacts, say, refreshRooms };
|
||||
window.__ss = { nav, top, go, push, back, openSheet, closeSheet, openHalf, closeHalf, player, lib, art, catalog, home, loadHome, lyr, loadLyrics, slsk, boot, net, resolver, streamSong, streamCatalogAlbum, sources, api, transfers, xfn: xfernote, pls, notice, importer, imp: { startFolder, startFile }, setup, openSetup, setupState, wifi: { downloadsAllowed, cellular }, next, prev, toggle, seekSecs, playQueueAt, patch, contacts, loadContacts, say, refreshRooms };
|
||||
return () => { if (unlisten) unlisten(); };
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -347,6 +347,26 @@ const PROBE = `(function () {
|
||||
folderGone: (document.querySelector('.folder-gone') || {}).textContent || '',
|
||||
lyrNote: (document.querySelector('.lyr-note') || {}).textContent || '',
|
||||
lyrTools: !!document.querySelector('.lyr-tools'),
|
||||
/* ── the faces round thirty-one drew and no step had ever read ──
|
||||
Home's own line and the slots behind it (how many follows it
|
||||
walked, how many came back empty-handed); the catalog's note on
|
||||
an artist page; whatever stub is up, whitespace collapsed; the
|
||||
Gaps line, the BUTTON only — the '.gapline.done' variant is a
|
||||
div and says something else entirely; the failed transfer's
|
||||
sentence; and whether the lyrics lookup itself failed, as
|
||||
opposed to having found nothing. Every backslash here is doubled
|
||||
and there is not one backtick, comment included — this block
|
||||
lives inside a template literal, and a backtick would END it
|
||||
(the file still PARSES, so node --check says nothing: what it
|
||||
becomes is a tagged template, and smoke.mjs throws on import). */
|
||||
catDown: (document.querySelector('.sec-note.cat-down') || {}).textContent || '',
|
||||
homeSlots: (function () { try { return __ss.home.order.length; } catch (e) { return -1; } })(),
|
||||
homeErr: (function () { try { return __ss.home.order.filter(function (id) { var s = __ss.home.slots[id]; return !!(s && s.error); }).length; } catch (e) { return -1; } })(),
|
||||
catNote: (function () { var n = [].slice.call(document.querySelectorAll('.screen .sec-note:not(.cat-down)')).find(function (x) { return /catalog/i.test(x.textContent || ''); }); return n ? n.textContent.replace(/\\s+/g, ' ').trim() : ''; })(),
|
||||
stubText: ((document.querySelector('.screen .stub') || {}).textContent || '').replace(/\\s+/g, ' ').trim(),
|
||||
gapLine: ((document.querySelector('button.gapline') || {}).textContent || '').replace(/\\s+/g, ' ').trim(),
|
||||
xferFail: (document.querySelector('.row.xfer.failed .m') || {}).textContent || '',
|
||||
lyrFailed: (function () { try { return __ss.lyr.failed ? 'yes' : 'no'; } catch (e) { return ''; } })(),
|
||||
trks: document.querySelectorAll('.screen .trk').length,
|
||||
/* the phone's trouble-words step leaves its eleven sentences here */
|
||||
trouble: (function () { try { return String(window.__trouble || ''); } catch (e) { return ''; } })(),
|
||||
@@ -971,6 +991,105 @@ const MOCK_STEPS = [
|
||||
{ do: `(function () { __ss.slsk.roomsWaitMs = 12000; __ssMock.clear(); __ss.refreshRooms(); __ss.player.error = ''; __ss.lib.error = ''; __ss.player.retry = null; return true; })()`, wait: 900 },
|
||||
{ do: `__settle()`, wait: 900 },
|
||||
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "weather-clear", wait: 1000 },
|
||||
/* ── the faces no walk had seen ─────────────────────────────────────
|
||||
Round thirty-one gave six screens an honest failure and its own
|
||||
verifier was straight about what it had shipped unobserved: Home's
|
||||
`catalogDown`, the lyrics `failed` note, Gaps' `gapErr`, Playlist's
|
||||
`loadErr`, the catalog record's and the artist page's stubs, and
|
||||
`failed · <sentence>` on a transfer row. It could drive none of them
|
||||
because neither `loadHome` nor `loadLyrics` was reachable from the
|
||||
`__ss` handle — both are gated on `started` / `key` and answer a
|
||||
second ask with nothing at all, so no amount of tapping re-runs
|
||||
them. The handle carries the two stores and their two loaders now,
|
||||
and this block is what they were added for.
|
||||
|
||||
It sits HERE, before the first run, and not at the end of the list:
|
||||
the block below reloads the page twice, and the mock's follows live
|
||||
in a module-level `let` that a reload empties — after `fresh-off`
|
||||
nobody is followed, and Home's `catalogDown` (which needs a follow)
|
||||
and the Gaps shelf (which IS the follows) are both structurally
|
||||
unreachable. Here the block inherits what the weather left behind:
|
||||
signed in, on Home, Daft Punk followed, a track on the mini bar.
|
||||
|
||||
Every assertion is on settled state — an armed failure stays armed,
|
||||
an error stub stays up, the toast stays until it is dismissed — and
|
||||
the two forced loads return their promise, which `cdp.evaluate`
|
||||
awaits, so the fetch is over before the step's wait even begins.
|
||||
Every failure is disarmed by the step that proves the face heals,
|
||||
and `faces-clear` is the proof that nothing was left armed. */
|
||||
{ do: `(function () { __ssMock.clear(); __ss.player.error = ''; __ss.lib.error = ''; __ss.player.retry = null; return __settle(); })()`, wait: 800 },
|
||||
/* Home with every follow's catalog page refused. `home.svelte.js` has
|
||||
always set `slot.error` there; until round thirty-one nothing read
|
||||
it, and until this step nothing had ever seen the line that does. */
|
||||
{ do: `(function () { __ssMock.fail('catalog_artist', 'io: failed to lookup address information: no address associated with hostname'); return __ss.loadHome(true); })()`, name: "faces-home-down", wait: 1200 },
|
||||
/* the same armed failure, on the other screen that asks the same
|
||||
command: Gaps. Home stays poisoned through these three steps on
|
||||
purpose — it is asserted above, and the second tap below is what
|
||||
clears the weather for both. */
|
||||
{ do: `document.querySelectorAll('.nav button')[2].click()`, wait: 800 },
|
||||
{ do: `__click('.segtabs button', 'Gaps')`, wait: 700 },
|
||||
{ do: `__click('.gapline', null)`, name: "faces-gap-down", wait: 1400 },
|
||||
/* a failed lookup leaves `gapRows[key]` undefined, so the second tap
|
||||
genuinely asks again rather than merely opening a rail */
|
||||
{ do: `__ssMock.fail('catalog_artist', '')`, wait: 300 },
|
||||
{ do: `__click('.gapline', null)`, name: "faces-gap-again", wait: 1600 },
|
||||
/* a catalog record. The walk has already made 302127 `ready` and
|
||||
`lookupAlbum` returns early on anything but an error, so the entry is
|
||||
forgotten first — the smallest honest way to say "ask again". */
|
||||
{ do: `(function () { delete __ss.catalog.albums[302127]; return __ssMock.fail('catalog_album', 'io: connection reset by peer'); })()`, wait: 300 },
|
||||
{ do: `__ss.push('catalogAlbum', { id: 302127, artist: 'Daft Punk', title: 'Discovery' })`, name: "faces-album-down", wait: 1500 },
|
||||
{ do: `__ssMock.fail('catalog_album', '')`, wait: 300 },
|
||||
{ do: `__click('.imp-foot .btn', 'Try again')`, name: "faces-album-again", wait: 1600 },
|
||||
{ do: `__ss.back()`, wait: 700 },
|
||||
/* the artist page has TWO of these and they say different things: a
|
||||
lookup that FAILED is "didn't answer", one that came back empty is
|
||||
"had nothing to add". `bestArtistFor` is what has to fail for the
|
||||
first — failing `catalog_artist` alone never reaches the page at all
|
||||
and leaves the status at none, which is the other copy. */
|
||||
{ do: `__ssMock.fail('catalog_search', 'io: failed to lookup address information: no address associated with hostname')`, wait: 250 },
|
||||
{ do: `__ss.push('artist', { name: 'Hollow Signal' })`, name: "faces-artist-down", wait: 1600 },
|
||||
{ do: `(function () { __ssMock.fail('catalog_search', ''); __ss.back(); return true; })()`, wait: 700 },
|
||||
{ do: `__ss.push('artist', { name: 'Hollow Signal' })`, name: "faces-artist-none", wait: 1600 },
|
||||
{ do: `__ss.back()`, wait: 700 },
|
||||
/* a playlist that will not read. Id 1 survives the whole walk — it is
|
||||
the one renamed to "Late night drive II"; the one deleted was New
|
||||
Playlist. Try again clears `loadErr` and bumps `again`, which is
|
||||
what re-arms the effect. */
|
||||
{ do: `__ssMock.fail('playlist_items', 'unable to open database file')`, wait: 250 },
|
||||
{ do: `__ss.push('playlist', { id: 1 })`, name: "faces-playlist-down", wait: 1400 },
|
||||
{ do: `__ssMock.fail('playlist_items', '')`, wait: 300 },
|
||||
{ do: `__click('.imp-foot .btn', 'Try again')`, name: "faces-playlist-again", wait: 1400 },
|
||||
{ do: `__ss.back()`, wait: 700 },
|
||||
/* the lyrics face. Sheet.svelte asks with the UN-forced `loadLyrics()`,
|
||||
which returns at once while `lyr.key` is already this track's — so
|
||||
the face alone can never ask again, which is exactly why round
|
||||
thirty-one could not drive this one. The loader, forced. */
|
||||
{ do: `(function () { __ss.openSheet(); return true; })()`, wait: 900 },
|
||||
{ do: `document.querySelectorAll('.fbtn')[1].click()`, wait: 900 },
|
||||
{ do: `(function () { __ssMock.fail('lyrics_get', 'io: failed to lookup address information: no address associated with hostname'); return __ss.loadLyrics(true); })()`, name: "faces-lyrics-down", wait: 1200 },
|
||||
{ do: `(function () { __ssMock.fail('lyrics_get', ''); return true; })()`, wait: 250 },
|
||||
{ do: `__ss.loadLyrics(true)`, name: "faces-lyrics-again", wait: 1200 },
|
||||
{ do: `(function () { document.querySelectorAll('.fbtn')[0].click(); __ss.closeSheet(); return true; })()`, wait: 700 },
|
||||
/* a transfer that was refused, written straight into the store the way
|
||||
the shade steps above do — nothing is fetched, and `failed` is in
|
||||
DONE, so `xfernote.shape()` sees nothing in flight and posts no
|
||||
notice. The id is far above the backend's own counter, and `list()`
|
||||
sorts id-descending, so this row is the first one on the screen. */
|
||||
{ do: `(function () { __ss.transfers.items[990011] = { id: 990011, username: 'walk_peer', path: 'Walk\\\\walk-c.flac', name: 'walk-c.flac', state: 'failed', queue_place: null, local_path: null, error: 'transfer rejected: File not shared.', received: 0, total: 0 }; __ss.push('transfers', {}); return true; })()`, name: "faces-xfer-failed", wait: 1300 },
|
||||
{ do: `(function () { delete __ss.transfers.items[990011]; __ss.back(); return true; })()`, wait: 800 },
|
||||
/* the one toast action round thirty-one armed that no step had ever
|
||||
pressed. `lib.error` is written by `loadLibrary` alone —
|
||||
`watchLibrary`'s own reload swallows its failure — so You → Rescan
|
||||
library is the only door in the app that reaches it twice. */
|
||||
{ do: `document.querySelectorAll('.nav button')[4].click()`, wait: 800 },
|
||||
{ do: `__ssMock.fail('library_albums', 'unable to open database file')`, wait: 250 },
|
||||
{ do: `__click('.screen .row', 'Rescan library')`, name: "faces-library-toast", wait: 1500 },
|
||||
{ do: `__ssMock.fail('library_albums', '')`, wait: 300 },
|
||||
{ do: `__click('.toast:not(.notice) .na', 'Try again')`, name: "faces-library-again", wait: 1400 },
|
||||
/* and all of it put away, so the first run below starts exactly where
|
||||
it would have started without this block at all */
|
||||
{ do: `(function () { __ssMock.clear(); __ss.player.error = ''; __ss.lib.error = ''; __ss.player.retry = null; __settle(); return __ss.loadHome(true); })()`, wait: 900 },
|
||||
{ name: "faces-clear", wait: 1200 },
|
||||
/* ── the first run, on a device that has just been installed ──────
|
||||
The mock is the whole backend here, so it can answer as one: no
|
||||
records, no playlists, no account, no all-files permission, and the
|
||||
@@ -1108,6 +1227,46 @@ const MOCK_EXPECT = {
|
||||
"weather-rescan": (i) => /1A2B-3C4D/.test(i.folderGone) && /isn't there any more/.test(i.toast) && /1A2B-3C4D/.test(i.toast)
|
||||
&& !/library-folder-missing/.test(i.toast + i.folderGone),
|
||||
"weather-clear": (i) => !i.toast && !i.why && !i.folderGone && !i.roomsNote && i.slskOn === "online" && /^(Good |Still)/.test(i.title),
|
||||
/* ── the faces no walk had seen ─────────────────────────────────────
|
||||
Each of these fails if round thirty-one's fix is undone: stop reading
|
||||
`slot.error` and Home's line empties; drop the `gapErr` branch and
|
||||
the Gaps line falls back to "See what's missing" and can be tapped
|
||||
for ever; put `lyr.failed` back and the face says "No lyrics found"
|
||||
about a lookup that never answered; take `say()` out of `stateLine`
|
||||
and the raw string is on the row again. Every one asserts the
|
||||
sentence a person reads AND that the Rust string is not in it — the
|
||||
second half is the round-thirty-one rule, and the half that survives
|
||||
any rewording of the first. */
|
||||
"faces-home-down": (i) => i.homeSlots > 0 && i.homeErr === i.homeSlots
|
||||
&& /^Couldn't reach the catalog, so there's nothing new/.test(i.catDown)
|
||||
&& !/New from your artists/.test(i.secs),
|
||||
"faces-gap-down": (i) => /Couldn't reach the catalogue — tap to try again/.test(i.gapLine) && !i.gapCards,
|
||||
"faces-gap-again": (i) => /more released/.test(i.gapLine) && !/Couldn't reach/.test(i.gapLine) && i.gapCards >= 3,
|
||||
"faces-album-down": (i) => /^The catalog did not answer/.test(i.why) && /The network didn't answer/.test(i.why)
|
||||
&& !/io:|connection reset/.test(i.why) && i.again && !i.trks,
|
||||
"faces-album-again": (i) => !i.why && !i.again && i.trks > 0,
|
||||
/* the pair that proves the distinction is real and not decoration */
|
||||
"faces-artist-down": (i) => /only what this phone holds is here/.test(i.catNote) && /didn't answer/.test(i.stubText)
|
||||
&& !/io:|address information/.test(i.catNote + i.stubText),
|
||||
"faces-artist-none": (i) => !i.catNote && /had nothing to add/.test(i.stubText) && !/didn't answer/.test(i.stubText),
|
||||
"faces-playlist-down": (i) => /^Couldn't read this playlist/.test(i.why) && /library folder isn't readable/.test(i.why)
|
||||
&& i.again && !i.plrows && !/database file/.test(i.why),
|
||||
"faces-playlist-again": (i) => !i.why && !i.again && i.plrows > 0,
|
||||
"faces-lyrics-down": (i) => i.lyrFailed === "yes" && !i.lyrLines && /^Couldn't look up the words/.test(i.lyrNote)
|
||||
&& i.lyrTools && !/io:|address information/.test(i.lyrNote),
|
||||
"faces-lyrics-again": (i) => i.lyrFailed === "no" && i.lyrLines >= 3 && !i.lyrNote,
|
||||
"faces-xfer-failed": (i) => i.title.startsWith("Transfers")
|
||||
&& /failed · They refused the transfer \(File not shared\)/.test(i.xferFail)
|
||||
&& !/transfer rejected/.test(i.xferFail),
|
||||
"faces-library-toast": (i) => /library folder isn't readable right now/.test(i.toast) && i.retry === "Try again"
|
||||
&& !/database file/.test(i.toast),
|
||||
"faces-library-again": (i) => i.title.startsWith("You") && !i.toast && !i.retry,
|
||||
/* the disarm, proved: no toast, no armed action, no stub, Home's slots
|
||||
healthy and its rails back — so the first run below is not quietly
|
||||
running on something this block left standing */
|
||||
"faces-clear": (i) => !i.toast && !i.why && !i.catDown && !i.retry
|
||||
&& i.homeSlots > 0 && i.homeErr === 0
|
||||
&& /New from your artists/.test(i.secs) && /^(Good |Still)/.test(i.title),
|
||||
/* sideways: the chrome budget, then every surface that was clipped. The
|
||||
before-value of each is in the rig's now.json — 46% of the height as
|
||||
chrome, a 879px cover with the transport 687px below the fold, a
|
||||
@@ -1604,6 +1763,17 @@ const PHONE_STEPS = [
|
||||
{ do: `document.querySelector('.scrim').click()`, wait: 700 },
|
||||
{ do: `history.back()`, wait: 800 },
|
||||
{ do: `history.back()`, wait: 900 },
|
||||
/* the one face of the six that a real build can show without arming
|
||||
anything: `__ssMock` does not exist here. One already-terminal row
|
||||
goes into the transfer store — the same thing the shade steps above
|
||||
do — and comes straight back out. Nothing is downloaded, no room is
|
||||
joined, no message is sent, nobody is signed in, and neither the
|
||||
config nor the library is written. A `failed` row is not in flight,
|
||||
so no notice is posted either. The id is far above the backend's own
|
||||
counter and `list()` sorts id-descending, so it is the first row on
|
||||
the screen even if Kyle's own session is carrying real failures. */
|
||||
{ do: `(function () { __ss.transfers.items[990011] = { id: 990011, username: 'walk_peer', path: 'Walk\\\\walk-c.flac', name: 'walk-c.flac', state: 'failed', queue_place: null, local_path: null, error: 'transfer rejected: File not shared.', received: 0, total: 0 }; __ss.push('transfers', {}); return true; })()`, name: "trouble-xfer", wait: 1400 },
|
||||
{ do: `(function () { delete __ss.transfers.items[990011]; __ss.back(); return true; })()`, name: "trouble-xfer-gone", wait: 1000 },
|
||||
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "end", wait: 800 },
|
||||
];
|
||||
const PHONE_EXPECT = {
|
||||
@@ -1737,6 +1907,21 @@ const PHONE_EXPECT = {
|
||||
"dg-page": (i) => i.dgTabs >= 1 && i.dgRows >= 1 && !i.toast && !i.a11yAnon && !i.a11yBad && !i.a11yGhost && !i.a11yState,
|
||||
"dg-sheet": (i) => i.half.includes("up") && /By year/.test(i.halfText) && !i.toast,
|
||||
"dg-az": (i) => i.dgRows >= 1 && /A–Z/.test(i.dgOrder) && !i.toast,
|
||||
/* the row's own words on the device, and then its absence. The "gone"
|
||||
clause is written against THIS row's sentence rather than against
|
||||
`xferFail` being empty: a real failed transfer of Kyle's sitting in
|
||||
the list is not a reason to fail the step, and the real core
|
||||
answering faster than a mock would cannot change either reading —
|
||||
both are settled state, written and removed by the step itself. The
|
||||
screen underneath is deliberately not named: sibling rounds append
|
||||
their own phone steps before `end` too, so whichever screen the back
|
||||
lands on is theirs, not this round's. What IS asserted is that the
|
||||
back popped at all — a sheet left up would close instead and the bar
|
||||
would still say Transfers. */
|
||||
"trouble-xfer": (i) => i.title.startsWith("Transfers")
|
||||
&& /failed · They refused the transfer \(File not shared\)/.test(i.xferFail)
|
||||
&& !/transfer rejected/.test(i.xferFail),
|
||||
"trouble-xfer-gone": (i) => !/^Transfers/.test(i.title) && !/File not shared/.test(i.xferFail),
|
||||
end: (i) => /^(Good |Still)/.test(i.title),
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user