From a393584a2a78bc1e0a9956279bdba2c56865e093 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 28 Sep 2025 14:40:30 +0300 Subject: [PATCH] test(server/share): implement basic shaca mocking with content --- .../server/src/share/content_renderer.spec.ts | 20 ++-- apps/server/src/share/content_renderer.ts | 4 +- apps/server/src/test/shaca_mocking.ts | 97 +++++++++++++++++++ 3 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 apps/server/src/test/shaca_mocking.ts diff --git a/apps/server/src/share/content_renderer.spec.ts b/apps/server/src/share/content_renderer.spec.ts index 80c754200..04f60bd71 100644 --- a/apps/server/src/share/content_renderer.spec.ts +++ b/apps/server/src/share/content_renderer.spec.ts @@ -1,12 +1,12 @@ import { describe, it, expect } from "vitest"; -import { renderCode, renderText, type Result } from "./content_renderer.js"; +import { getContent, renderCode, renderText, type Result } from "./content_renderer.js"; import { trimIndentation } from "@triliumnext/commons"; -import SNote from "./shaca/entities/snote.js"; +import { buildShareNote } from "../test/shaca_mocking.js"; describe("content_renderer", () => { describe("renderText", () => { it("parses simple note", () => { - const input = trimIndentation`\ + const content = trimIndentation`\
@@ -15,14 +15,12 @@ describe("content_renderer", () => { Welcome to Trilium Notes!

`; - - const result = { - content: input, - header: "", - isEmpty: false - }; - renderText(result, new SNote([ "root", "Note", "text", "text/plain", "1234", "2025-09-28T00:00Z", false])); - expect(result.content).toMatch(input); + const note = buildShareNote({ + title: "Note", + content + }); + const result = getContent(note); + expect(result.content).toStrictEqual(content); }); }); diff --git a/apps/server/src/share/content_renderer.ts b/apps/server/src/share/content_renderer.ts index c12e7887f..253c43b39 100644 --- a/apps/server/src/share/content_renderer.ts +++ b/apps/server/src/share/content_renderer.ts @@ -16,7 +16,7 @@ export interface Result { isEmpty?: boolean; } -function getContent(note: SNote) { +export function getContent(note: SNote) { if (note.isProtected) { return { header: "", @@ -65,7 +65,7 @@ function renderIndex(result: Result) { result.content += ""; } -function renderText(result: Result, note: SNote) { +export function renderText(result: Result, note: SNote) { const document = new JSDOM(result.content || "").window.document; // Process include notes. diff --git a/apps/server/src/test/shaca_mocking.ts b/apps/server/src/test/shaca_mocking.ts new file mode 100644 index 000000000..e92411500 --- /dev/null +++ b/apps/server/src/test/shaca_mocking.ts @@ -0,0 +1,97 @@ +import utils from "../services/utils.js"; +import SAttribute from "../share/shaca/entities/sattribute.js"; +import SNote from "../share/shaca/entities/snote.js"; +import shaca from "../share/shaca/shaca.js"; + +type AttributeDefinitions = { [key in `#${string}`]: string; }; +type RelationDefinitions = { [key in `~${string}`]: string; }; + +interface NoteDefinition extends AttributeDefinitions, RelationDefinitions { + id?: string | undefined; + title: string; + content?: string | Buffer; +} + +/** + * Creates the given notes with the given title and optionally one or more attributes. + * + * For a label to be created, simply pass on a key prefixed with `#` and any desired value. + * + * The notes and attributes will be injected in the froca. + * + * @param notes + * @returns an array containing the IDs of the created notes. + * @example + * buildShareNotes([ + * { title: "A", "#startDate": "2025-05-05" }, + * { title: "B", "#startDate": "2025-05-07" } + * ]); + */ +export function buildShareNotes(notes: NoteDefinition[]) { + const ids: string[] = []; + + for (const noteDef of notes) { + ids.push(buildShareNote(noteDef).noteId); + } + + return ids; +} + +export function buildShareNote(noteDef: NoteDefinition) { + const blobId = "foo"; + const note = new SNote([ + noteDef.id ?? utils.randomString(12), + noteDef.title, + "text", + "text/html", + blobId, + new Date().toUTCString(), // utcDateModified + false // is protected + ]); + shaca.notes[note.noteId] = note; + + // Handle content + if (noteDef.content) { + note.getContent = () => noteDef.content; + } + + // Handle labels & relations + let position = 0; + for (const [ key, value ] of Object.entries(noteDef)) { + const attributeId = utils.randomString(12); + const name = key.substring(1); + + let attribute: SAttribute | null = null; + if (key.startsWith("#")) { + attribute = new SAttribute([ + attributeId, + note.noteId, + "label", + name, + value, + false, // isInheritable + position // position + ]); + } + + if (key.startsWith("~")) { + attribute = new SAttribute([ + attributeId, + note.noteId, + "relation", + name, + value, + false, // isInheritable + position // position + ]); + } + + if (!attribute) { + continue; + } + + shaca.attributes[attributeId] = attribute; + position++; + } + return note; +}