android: the whole discography, on a page of its own

The artist page already filed every release the desktop's way, but it only
ever showed one category at a time and it showed all of it: no slice anywhere
in that grid, so an artist with 140 releases drew 140 cards. There is a page
now — All in front of the categories, a sort, a grouping by year, and the
editions opened in place under their own record, which is the part a flat
list loses — and the grid behind it stops at twelve with the way down under
it. It is the same classifier twice over the same cached albums, which the
desktop cannot do because its filing waits on a background description
harvest; the three things its own page has and this one does not — a
tracklist per record, an accent sampled per cover, the scroll-led banner —
are 140 calls and 140 decodes over mobile data for rows that are one tap
away, and ANDROID.md says so rather than leaving them looking forgotten.

The list caps at sixty with the rest a button away, before the grouping, so
the last year on screen can be a part of itself; under a name order By year
is not offered, because the years would repeat out of sequence. The mock
gains a 140-record artist so both caps are walked rather than argued about.
This commit is contained in:
KeelrLeek committed 2026-09-14 03:41:16 -05:00
1 parent 1258b2b78d
commit f2cde4ec7c
9 files changed
+491 -12

No files matched your search

+76
View File
@@ -355,6 +355,19 @@ const PROBE = `(function () {
syEst: (document.querySelector('.sy-est') || {}).textContent || '',
syHint: (document.querySelector('.sy-hint') || {}).textContent || '',
disco: document.querySelectorAll('.grid2 .release').length,
/* the whole-discography page: its rows, its tabs (All in front), the
year headers, an opened edition rail, the cap's own button, the
first row's title, the order the head is showing and the summary
line — plus, back on the artist page, the link down to it */
dgRows: document.querySelectorAll('.dg-row').length,
dgTabs: document.querySelectorAll('.dg-tabs button').length,
dgGroups: document.querySelectorAll('.dg-group').length,
dgEds: document.querySelectorAll('.dg-eds .ed').length,
dgMore: (document.querySelector('.dg-more') || {}).textContent || '',
dgFirst: (document.querySelector('.dg-row .t') || {}).textContent || '',
dgOrder: (document.querySelector('.dg-sec .all') || {}).textContent || '',
dgSum: (document.querySelector('.dg-head .m') || {}).textContent || '',
dgAll: (document.querySelector('.more-link.dg-all') || {}).textContent || '',
/* the artist page's three lanes at once — the page, the hero seed and
the overview — so a step can say what painted and what had not
landed yet: "<page>:<seed>[+art]:<overview>:<releases>" */
@@ -587,6 +600,27 @@ const MOCK_STEPS = [
{ do: `__click('.top button', 'Open')`, name: "cat-artist", wait: 2200 },
{ do: `__click('.stats .fb', 'Follow')`, name: "cat-artist-follow", wait: 900 },
{ do: `__click('.disco-tabs button', 'Singles')`, name: "cat-artist-tab", wait: 700 },
/* the whole discography, the page the artist page's "All" opens: the
eleven mock rows folded to ten families across six categories, the
year headers, a category tab, the one linked family opened in place,
and the sort sheet taking it to a name order — which is where the
grouping has to fall away */
{ do: `__click('.sec .all', 'All')`, name: "disco-all", wait: 1400 },
{ do: `__click('.dg-tabs button', 'Albums')`, name: "disco-albums", wait: 900 },
{ do: `__click('.dg-tabs button', 'All')`, wait: 800 },
{ do: `__click('.dg-eline', null)`, name: "disco-editions", wait: 800 },
{ do: `__click('.dg-sec .all', null)`, name: "disco-sheet", wait: 900 },
{ do: `__click('.halfsheet .chip', 'A–Z')`, name: "disco-az", wait: 900 },
{ do: `document.querySelector('.scrim').click()`, wait: 700 },
{ do: `history.back()`, wait: 800 },
/* and the cap, on an artist with 140 records: the grid on the artist
page is a slice with the way down under it, the page itself opens on
the tab that link was showing, and sixty rows extend to all of them */
{ do: `__ss.push('artist', { name: 'Ember Parade', seed: { id: 416, name: 'Ember Parade', picture: null, album_count: 140, fans: 91000 } })`, name: "disco-many-artist", wait: 1800 },
{ do: `__click('.more-link.dg-all', 'Show all')`, name: "disco-many", wait: 1500 },
{ do: `__click('.dg-more', null)`, name: "disco-many-all", wait: 1400 },
{ do: `history.back()`, wait: 800 },
{ do: `history.back()`, wait: 900 },
/* the artist's photo: the page's ⋯ → a picture from this phone becomes
the hero, the page is asked again, then the catalog's own come back */
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "artist-more", wait: 700 },
@@ -1171,6 +1205,26 @@ const MOCK_EXPECT = {
"cat-artist": (i) => i.screen.includes("has-hero") && i.title.startsWith("Daft Punk") && i.tabs >= 4 && i.pop >= 5 && i.socs === 2,
"cat-artist-follow": (i) => i.fb.includes("Following"),
"cat-artist-tab": (i) => i.title.startsWith("Daft Punk") && i.cardImgs === 2 && i.cardPhs === 2,
/* the whole discography: All in front of the six categories the eleven
mock rows file into, the ten families they fold to, a year header per
year, the desktop's own summary line and the order the head shows.
Two of those rows are dated relative to today (dayIso(-9), dayIso(21)),
so a run across a New Year splits their year in two — hence the range
rather than a number. */
"disco-all": (i) => i.dgTabs === 7 && i.dgRows === 10 && i.dgGroups >= 8 && i.dgGroups <= 9
&& /^10 releases/.test(i.dgSum) && /Newest/.test(i.dgOrder)
&& !i.a11yAnon && !i.a11yBad && !i.a11yGhost && !i.a11yState,
"disco-albums": (i) => i.dgRows === 4 && i.dgGroups === 4,
/* Discovery and its Deluxe — the one edition_group in the mock, and the
thing a flat list of releases loses */
"disco-editions": (i) => i.dgEds === 2 && i.dgRows === 10,
"disco-sheet": (i) => i.half.includes("up") && /By year/.test(i.halfText),
/* the rule this page has to get right: a name order cannot be grouped by
year, so the grouping falls away with it */
"disco-az": (i) => i.dgFirst === "Alive 1997" && i.dgGroups === 0 && /A–Z/.test(i.dgOrder),
"disco-many-artist": (i) => i.title.startsWith("Ember Parade") && i.disco === 12 && /Show all 140/.test(i.dgAll),
"disco-many": (i) => i.dgRows === 60 && /\+ 80 more/.test(i.dgMore),
"disco-many-all": (i) => i.dgRows === 140 && !i.dgMore,
"cat-artist-related": (i) => i.title.startsWith("Justice") && i.screen.includes("has-hero") && i.heroPix && i.pop >= 4 && i.popCounts >= 4,
"artist-more": (i) => i.half.includes("up") && /Choose hero photo/.test(i.halfText) && !/Remove custom hero/.test(i.halfText),
"artist-hero-set": (i) => !i.half.includes("up") && /3840×2160/.test(i.notice),
@@ -1536,6 +1590,20 @@ const PHONE_STEPS = [
same no-op as the button. Nothing is typed and nothing is picked. */
{ do: `__ss.openSetup('preview')`, name: "setup-preview", wait: 1300 },
{ do: `document.querySelector('.scrim').click()`, name: "setup-closed", wait: 900 },
/* the whole discography on the real core, on the same artist the walk
already opened above — so the catalog store is warm and the page has
its releases the moment it is pushed. It reads and it sorts; nothing
is downloaded, played, joined, sent, signed in to or written. */
{ do: `document.querySelectorAll('.nav button')[2].click()`, wait: 600 },
{ do: `document.querySelectorAll('.segtabs button')[1].click()`, wait: 600 },
{ do: `__click('.screen button.row', null)`, wait: 500 },
{ wait: 2500 },
{ do: `__click('.sec .all', 'All')`, name: "dg-page", wait: 1600 },
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "dg-sheet", wait: 1000 },
{ do: `__click('.halfsheet .chip', 'A–Z')`, name: "dg-az", wait: 1000 },
{ do: `document.querySelector('.scrim').click()`, wait: 700 },
{ do: `history.back()`, wait: 800 },
{ do: `history.back()`, wait: 900 },
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "end", wait: 800 },
];
const PHONE_EXPECT = {
@@ -1661,6 +1729,14 @@ const PHONE_EXPECT = {
"export-door": (i) => i.half.includes("up") && /M3U8/.test(i.halfText) && /Backup \(JSON\)/.test(i.halfText),
"export-picker": (i) => !i.half.includes("up") && !i.toast,
"export-dismissed": (i) => i.title.startsWith("Walk test") && !i.half.includes("up") && !i.toast && !i.notice,
/* the real core answers faster than the mock does, and it answers with
Kyle's own artists, so none of this asserts a count, a title or a
state with a window shorter than the still: only that the page is
there, listing something, in the order the sheet was just told to
use, and that nothing went wrong saying so */
"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,
end: (i) => /^(Good |Still)/.test(i.title),
};