fix(KB-186): add ID-based tiebreaker to sort comparators for stable ordering
- Add numeric ID tiebreaker to TaskStore.listTasks() when createdAt timestamps match - Add same tiebreaker to InlineCreateCard and TaskDetailModal dependency dropdown sorts - Add store-sort integration test verifying ascending ID order with identical timestamps - Add InlineCreateCard tests for identical-timestamp dropdown ordering and search filtering - Add TaskDetailModal test for identical-timestamp dependency dropdown ordering
This commit is contained in:
@@ -133,6 +133,34 @@ describe("InlineCreateCard dependency dropdown sort order", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("InlineCreateCard dependency dropdown sort with identical timestamps", () => {
|
||||
const sameTimeTasks: Task[] = [
|
||||
{ id: "KB-001", title: "First", description: "First task", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
{ id: "KB-002", title: "Second", description: "Second task", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
{ id: "KB-003", title: "Third", description: "Third task", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
];
|
||||
|
||||
it("renders tasks with identical createdAt sorted newest-ID-first (descending numeric ID)", () => {
|
||||
renderCard(sameTimeTasks);
|
||||
fireEvent.click(screen.getByText(/Deps/));
|
||||
const items = document.querySelectorAll(".dep-dropdown-item");
|
||||
expect(items).toHaveLength(3);
|
||||
const ids = Array.from(items).map((el) => el.querySelector(".dep-dropdown-id")?.textContent);
|
||||
expect(ids).toEqual(["KB-003", "KB-002", "KB-001"]);
|
||||
});
|
||||
|
||||
it("preserves newest-ID-first order when search filter is applied with identical timestamps", () => {
|
||||
renderCard(sameTimeTasks);
|
||||
fireEvent.click(screen.getByText(/Deps/));
|
||||
const input = document.querySelector(".dep-dropdown-search") as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: "KB-00" } });
|
||||
const items = document.querySelectorAll(".dep-dropdown-item");
|
||||
expect(items).toHaveLength(3);
|
||||
const ids = Array.from(items).map((el) => el.querySelector(".dep-dropdown-id")?.textContent);
|
||||
expect(ids).toEqual(["KB-003", "KB-002", "KB-001"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("InlineCreateCard dependency dropdown search", () => {
|
||||
const testTasks: Task[] = [
|
||||
{ id: "KB-001", title: "Fix login", description: "Login page broken", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
|
||||
@@ -540,6 +540,34 @@ describe("TaskDetailModal", () => {
|
||||
expect(ids).toEqual(["KB-003", "KB-002", "KB-001"]);
|
||||
});
|
||||
|
||||
it("renders tasks with identical createdAt sorted newest-ID-first in dependency dropdown", () => {
|
||||
const allTasks: Task[] = [
|
||||
{ id: "KB-001", description: "First", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
{ id: "KB-002", description: "Second", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
{ id: "KB-003", description: "Third", column: "todo" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
{ id: "KB-099", description: "Self", column: "in-progress" as Column, dependencies: [], steps: [], currentStep: 0, log: [], createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
|
||||
];
|
||||
|
||||
render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ dependencies: [] })}
|
||||
tasks={allTasks}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("Add Dependency"));
|
||||
const items = document.querySelectorAll(".dep-dropdown-item");
|
||||
expect(items).toHaveLength(3);
|
||||
|
||||
const ids = Array.from(items).map((el) => el.querySelector(".dep-dropdown-id")?.textContent);
|
||||
expect(ids).toEqual(["KB-003", "KB-002", "KB-001"]);
|
||||
});
|
||||
|
||||
describe("tab toggle", () => {
|
||||
it("defaults to the Definition tab", () => {
|
||||
const { container } = render(
|
||||
|
||||
Reference in New Issue
Block a user