diff --git a/spec/support/becca_mocking.ts b/spec/support/becca_mocking.ts index 0c65f16b6..495212226 100644 --- a/spec/support/becca_mocking.ts +++ b/spec/support/becca_mocking.ts @@ -4,7 +4,7 @@ import BAttribute from "../../src/becca/entities/battribute.js"; import becca from "../../src/becca/becca.js"; import randtoken from "rand-token"; import type SearchResult from "../../src/services/search/search_result.js"; -import type { NoteType } from "../../src/becca/entities/rows.js"; +import type { NoteRow, NoteType } from "../../src/becca/entities/rows.js"; randtoken.generator({ source: "crypto" }); function findNoteByTitle(searchResults: Array, title: string): BNote | undefined { @@ -59,7 +59,7 @@ function id() { return randtoken.generate(10); } -function note(title: string, extraParams = {}) { +function note(title: string, extraParams: Partial = {}) { const row = Object.assign( { noteId: id(), diff --git a/src/services/tree.spec.ts b/src/services/tree.spec.ts new file mode 100644 index 000000000..57a13cf0e --- /dev/null +++ b/src/services/tree.spec.ts @@ -0,0 +1,72 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import becca_mocking from "../../spec/support/becca_mocking.js"; +import becca from "../becca/becca.js"; +import BBranch from "../becca/entities/bbranch.js"; +import BNote from "../becca/entities/bnote.js"; +import tree from "./tree.js"; +import cls from "./cls.js"; + +describe("Tree", () => { + let rootNote!: any; + + beforeEach(() => { + becca.reset(); + + rootNote = new becca_mocking.NoteBuilder(new BNote({ + noteId: "root", + title: "root", + type: "text" + })) + new BBranch({ + branchId: "none_root", + noteId: "root", + parentNoteId: "none", + notePosition: 10 + }); + + vi.mock("./sql.js", () => { + return { + default: { + transactional: (cb) => { + cb(); + }, + execute: (...args) => { }, + replace: (...args) => { }, + getMap: (...args) => { } + } + } + }); + + vi.mock("./sql_init.js", () => { + return { + dbReady: () => { console.log("Hello world") } + } + }); + }); + + it("custom sort order is idempotent", () => { + rootNote.label("sorted", "order"); + + // Add values which have a defined order. + for (let i=0; i<=5; i++) { + rootNote.child(becca_mocking.note(String(i)).label("order", String(i))); + } + + // Add a few values which have no defined order. + for (let i=6; i<10; i++) { + rootNote.child(becca_mocking.note(String(i))); + } + + const expectedOrder = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; + + // Sort a few times to ensure that the resulting order is the same. + for (let i=0; i<5; i++) { + cls.init(() => { + tree.sortNotesIfNeeded(rootNote.note.noteId); + }); + + const order = rootNote.note.children.map((child) => child.title); + expect(order).toStrictEqual(expectedOrder); + } + }); +}); diff --git a/src/services/tree.ts b/src/services/tree.ts index 64d1279e5..9b695cd69 100644 --- a/src/services/tree.ts +++ b/src/services/tree.ts @@ -145,8 +145,8 @@ function sortNotes(parentNoteId: string, customSortBy: string = "title", reverse return compare(bottomBEl, bottomAEl) * (reverse ? -1 : 1); } - const customAEl = fetchValue(a, customSortBy); - const customBEl = fetchValue(b, customSortBy); + const customAEl = fetchValue(a, customSortBy) ?? fetchValue(a, "title"); + const customBEl = fetchValue(b, customSortBy) ?? fetchValue(b, "title"); if (customAEl !== customBEl) { return compare(customAEl, customBEl);