loom/test/interpolate.test.mjs

408 lines
7.5 KiB
JavaScript

import {
getUnifiedContext,
interpolateArray,
interpolateNode,
interpolateObject,
interpolateString,
interpolateTree,
interpolateValue,
} from '../src/interpolate.mjs'
/**
* The minimal information on an AST node. 'data' and 'position' are
* optional.
*
* interface Node {
* type : string
* data : Data?
* position : Position?
* }
*/
describe("importing interpolation", () => {
it("should import", () => {
expect(interpolateTree).toBeDefined();
expect(interpolateString).toBeDefined();
expect(interpolateNode).toBeDefined();
});
});
describe("interpolateNode()", () => {
it("should not interpolate 'type', 'data', or 'position'", () => {
const node = {
type: '${type}',
value: 'Hello, ${name}!',
data: {
context: {
name: "World",
},
},
position: {},
};
expect(interpolateNode(node)).toEqual({
type: '${type}',
value: 'Hello, World!',
data: {
context: {
name: "World",
},
},
position: {},
});
});
it("should interpolate strings deeply in non-children attributes", () => {
const node = {
type: '${type}',
str: 'Hello, ${name}!',
arr: [
'Another ${name}',
[
'Yet another ${name}',
],
],
obj: {
a: 'my ${name}',
b: {
c: 'some ${name}',
},
},
data: {
context: {
name: "World",
},
},
position: {},
};
expect(interpolateNode(node)).toEqual({
type: '${type}',
str: 'Hello, World!',
arr: [
'Another World',
[
'Yet another World',
],
],
obj: {
a: 'my World',
b: {
c: 'some World',
},
},
data: {
context: {
name: "World",
},
},
position: {},
});
});
it("should handle nodes without data", () => {
const node = {
type: '${type}',
str: 'Hello, ${name}!',
arr: [
'Another ${name}',
[
'Yet another ${game}',
],
],
obj: {
a: 'my ${game}',
b: {
c: 'some ${name}',
},
},
data: {
context: {
name: "World",
},
},
position: {},
};
expect(interpolateNode(node)).toEqual({
type: '${type}',
str: 'Hello, World!',
arr: [
'Another World',
[
'Yet another ${game}',
],
],
obj: {
a: 'my ${game}',
b: {
c: 'some World',
},
},
data: {
context: {
name: "World",
},
},
position: {},
});
});
it("should handle context masking", () => {
const node = {
type: '${type}',
str: 'Hello, ${name}!',
arr: [
'Another ${name}',
[
'Yet another ${game}',
],
],
obj: {
a: 'my ${game}',
b: {
c: 'some ${name}',
},
},
data: {
context: {
name: "World",
},
},
position: {},
};
expect(interpolateNode(node, {name: "Charles", game: "croquet"})).toEqual({
type: '${type}',
str: 'Hello, Charles!',
arr: [
'Another Charles',
[
'Yet another croquet',
],
],
obj: {
a: 'my croquet',
b: {
c: 'some Charles',
},
},
data: {
context: {
name: "World",
},
},
position: {},
});
});
});
describe("interpolateTree()", () => {
it("should handle implicit context masking", () => {
const node = {
type: '${type}',
data: {
context: {
name: "World",
},
},
position: {},
children: [
{
type: 'text',
value: 'It is the new ${name}!',
},
{
type: 'element',
tagName: 'div',
data: {
context: {
name: "Jasmine",
},
},
children: [
{
type: 'some-${name}',
value: 'Hello, ${name}!',
},
{
type: 'some-${name}',
value: 'Hello, ${name}!',
data: {
context: {
name: "Charles",
},
},
},
],
},
{
type: 'text',
value: 'Another ${game}',
},
],
};
let interpolated = interpolateTree(node);
expect(interpolated.children[0].value).toBe("It is the new World!");
expect(interpolated.children[1].children[0].value).toBe("Hello, Jasmine!");
expect(interpolated.children[1].children[1].value).toBe("Hello, Charles!");
expect(interpolated.children[2].value).toBe("Another ${game}");
});
it("should handle explicit context masking", () => {
const node = {
type: '${type}',
data: {
context: {
name: "World",
},
},
position: {},
children: [
{
type: 'element',
tagName: 'div',
children: [
{
type: 'text',
value: 'Hello, ${name}!',
},
],
},
{
type: 'text',
value: 'Another ${game}',
},
],
};
let interpolated = interpolateTree(node, {name: "Charles", game: "croquet"});
expect(interpolated.children[0].children[0].value).toBe("Hello, Charles!");
expect(interpolated.children[1].value).toBe("Another croquet");
});
});
/**
* interpolateString() is where the rubber hits the road. Everything
* that gets interpolated eventually does it through interpolateString().
*/
describe("interpolateString()", () => {
it("should handle arbitrary javascript", () => {
const str = "sum: ${count + 5}";
const context = { count: 9 };
expect(interpolateString(str, context)).toEqual("sum: 14");
});
it("should handle javascript globals", () => {
const str = "total: ${Math.pow(5,3)}";
expect(interpolateString(str, {})).toEqual("total: 125");
});
it("should handle multiple interpolations", () => {
const str = "${count} squared: ${Math.pow(count, 2)}";
const context = { count: 9 };
expect(interpolateString(str, context)).toEqual("9 squared: 81");
});
it("should handle function interpolations", () => {
const str = "${count} squared: ${(() => (count * count))()}";
const context = { count: 9 };
expect(interpolateString(str, context)).toEqual("9 squared: 81");
});
})
describe("interpolateArray()", () => {
it("should interpolate arrays without a context", () => {
expect(interpolateArray([1,2,3])).toEqual([1,2,3]);
})
});
describe("interpolateObject()", () => {
it("should interpolate objects without a context", () => {
expect(interpolateObject({a:1,b:2,c:3})).toEqual({a:1,b:2,c:3});
})
});
describe("interpolateValue()", () => {
it("should pass through without a context", () => {
const str = "sum: ${count + 5}";
expect(interpolateValue(str)).toEqual("sum: ${count + 5}");
});
it("should interpolate strings", () => {
const str = "sum: ${count + 5}";
const context = { count: 9 };
expect(interpolateValue(str, context)).toEqual("sum: 14");
});
it("should interpolate arrays", () => {
const arr = ["${count}"];
const context = { count: 9 };
expect(interpolateValue(arr, context)).toEqual(["9"]);
});
it("should interpolate objects", () => {
const obj = {
a: "${count}",
};
const context = { count: 9 };
expect(interpolateValue(obj, context)).toEqual({a:"9"});
});
it("should pass through other values", () => {
const val = 12;
const context = { count: 9 };
expect(interpolateValue(val, context)).toEqual(12);
});
});
describe("getUnifiedContext()", () => {
it("should provide a context object without a lineage", () => {
expect(getUnifiedContext()).toEqual({});
});
it("should return the unified context", () => {
const ancestry = [
{
type: 'element',
tagName: 'div',
data: {
context: {
name: "Jasmine",
game: "croquet",
},
},
},
{
type: 'element',
tagName: 'div',
data: {
context: {
name: "Charles",
same: "things",
},
},
},
{
type: 'text',
value: "pigs",
},
];
expect(getUnifiedContext(ancestry)).toEqual({
name: "Charles",
game: "croquet",
same: "things",
});
});
});