Files
racket-wiki/test/cmap-json-import-export.test.mjs
T

104 lines
3.4 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { CmapJsonExporter } from "../static/cmap/model/json-exporter.js";
import { CmapJsonImporter } from "../static/cmap/model/json-importer.js";
import {
CmapRepository,
StoredConceptMap
} from "../static/cmap/model/cmap-repository.js";
import { CmapModel } from "../static/cmap/model/concept-map.js";
const conceptId = "11111111-1111-4111-8111-111111111111";
const rootMap = {
slug: "root",
title: "Root",
document: {
schemaVersion: 2,
metadata: { explanationPageSlug: "root-page" },
concepts: [{ id: conceptId, label: "Root concept" }],
items: [{ id: 1, conceptId, kind: "concept", x: 10, y: 20 }],
connectors: []
}
};
const page = {
slug: "root-page",
title: "Root page",
markdown: "![Image](/uploads/root-page/image.png)",
tags: []
};
const storedRootMap = new StoredConceptMap(rootMap, CmapModel.fromDocument(rootMap.document));
const cmapRepository = { load: async () => { throw new Error("no linked map expected"); } };
test("CmapJsonExporter builds a complete bundle including attachments", async () => {
const exporter = new CmapJsonExporter(
cmapRepository,
async () => page,
async () => ({
ok: true,
headers: { get: () => "image/png" },
arrayBuffer: async () => Uint8Array.from([1, 2, 3]).buffer
}),
"test"
);
const bundle = await exporter.export(storedRootMap);
assert.equal(bundle.generator, "test");
assert.equal(bundle.pages[0].attachments[0].contentBase64, "AQID");
});
test("CmapJsonImporter reports conflicts and imports pages, attachments and maps", async () => {
const calls = [];
const api = async (path, options = {}) => {
calls.push({ path, options });
if (path.endsWith("/upload")) return { url: "/uploads/root-page/imported-image.png" };
if (path === "/api/pages" && options.method === "POST") return { currentVersion: 1 };
if (path === "/api/cmaps" && options.method === "POST") {
const body = JSON.parse(options.body);
return {
slug: body.slug,
title: body.title,
currentVersion: 1,
document: body.document
};
}
return {};
};
const importer = new CmapJsonImporter(
new CmapRepository(api), api, (_key, fallback) => fallback);
const exporter = new CmapJsonExporter(
cmapRepository,
async () => page,
async () => ({
ok: true,
headers: { get: () => "image/png" },
arrayBuffer: async () => Uint8Array.from([1, 2, 3]).buffer
})
);
const bundle = await exporter.export(storedRootMap);
const file = { size: 100, text: async () => JSON.stringify(bundle) };
const parsed = await importer.read(file);
assert.equal(parsed.rootCmapSlug, "root");
assert.deepEqual(importer.conflicts(parsed, [{ slug: "root-page" }], []), {
pages: [parsed.pages[0]],
conceptMaps: []
});
const result = await importer.import(parsed, {
pages: [], conceptMaps: [], summary: "Imported"
});
assert.equal(result.pagesCreated, 1);
assert.equal(result.mapsCreated, 1);
assert.equal(result.attachmentsImported, 1);
assert.deepEqual(calls.map((call) => `${call.options.method} ${call.path}`), [
"POST /api/pages",
"POST /api/pages/root-page/upload",
"PUT /api/pages/root-page",
"POST /api/cmaps"
]);
const pageUpdate = JSON.parse(calls[2].options.body);
assert.match(pageUpdate.markdown, /imported-image\.png/);
});