test(feed): cover the filter-bar gaps deep-review flagged

- test_feed_views: extend _seed_tagged so a channel carries 2+ tags in a
  non-topic category, and add language-AND / other-AND equivalence cases — the
  per-category any/all generalization was only exercised for topic.
- tagModes.test: coerceTagModes migration (legacy global tagMode -> topic),
  per-category preservation, invalid-value coercion, defaults.
- urlState.dom.test: per-category tag-mode URL round-trip + legacy ?tagmode=and
  migration into the topic slot.
- Combobox.dom.test: the any/all match footer (hidden below 2 selections, reports
  a mode change at 2+).
- chipOverflow.test: the all/some/count fit boundaries and the +N reservation.
This commit is contained in:
2026-08-22 06:33:48 +02:00
parent e90603d4db
commit 7692253636
5 changed files with 193 additions and 2 deletions
+93
View File
@@ -0,0 +1,93 @@
import { describe, it, expect } from "vitest";
import { decideChipLayout, type ChipFitInput } from "./chipOverflow";
// Build measurer-style right edges for `n` chips of a fixed width, starting after a label, with a gap.
function rights(labelRight: number, widths: number[], gap: number): number[] {
const out: number[] = [];
let cursor = labelRight;
for (const w of widths) {
cursor += gap + w;
out.push(cursor);
}
return out;
}
const base = (over: Partial<ChipFitInput> = {}): ChipFitInput => {
const gap = 6;
const labelRight = 60;
const widths = [50, 50, 50]; // 3 chips
return {
n: 3,
labelRight,
chipRights: rights(labelRight, widths, gap),
plusW: 40,
clearW: 6 + 60,
avail: 1000,
gap,
narrowPx: 420,
...over,
};
};
describe("decideChipLayout", () => {
it("shows all chips when the whole set plus Clear all fits", () => {
expect(decideChipLayout(base({ avail: 1000 }))).toEqual({ mode: "all", visible: 3 });
});
it("empty chip set is a no-op 'all' with 0 visible", () => {
expect(decideChipLayout(base({ n: 0, chipRights: [] }))).toEqual({ mode: "all", visible: 0 });
});
it("collapses to a single count pill on a narrow lane when overflowing", () => {
// last chip right edge is ~228; avail below NARROW and below that -> count.
expect(decideChipLayout(base({ avail: 200, narrowPx: 420 }))).toEqual({
mode: "count",
visible: 0,
});
});
it("fits as many chips as leave room for the +N pill on a wide-but-tight lane", () => {
// chip right edges: 116, 172, 228; clearW 66. all-fit needs 228+66=294 <= avail; keep avail below
// that (so not 'all') but >= narrowPx (so not 'count'). Loop reserves gap(6)+plus(40)=46 per chip:
// chip2 228+46=274 > 250 -> dropped; chip1 218 <= 250 -> visible 2.
expect(decideChipLayout(base({ avail: 250, narrowPx: 200 }))).toEqual({
mode: "some",
visible: 2,
});
});
it("reserves the +N pill width so a later chip is dropped when it wouldn't fit beside it", () => {
// Two chips of width 100 after a label, gap 6, plus pill 40. Right edges: 166, 272. avail 300,
// narrowPx 100 (so 300 >= narrow -> 'some'): chip0 166+46=212 <= 300 ok; chip1 272+46=318 > 300
// -> dropped. visible 1.
const input = base({
n: 2,
chipRights: rights(60, [100, 100], 6),
plusW: 40,
clearW: 6 + 60,
avail: 300,
gap: 6,
narrowPx: 100,
});
expect(decideChipLayout(input)).toEqual({ mode: "some", visible: 1 });
});
it("falls back to the count pill when not even one chip fits beside the +N pill", () => {
// Wide-ish lane (>= narrow) but a single very wide chip: label 60, chip width 500 -> right 566.
// avail 430 >= narrow(420); all-fit 566+clearW > 430; loop: 566+6+40 > 430 -> k=0; labelRight
// 60+6+40=106 <= 430 so NOT count by that guard... use narrow-avail instead: keep avail 430 but
// make label+gap+plus exceed it to force count.
const input = base({
n: 1,
labelRight: 400,
chipRights: [966],
plusW: 40,
clearW: 0,
avail: 430,
gap: 6,
narrowPx: 420,
});
// label 400 + gap 6 + plus 40 = 446 > 430 and k===0 -> count.
expect(decideChipLayout(input)).toEqual({ mode: "count", visible: 0 });
});
});