408 lines
7.5 KiB
JavaScript
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",
|
|
});
|
|
});
|
|
});
|