feat: the admin settings cards read as menus: each a titled group of rows with one short line, status first, failures in words (M503 #5607)
release / govulncheck (push) Successful in 16s
release / web (push) Failing after 1m27s
release / go (push) Successful in 1m44s
release / android (push) Successful in 5m13s
release / integration (push) Successful in 5m18s
release / Build signed APK (releases and dev) (push) Successful in 5m26s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped

Indexers and clients, searching, missing files, Lidarr's neighbours, AcoustID, fingerprinting, loudness, outgoing email's schedule, public address and proxies move from paragraph cards to SettingsGroup and SettingRow. Switches are role="switch". Also fixes the two type errors run 9023 found: the Lidarr test result is narrowed before its lists are read, and the tag-source save mock carries version_bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 11:25:46 -04:00
co-authored by Claude Opus 5.5
parent 7e1cfb8d08
commit 7f913e3e70
16 changed files with 449 additions and 916 deletions
+4 -16
View File
@@ -43,7 +43,7 @@ afterEach(() => vi.clearAllMocks());
async function renderCard(o: AcoustIDOverview = base) {
vi.mocked(getAcoustID).mockResolvedValue(o);
render(AcoustIDCard);
await screen.findByRole("checkbox", { name: /look up untagged tracks/i });
await screen.findByRole("switch", { name: /look up untagged tracks/i });
}
const saveButton = () => screen.getByRole("button", { name: /^save$/i });
@@ -52,22 +52,10 @@ const lookUpNow = () => screen.getByRole("button", { name: /look up now/i });
describe("AcoustIDCard", () => {
test("shows where the library’s recording ids came from", async () => {
await renderCard();
const cov = screen.getByTestId("acoustid-coverage");
const cov = screen.getByTestId("acoustid-coverage").textContent?.replace(/\s+/g, " ");
// 1000 - 700 - 150 have no id.
for (const [label, n] of [
["From tags", "700"],
["Looked up", "150"],
["None", "150"],
["Waiting to be looked up", "55"],
["No match", "80"],
["Ambiguous", "12"],
["Failed", "3"],
]) {
const row = [...cov.querySelectorAll("dt")].find(
(dt) => dt.textContent?.trim() === label,
);
expect(row?.nextElementSibling?.textContent?.trim(), label).toBe(n);
}
expect(cov).toMatch(/Recording ids 700 from tags · 150 looked up · 150 none/);
expect(cov).toMatch(/Without one 55 waiting · 80 no match · 12 ambiguous · 3 failed/);
});
test("never shows the stored key, and sends one only when typed", async () => {