5105ac224c
* feat: add anchored range selection to multi-select reducer * feat: cmd+click and shift+click multi-select on kanban board * feat: add cmd/shift multi-select to sidebar task list * test: e2e coverage for cmd/shift multi-select * fix: harden multi-select selection lifecycle from review feedback * test: scope multi-select e2e helpers to active containers * fix: add sidebar multi-select hook tests and tighten bulk-action lifecycle * fix: prune hidden sidebar selections and order bulk moves by view * test: cover visible-order bulk move and anchor realignment * fix: harden sidebar bulk-move scope and keyboard selection parity * fix: order kanban bulk moves by board position * test: cover multi-select helpers and fix stale-id sort fallback * fix: order kanban card context-menu moves by board position * feat: sidebar multi-select shows only bulk-valid actions with bulk pin and delete * fix: harden multi-select bulk ordering, selection clearing, and a11y * fix: gate kanban card move-to-step for mixed-workflow selections * fix: unpin fully pinned sidebar selections * refactor: keep sidebar task components under limits * test: restore sidebar selection console spy safely
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { compareTasksByCreatedDesc, sortIdsByCreatedDesc } from "./task-order";
|
|
|
|
describe("compareTasksByCreatedDesc", () => {
|
|
it("sorts newer created tasks first", () => {
|
|
const tasks = [
|
|
{ id: "old", createdAt: "2026-05-01T10:00:00Z" },
|
|
{ id: "new", createdAt: "2026-05-02T10:00:00Z" },
|
|
];
|
|
|
|
expect([...tasks].sort(compareTasksByCreatedDesc).map((task) => task.id)).toEqual([
|
|
"new",
|
|
"old",
|
|
]);
|
|
});
|
|
|
|
it("sorts tasks without createdAt after dated tasks", () => {
|
|
const tasks = [
|
|
{ id: "missing" },
|
|
{ id: "old", createdAt: "2026-05-01T10:00:00Z" },
|
|
{ id: "new", createdAt: "2026-05-02T10:00:00Z" },
|
|
];
|
|
|
|
expect([...tasks].sort(compareTasksByCreatedDesc).map((task) => task.id)).toEqual([
|
|
"new",
|
|
"old",
|
|
"missing",
|
|
]);
|
|
});
|
|
|
|
it("sorts by actual timestamp when ISO offsets differ", () => {
|
|
const tasks = [
|
|
{ id: "later-offset", createdAt: "2026-05-02T09:30:00-04:00" },
|
|
{ id: "earlier-zulu", createdAt: "2026-05-02T13:00:00Z" },
|
|
];
|
|
|
|
expect([...tasks].sort(compareTasksByCreatedDesc).map((task) => task.id)).toEqual([
|
|
"later-offset",
|
|
"earlier-zulu",
|
|
]);
|
|
});
|
|
|
|
it("keeps equal missing createdAt tasks stable", () => {
|
|
const tasks: Array<{ id: string; createdAt?: string }> = [{ id: "first" }, { id: "second" }];
|
|
|
|
expect([...tasks].sort(compareTasksByCreatedDesc).map((task) => task.id)).toEqual([
|
|
"first",
|
|
"second",
|
|
]);
|
|
});
|
|
|
|
it("returns 0 when both tasks are missing createdAt", () => {
|
|
expect(compareTasksByCreatedDesc({}, {})).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("sortIdsByCreatedDesc", () => {
|
|
// d newest … a oldest → board order is d, c, b, a.
|
|
const taskById = new Map<string, { createdAt?: string }>([
|
|
["a", { createdAt: "2026-01-01T00:00:00Z" }],
|
|
["b", { createdAt: "2026-01-02T00:00:00Z" }],
|
|
["c", { createdAt: "2026-01-03T00:00:00Z" }],
|
|
["d", { createdAt: "2026-01-04T00:00:00Z" }],
|
|
]);
|
|
|
|
it("reorders a backward range selection into board (created-desc) order", () => {
|
|
// Anchor on the oldest then shift up leaves the Set as [a, c, b] (insertion).
|
|
expect(sortIdsByCreatedDesc(["a", "c", "b"], taskById)).toEqual(["c", "b", "a"]);
|
|
});
|
|
|
|
it("sorts ids without a known task last (transitive fallback)", () => {
|
|
expect(sortIdsByCreatedDesc(["zzz", "a"], taskById)).toEqual(["a", "zzz"]);
|
|
});
|
|
|
|
it("stays transitive when only some ids are missing", () => {
|
|
// c (newest present) < a (older present) < missing → deterministic order.
|
|
expect(sortIdsByCreatedDesc(["a", "missing", "c"], taskById)).toEqual(["c", "a", "missing"]);
|
|
});
|
|
});
|