Files
siftlode/frontend/src/lib/subscriptionCache.test.ts
T
peter 4d1cf66d96 fix: tag counts, chip focus, upload naming, share links and unsized clips hold after round 2
- A tag counts the followed channels the Subscriptions tab shows: hidden ones are left out, and a
  subscribe, unsubscribe, hide or tag toggle refreshes the count. The Tag manager's row and its
  hover list count the same channels as the chip; /api/tags sends both counts under their names.
- A chip's count keeps the chip's colour after a dot (a dimmed count fell under 4.5:1). A pick in
  the open "+N" fold closes it; a chip that leaves the list hands the focus to the one in its place.
  The fold is a dialog of toggle buttons, not an empty menu.
- Upload: a folder name left by a press is made once the press is over, if the dialog is still
  open, instead of guessing a dismissal from Modal's markup. While the folder is made, its place
  holds the focus and a live region says so.
- Share: an expired link has no Copy and a warn badge; the link made on open goes into the cached
  list, so a reopen makes no second one; the New link form survives the link made on open.
- Watch: a repeated Enter sends one unlock while one is on its way. See all buttons are named with
  their rail.
- An unsized clip of a rotated file gets the upright size its crop cut.
2026-11-06 02:20:52 +01:00

55 lines
2.5 KiB
TypeScript

import { readdirSync, readFileSync } from "node:fs";
import { QueryClient } from "@tanstack/react-query";
import { describe, expect, it, vi } from "vitest";
import { qk } from "./queryKeys";
import { invalidateSubscription } from "./subscriptionCache";
// A subscription change touches the same reads wherever it is made, and each site kept
// its own list — the channel menu's unsubscribe left the feed showing the channel's videos, the
// channel page's left the status counts stale. One set, one function, every site on it.
describe("a subscription change", () => {
it("refreshes the channel, the lists, the counts and the feed", () => {
const qc = new QueryClient();
const spy = vi.spyOn(qc, "invalidateQueries");
invalidateSubscription(qc, "UC1");
expect(spy.mock.calls.map(([f]) => f?.queryKey)).toEqual([
qk.channel("UC1"),
qk.channels(),
qk.myStatus(),
qk.feed(),
qk.feedSummary(),
qk.discoveredChannels(),
qk.watchedChannels(),
qk.tags(),
]);
});
// A tag counts the followed channels it is on, not hidden: a hide changes the number as an
// unsubscribe does, and both places that hide a channel must refresh it.
it("a hide refreshes the tags wherever it is made", () => {
const root = new URL("../", import.meta.url);
const sites = readdirSync(root, { recursive: true, encoding: "utf8" })
.map((f) => f.split("\\").join("/"))
.filter((f) => f.endsWith(".tsx") && !f.includes(".test.") && !f.startsWith("design-lab/"))
.map((f) => [f, readFileSync(new URL(f, root), "utf8")] as const)
.filter(([, src]) => /api\.updateChannel\([^)]*hidden/.test(src));
expect(sites.length).toBeGreaterThanOrEqual(2);
for (const [f, src] of sites) expect(src, f).toContain("queryKey: qk.tags()");
});
it("is refreshed through that one function wherever it is made", () => {
const root = new URL("../", import.meta.url);
const sites = readdirSync(root, { recursive: true, encoding: "utf8" })
.map((f) => f.split("\\").join("/"))
.filter((f) => f.endsWith(".tsx") && !f.includes(".test.") && !f.startsWith("design-lab/"))
.map((f) => [f, readFileSync(new URL(f, root), "utf8")] as const)
.filter(([, src]) => /api\.(un)?subscribeChannel\(/.test(src));
expect(sites.length).toBeGreaterThanOrEqual(3);
for (const [f, src] of sites) {
const calls = src.match(/api\.(un)?subscribeChannel\(/g)!.length;
expect(src.match(/invalidateSubscription\(/g)?.length ?? 0, f).toBe(calls);
}
});
});