Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/utils/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,50 @@ export const COMMANDS: {
return ref;
},
},
{
text: "MOVEBLOCK",
help: "Moves a block under a new parent\n\n1. Block ref or uid to move (optional, defaults to current block)\n\n2. Parent ref, uid, or page title (optional, defaults to current parent)\n\n3. Order index or 'last' (optional, defaults to last)",
handler: async (blockArg = "", parentArg = "", orderArg = "last") => {
const sourceArg = smartBlocksContext.variables[blockArg] || blockArg;
const sourceUid =
extractRef(sourceArg) || sourceArg || smartBlocksContext.currentUid || "";
if (!sourceUid) {
return "--> MOVEBLOCK failed: source block was not found <--";
}

const rawParentArg = smartBlocksContext.variables[parentArg] || parentArg;
const targetParentUid =
getUidFromText(rawParentArg) ||
extractRef(rawParentArg) ||
rawParentArg ||
getParentUidByBlockUid(sourceUid);
if (!targetParentUid) {
return "--> MOVEBLOCK failed: target parent was not found <--";
}
if (sourceUid === targetParentUid) {
return "--> MOVEBLOCK failed: source block cannot be its own parent <--";
}

const order =
/^last$/i.test(orderArg) || !orderArg
? getBasicTreeByParentUid(targetParentUid).length
: Math.max(0, Number(orderArg) || 0);

if (typeof (window.roamAlphaAPI as any).moveBlock !== "function") {
return "--> MOVEBLOCK failed: moveBlock API not available <--";
}
try {
await (window.roamAlphaAPI as any).moveBlock({
location: { "parent-uid": targetParentUid, order },
block: { uid: sourceUid },
});
return `((${sourceUid}))`;
} catch (e) {
const message = e instanceof Error ? e.message : String(e);
return `--> MOVEBLOCK failed: ${message} <--`;
}
},
},
{
text: "INDENT",
help: "Indents the current block if indentation can be done at current block. ",
Expand Down
131 changes: 99 additions & 32 deletions tests/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,112 @@ import { test, expect } from "@playwright/test";
import mockRoamEnvironment from "roamjs-components/testing/mockRoamEnvironment";
import createBlock from "roamjs-components/writes/createBlock";
import createPage from "roamjs-components/writes/createPage";
import { sbBomb } from "../src/core";
import nanoid from "nanoid";
import { JSDOM } from "jsdom";

// @ts-ignore
global.window = global.window || {};
// @ts-ignore
global.window.roamAlphaAPI = global.window.roamAlphaAPI || {
graph: { name: "test" },
};
// @ts-ignore
global.localStorage = global.localStorage || {
getItem: () => "",
setItem: () => {},
removeItem: () => {},
clear: () => {},
key: () => null,
length: 0,
};

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { COMMANDS, resetContext } = require("../src/utils/core");

const moveBlockCommand = COMMANDS.find((c) => c.text === "MOVEBLOCK")?.handler;

test.beforeAll(() => {
mockRoamEnvironment();
const { roamAlphaAPI } = window;
const jsdom = new JSDOM();
// @ts-ignore
global.window = jsdom.window;
global.document = jsdom.window.document;
window.roamAlphaAPI = roamAlphaAPI;
});

test("Nested CURSOR command", async () => {
const parentUid = await createPage({ title: `page-${nanoid()}` });
const srcUid = await createBlock({
node: {
text: "#SmartBlock Nested",
children: [
{
text: "<%SMARTBLOCK:Cursor%>",
},
],
},
parentUid,
test.beforeEach(() => {
resetContext();
});

test("MOVEBLOCK returns API-not-available message when moveBlock is missing", async () => {
const command = moveBlockCommand;
expect(command).toBeDefined();

const pageUid = await createPage({ title: `page-${nanoid()}` });
const sourceUid = await createBlock({ parentUid: pageUid, node: { text: "s" } });
const targetUid = await createBlock({ parentUid: pageUid, node: { text: "t" } });

const originalMoveBlock = (window.roamAlphaAPI as any).moveBlock;
(window.roamAlphaAPI as any).moveBlock = undefined;

try {
const result = await command!(sourceUid, targetUid);
expect(result).toBe("--> MOVEBLOCK failed: moveBlock API not available <--");
} finally {
(window.roamAlphaAPI as any).moveBlock = originalMoveBlock;
}
});

test("MOVEBLOCK reports source/target validation failures", async () => {
const command = moveBlockCommand;
expect(command).toBeDefined();

const noSourceResult = await command!();
expect(noSourceResult).toBe(
"--> MOVEBLOCK failed: source block was not found <--"
);

const pageUid = await createPage({ title: `page-${nanoid()}` });
const sourceUid = await createBlock({
parentUid: pageUid,
node: { text: "source" },
});
const selfParentResult = await command!(sourceUid, sourceUid);
expect(selfParentResult).toBe(
"--> MOVEBLOCK failed: source block cannot be its own parent <--"
);
});

test("MOVEBLOCK returns moved block ref and calls moveBlock with expected args", async () => {
const command = moveBlockCommand;
expect(command).toBeDefined();

const pageUid = await createPage({ title: `page-${nanoid()}` });
const sourceParentUid = await createBlock({
parentUid: pageUid,
node: { text: "source-parent" },
});
const sourceUid = await createBlock({
parentUid: sourceParentUid,
node: { text: "source" },
});
const targetParentUid = await createBlock({
parentUid: pageUid,
node: { text: "target-parent" },
});
await createBlock({
node: {
text: "#SmartBlock Cursor",
children: [
{
text: "Place the cursor here: <%CURSOR%>",
},
],
},
parentUid,
parentUid: targetParentUid,
node: { text: "existing-child" },
});
const targetUid = await createBlock({ node: { text: "" }, parentUid });
const result = targetUid;// TODO: await sbBomb({ srcUid, target: { uid: targetUid } });
expect(result).toEqual(targetUid);

const originalMoveBlock = (window.roamAlphaAPI as any).moveBlock;
let moveBlockArgs: unknown;
(window.roamAlphaAPI as any).moveBlock = async (args: unknown) => {
moveBlockArgs = args;
};

try {
const result = await command!(sourceUid, targetParentUid, "last");
expect(result).toBe(`((${sourceUid}))`);
expect(moveBlockArgs).toEqual({
location: { "parent-uid": targetParentUid, order: 1 },
block: { uid: sourceUid },
});
} finally {
(window.roamAlphaAPI as any).moveBlock = originalMoveBlock;
}
});
Loading