feat(journal): var stuff
This commit is contained in:
@@ -32,7 +32,7 @@ jest.mock("github-slugger", () => {
|
||||
|
||||
import { parseDeclareCsv } from "./declare-parser";
|
||||
import { parseBlockAttrs, resolveBlockAs } from "./block-scanner";
|
||||
import { evaluateExpression } from "../../components/journal/variable-expression";
|
||||
import { evaluateExpression, exprValueToString } from "../../components/journal/variable-expression";
|
||||
import {
|
||||
initReactivity,
|
||||
computeCascade,
|
||||
@@ -281,14 +281,12 @@ describe("resolveBlockAs", () => {
|
||||
describe("evaluateExpression", () => {
|
||||
test("evaluates simple arithmetic", () => {
|
||||
const result = evaluateExpression("2 + 3 * 4", { lookup: () => undefined });
|
||||
expect(result.value).toBe(14);
|
||||
expect(result.value).toEqual({ kind: "number", value: 14 });
|
||||
});
|
||||
|
||||
test("evaluates with parentheses", () => {
|
||||
const result = evaluateExpression("(2 + 3) * 4", {
|
||||
lookup: () => undefined,
|
||||
});
|
||||
expect(result.value).toBe(20);
|
||||
const result = evaluateExpression("(2 + 3) * 4", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "number", value: 20 });
|
||||
});
|
||||
|
||||
test("evaluates variable references", () => {
|
||||
@@ -299,54 +297,105 @@ describe("evaluateExpression", () => {
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
expect(result.value).toBe(80); // 12*5 + 20
|
||||
expect(result.value).toEqual({ kind: "number", value: 80 }); // 12*5 + 20
|
||||
});
|
||||
|
||||
test("returns 0 for undefined variables", () => {
|
||||
const result = evaluateExpression("$unknown + 5", {
|
||||
lookup: () => undefined,
|
||||
});
|
||||
expect(result.value).toBe(5);
|
||||
expect(result.value).toEqual({ kind: "number", value: 5 });
|
||||
});
|
||||
|
||||
test("throws on tag values in arithmetic", () => {
|
||||
test("throws on type mismatch (tagmap + number)", () => {
|
||||
expect(() =>
|
||||
evaluateExpression("$class + 5", {
|
||||
lookup: (name) => (name === "class" ? "#warrior" : undefined),
|
||||
}),
|
||||
).toThrow("$class is a tag");
|
||||
).toThrow("Type mismatch");
|
||||
});
|
||||
|
||||
test("resolves tagmap variable without arithmetic", () => {
|
||||
const result = evaluateExpression("$class", {
|
||||
lookup: (name) => (name === "class" ? "#warrior:1" : undefined),
|
||||
});
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 1 } });
|
||||
});
|
||||
|
||||
test("evaluates tagmap literals", () => {
|
||||
const result = evaluateExpression("#warrior:1", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 1 } });
|
||||
});
|
||||
|
||||
test("evaluates bare tag as tagmap", () => {
|
||||
const result = evaluateExpression("#warrior", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 1 } });
|
||||
});
|
||||
|
||||
test("evaluates multi-entry tagmap literal", () => {
|
||||
const result = evaluateExpression("#warrior:1;#druid:2", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 1, "#druid": 2 } });
|
||||
});
|
||||
|
||||
test("merges tagmaps with +", () => {
|
||||
const result = evaluateExpression("#warrior:1 + #druid:2", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 1, "#druid": 2 } });
|
||||
});
|
||||
|
||||
test("adds counts for same tag with +", () => {
|
||||
const result = evaluateExpression("#warrior:1 + #warrior:2", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 3 } });
|
||||
});
|
||||
|
||||
test("subtracts tagmaps with -", () => {
|
||||
const result = evaluateExpression("#warrior:3;#druid:2 - #druid:2", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "tagmap", value: { "#warrior": 3 } });
|
||||
});
|
||||
|
||||
test("throws on tagmap * tagmap", () => {
|
||||
expect(() =>
|
||||
evaluateExpression("#warrior:1 * #druid:2", { lookup: () => undefined }),
|
||||
).toThrow("Type mismatch");
|
||||
});
|
||||
|
||||
test("throws on tagmap / tagmap", () => {
|
||||
expect(() =>
|
||||
evaluateExpression("#warrior:1 / #druid:2", { lookup: () => undefined }),
|
||||
).toThrow("Type mismatch");
|
||||
});
|
||||
|
||||
test("evaluates floor function", () => {
|
||||
const result = evaluateExpression("floor(3.7)", {
|
||||
lookup: () => undefined,
|
||||
});
|
||||
expect(result.value).toBe(3);
|
||||
const result = evaluateExpression("floor(3.7)", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "number", value: 3 });
|
||||
});
|
||||
|
||||
test("evaluates ceil function", () => {
|
||||
const result = evaluateExpression("ceil(3.2)", { lookup: () => undefined });
|
||||
expect(result.value).toBe(4);
|
||||
expect(result.value).toEqual({ kind: "number", value: 4 });
|
||||
});
|
||||
|
||||
test("evaluates round function", () => {
|
||||
const result = evaluateExpression("round(3.5)", {
|
||||
lookup: () => undefined,
|
||||
});
|
||||
expect(result.value).toBe(4);
|
||||
const result = evaluateExpression("round(3.5)", { lookup: () => undefined });
|
||||
expect(result.value).toEqual({ kind: "number", value: 4 });
|
||||
});
|
||||
|
||||
test("evaluates unary minus", () => {
|
||||
const result = evaluateExpression("-5 + 10", { lookup: () => undefined });
|
||||
expect(result.value).toBe(5);
|
||||
expect(result.value).toEqual({ kind: "number", value: 5 });
|
||||
});
|
||||
|
||||
test("throws on negating tagmap", () => {
|
||||
expect(() =>
|
||||
evaluateExpression("-#warrior", { lookup: () => undefined }),
|
||||
).toThrow("Type mismatch");
|
||||
});
|
||||
|
||||
test("evaluates dice notation", () => {
|
||||
const result = evaluateExpression("3d6 + 5", { lookup: () => undefined });
|
||||
expect(typeof result.value).toBe("number");
|
||||
expect(result.value.kind).toBe("number");
|
||||
// 3d6 is between 3 and 18, +5 gives 8-23
|
||||
expect(result.value).toBeGreaterThanOrEqual(8);
|
||||
expect(result.value).toBeLessThanOrEqual(23);
|
||||
expect(result.value.value).toBeGreaterThanOrEqual(8);
|
||||
expect(result.value.value).toBeLessThanOrEqual(23);
|
||||
});
|
||||
|
||||
test("throws on division by zero", () => {
|
||||
@@ -369,7 +418,7 @@ describe("evaluateExpression", () => {
|
||||
|
||||
test("handles decimal numbers", () => {
|
||||
const result = evaluateExpression("3.5 + 2.5", { lookup: () => undefined });
|
||||
expect(result.value).toBeCloseTo(6);
|
||||
expect(result.value).toEqual({ kind: "number", value: 6 });
|
||||
});
|
||||
|
||||
test("nested function calls", () => {
|
||||
@@ -377,7 +426,7 @@ describe("evaluateExpression", () => {
|
||||
lookup: () => undefined,
|
||||
});
|
||||
// ceil(3.2) = 4, floor(4) = 4
|
||||
expect(result.value).toBe(4);
|
||||
expect(result.value).toEqual({ kind: "number", value: 4 });
|
||||
});
|
||||
|
||||
test("complex expression with variables and functions", () => {
|
||||
@@ -388,7 +437,20 @@ describe("evaluateExpression", () => {
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
expect(result.value).toBe(10); // floor(7.5) + 3 = 7 + 3
|
||||
expect(result.value).toEqual({ kind: "number", value: 10 }); // floor(7.5) + 3 = 7 + 3
|
||||
});
|
||||
|
||||
test("exprValueToString serializes number", () => {
|
||||
expect(exprValueToString({ kind: "number", value: 42 })).toBe("42");
|
||||
});
|
||||
|
||||
test("exprValueToString serializes tagmap", () => {
|
||||
expect(exprValueToString({ kind: "tagmap", value: { "#warrior": 1, "#druid": 2 } }))
|
||||
.toBe("#warrior:1;#druid:2");
|
||||
});
|
||||
|
||||
test("exprValueToString returns 0 for empty tagmap", () => {
|
||||
expect(exprValueToString({ kind: "tagmap", value: {} })).toBe("0");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user