Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
94 changes: 93 additions & 1 deletion src/engine/MacroChoiceEngine.entry.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { MacroChoiceEngine } from "./MacroChoiceEngine";
import type QuickAdd from "../main";
import type { IChoiceExecutor } from "../IChoiceExecutor";
Expand Down Expand Up @@ -211,6 +211,98 @@ describe("MacroChoiceEngine user script entry handling", () => {
});
});

describe("MacroChoiceEngine user script variable propagation", () => {
const app = {} as App;
const plugin = {} as unknown as QuickAdd;

let choiceExecutor: IChoiceExecutor;
let variables: Map<string, unknown>;
let macroChoice: IMacroChoice;
let logs: Array<number | undefined>;
let getApiSpy: ReturnType<typeof vi.spyOn>;

beforeEach(() => {
vi.clearAllMocks();
mockGetUserScript.mockReset();
mockInitializeUserScriptSettings.mockReset();
mockSuggest.mockReset();

getApiSpy = vi
.spyOn(QuickAddApi, "GetApi")
.mockReturnValue({} as unknown as ReturnType<typeof QuickAddApi.GetApi>);

logs = [];
variables = new Map<string, unknown>();
choiceExecutor = {
execute: vi.fn(),
variables,
};

const makeScript = (index: number): IUserScript => ({
id: `script-${index}`,
name: `Script ${index}`,
type: CommandType.UserScript,
path: `script-${index}.js`,
settings: {},
});

macroChoice = {
id: "macro-sequence",
name: "Macro sequence",
type: "Macro",
command: false,
runOnStartup: false,
macro: {
id: "macro-sequence",
name: "Macro sequence",
commands: [
makeScript(1),
makeScript(2),
makeScript(3),
makeScript(4),
],
} as IMacro,
};

mockGetUserScript.mockImplementation((command: IUserScript) => {
const nextValueByPath: Record<string, number> = {
"script-1.js": 1,
"script-2.js": 2,
"script-3.js": 3,
"script-4.js": 3,
};

const nextValue = nextValueByPath[command.path ?? ""] ?? 0;

return Promise.resolve(async (params: { variables: Record<string, unknown> }) => {
logs.push(params.variables.target as number | undefined);
if (nextValue !== 0) {
params.variables.target = nextValue;
}
});
});
});

afterEach(() => {
getApiSpy.mockRestore();
});

it("propagates variable mutations across sequential user scripts", async () => {
const engine = new MacroChoiceEngine(
app,
plugin,
macroChoice,
choiceExecutor,
variables,
);

await engine.run();

expect(logs).toEqual([undefined, 1, 2, 3]);
expect(choiceExecutor.variables.get("target")).toBe(3);
});
});

describe("MacroChoiceEngine choice command cancellation", () => {
const app = {} as App;
let plugin: QuickAdd & { getChoiceById: ReturnType<typeof vi.fn> };
Expand Down
18 changes: 11 additions & 7 deletions src/engine/MacroChoiceEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export class MacroChoiceEngine extends QuickAddChoiceEngine {
protected async executeCommands(commands: ICommand[]) {
try {
for (const command of commands) {
// Always start with the freshest shared variables so each command sees
// updates from the previous one.
this.pullExecutorVariablesIntoParams();

if (command?.type === CommandType.Obsidian)
this.executeObsidianCommand(command as IObsidianCommand);
if (command?.type === CommandType.UserScript)
Expand All @@ -152,13 +156,7 @@ export class MacroChoiceEngine extends QuickAddChoiceEngine {
await this.executeConditional(command as IConditionalCommand);
}

this.pullExecutorVariablesIntoParams();
Object.keys(this.params.variables).forEach((key) => {
this.choiceExecutor.variables.set(
key,
this.params.variables[key]
);
});
this.pushParamsVariablesIntoExecutor();
}
} catch (error) {
if (
Expand Down Expand Up @@ -495,6 +493,12 @@ export class MacroChoiceEngine extends QuickAddChoiceEngine {
});
}

private pushParamsVariablesIntoExecutor() {
Object.keys(this.params.variables).forEach((key) => {
this.choiceExecutor.variables.set(key, this.params.variables[key]);
Comment thread
chhoumann marked this conversation as resolved.
Outdated
});
}

private async evaluateScriptCondition(
condition: ScriptCondition
): Promise<boolean> {
Expand Down