@asimojs/lml
Version:
LML - List Markup Language
961 lines (886 loc) • 34 kB
text/typescript
import { beforeEach, describe, expect, it } from 'vitest'
import { LML } from '../types';
import { scan, updateLML } from '../core';
import { print } from './utils';
describe('LML update', () => {
let ex1: LML, ex1bis: LML, ex2: LML, ex3: LML, ex4: LML;
beforeEach(() => {
ex1 = ["#div", "Hello", ["#span.firstName!FN", "Bart"], ["#span.lastName!LN", "Simpson"]];
ex1bis = ["#div", "Hello", ["#span.firstName", { "key": "FN" }, "Bart"], ["#span.lastName!LN", "Simpson"]];
ex2 = ["#div!ROOT", "Hello World"];
ex3 = ["*mycpt!CPT", { "foo": "bar", "title": ["#span!NAME", "Bart Simpson"] }];
ex4 = ["*mycpt!CPT", { "footer": { "sections": ["First", ["#span", "Second"]] } }, "Hello"];
});
describe('scan', () => {
let keys: string[] = [];
function p(nodeKey: string, node: LML): boolean {
keys.push(nodeKey);
return true;
}
it('should find all keyed nodes', async () => {
scan("abc", p);
expect(keys.length).toBe(0);
scan(123, p);
expect(keys.length).toBe(0);
scan(true, p);
expect(keys.length).toBe(0);
scan({ foo: "bar", bar: 123, baz: { x: 123 } }, p);
expect(keys.length).toBe(0);
scan(["*MyCpt.abc", { "title": "Data" }, " Some ", ["#span.em", "content... "]], p);
expect(keys.length).toBe(0);
scan(["*MyCpt.abc", { "title": "Data" }, " Some ", ["#span.em!AA", "content... ", ["#span!BB", "... "]]], p);
expect(keys).toMatchObject([
"AA",
"BB",
]);
keys = [];
scan([], p);
expect(keys.length).toBe(0);
scan(["a", 123, ["#span.em", "content... "], ["*MyCpt.abc", { "title": ["#span.em!AA", "content... "] }, ["#span!BB", "... "]]], p);
expect(keys).toMatchObject([
"AA",
"BB",
]);
});
});
describe('create/replace with node ref', () => {
describe('node content', () => {
it('should support insertBefore - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertBefore",
node: "FN",
content: ["#span.title!TITLE", "Mr"]
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
' <span class="title">',
' Mr',
' </span>',
' <span class="firstName">',
' Bart',
' </span>',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support insertBefore - node in array', async () => {
const r = updateLML(ex1bis, [{
action: "insertBefore",
node: "FN",
content: ["#span.title", { "key": "TITLE" }, "Mr"]
}]);
expect(print(r)).toMatchObject([
'<div>',
' Hello',
' <span class="title" key="TITLE">',
' Mr',
' </span>',
' <span class="firstName" key="FN">',
' Bart',
' </span>',
' <span class="lastName">',
' Simpson',
' </span>',
'</div>',
]);
});
it('should support insertAfter - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertAfter",
node: "FN",
content: " / "
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
' <span class="firstName">',
' Bart',
' </span>',
' / ',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support replace - node in array', async () => {
const r = updateLML(ex1, [{
action: "replace",
node: "FN",
content: "Lisa"
}]);
expect(r === ex1);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
' Lisa',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support insertBefore - root', async () => {
const r = updateLML(ex2, [{
action: "insertBefore",
node: "ROOT",
content: ["#span", "AAA"]
}]);
expect(print(r)).toMatchObject([
"<span>",
" AAA",
"</span>",
"<div>",
" Hello World",
"</div>",
]);
});
it('should support insertAfter - root', async () => {
const r = updateLML(ex2, [{
action: "insertAfter",
node: "ROOT",
content: ["#span", "ZZZ"]
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello World",
"</div>",
"<span>",
" ZZZ",
"</span>",
]);
});
it('should support replace - root', async () => {
const r = updateLML(ex2, [{
action: "replace",
node: "ROOT",
content: ["#span", "HELLO"]
}]);
expect(print(r)).toMatchObject([
"<span>",
" HELLO",
"</span>",
]);
});
it('should support insertBefore - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertBefore",
node: "NAME",
content: ["#span.title!TITLE", "Mr"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span.title!TITLE","Mr"],["#span!NAME","Bart Simpson"]]/>'
]);
});
it('should support insertAfter - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "NAME",
content: "!!!"
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span!NAME","Bart Simpson"],"!!!"]/>'
]);
});
it('should support replace - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "NAME",
content: "Marge"
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title="Marge"/>'
]);
});
it('should support insertBefore - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "insertBefore",
node: "CPT",
path: "title",
content: ["#span.title!TITLE", "Mr"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span.title!TITLE","Mr"],["#span!NAME","Bart Simpson"]]/>'
]);
});
it('should support insertAfter - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "CPT",
path: "title",
content: "!!!"
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span!NAME","Bart Simpson"],"!!!"]/>'
]);
});
it('should support replace - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "CPT",
path: "title",
content: "Marge"
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title="Marge"/>'
]);
});
});
describe('fragment content', () => {
it('should support insertBefore - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertBefore",
node: "FN",
content: ["AAA", ["#span.title!TITLE", "Mr"]]
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
" AAA",
' <span class="title">',
' Mr',
' </span>',
' <span class="firstName">',
' Bart',
' </span>',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support insertAfter - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertAfter",
node: "FN",
content: ["XXX", "YYY", "ZZZ"]
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
' <span class="firstName">',
' Bart',
' </span>',
' XXX',
' YYY',
' ZZZ',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support replace - node in array', async () => {
const r = updateLML(ex1, [{
action: "replace",
node: "FN",
content: ["Lisa,", "Bart"]
}]);
expect(r === ex1);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
' Lisa,',
' Bart',
' <span class="lastName">',
" Simpson",
" </span>",
"</div>",
]);
});
it('should support insertBefore - root', async () => {
const r = updateLML(ex2, [{
action: "insertBefore",
node: "ROOT",
content: ["000", ["#span", "AAA"]]
}]);
expect(print(r)).toMatchObject([
"000",
"<span>",
" AAA",
"</span>",
"<div>",
" Hello World",
"</div>",
]);
});
it('should support insertAfter - root', async () => {
const r = updateLML(ex2, [{
action: "insertAfter",
node: "ROOT",
content: ["AAA", ["#span", "ZZZ"]]
}]);
expect(print(r)).toMatchObject([
"<div>",
" Hello World",
"</div>",
"AAA",
"<span>",
" ZZZ",
"</span>",
]);
});
it('should support replace - root', async () => {
const r = updateLML(ex2, [{
action: "replace",
node: "ROOT",
content: ["AAA", ["#span", "HELLO"]]
}]);
expect(print(r)).toMatchObject([
"AAA",
"<span>",
" HELLO",
"</span>",
]);
});
it('should support insertBefore - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertBefore",
node: "NAME",
content: ["AAA", ["#span.title!TITLE", "Mr"]]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=["AAA",["#span.title!TITLE","Mr"],["#span!NAME","Bart Simpson"]]/>'
]);
});
it('should support insertAfter - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "NAME",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span!NAME","Bart Simpson"],"AAA","BBB"]/>'
]);
});
it('should support replace - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "NAME",
content: ["Marge", "Simpson"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=["Marge","Simpson"]/>'
]);
});
it('should support insertBefore - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "insertBefore",
node: "CPT",
path: "title",
content: ["AAA", ["#span.title!TITLE", "Mr"]]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=["AAA",["#span.title!TITLE","Mr"],["#span!NAME","Bart Simpson"]]/>'
]);
});
it('should support insertAfter - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "CPT",
path: "title",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=[["#span!NAME","Bart Simpson"],"AAA","BBB"]/>'
]);
});
it('should support replace - node as root argument w/ path', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "CPT",
path: "title",
content: ["Marge", "Simpson"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() foo="bar" title=["Marge","Simpson"]/>'
]);
});
});
});
describe('update with node list', () => {
describe('node content', () => {
it('should append content', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "footer/sections",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"],"NEW-NODE"]}>',
" Hello",
"</mycpt>",
]);
});
it('should prepend content', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "footer/sections",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["NEW-NODE","First",["#span","Second"]]}>',
" Hello",
"</mycpt>",
]);
});
it('should prepend replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "footer/sections",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":"NEW-NODE"}>',
" Hello",
"</mycpt>",
]);
});
});
describe('fragment content', () => {
it('should append content', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "footer/sections",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"],"AAA","BBB"]}>',
" Hello",
"</mycpt>",
]);
});
it('should prepend content', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "footer/sections",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["AAA","BBB","First",["#span","Second"]]}>',
" Hello",
"</mycpt>",
]);
});
it('should prepend replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "footer/sections",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["AAA","BBB"]}>',
" Hello",
"</mycpt>",
]);
});
});
});
describe('updates on root node', () => {
describe('fragment root', () => {
it('should support append on empty fragment', async () => {
const r = updateLML([], [{
action: "append",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
"NEW-NODE"
]);
const r2 = updateLML([], [{
action: "append",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB"
]);
});
it('should support append on non-empty fragment', async () => {
const r = updateLML(["Hello"], [{
action: "append",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
"Hello", "NEW-NODE"
]);
const r2 = updateLML(["Hello"], [{
action: "append",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"Hello", "AA", "BB"
]);
});
it('should support prepend on non-empty fragment', async () => {
const r = updateLML(["Hello"], [{
action: "prepend",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
"NEW-NODE", "Hello"
]);
const r2 = updateLML(["Hello"], [{
action: "prepend",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB", "Hello"
]);
});
it('should support replace on non-empty fragment', async () => {
const r = updateLML(["Hello"], [{
action: "replace",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
"NEW-NODE"
]);
const r2 = updateLML(["Hello"], [{
action: "replace",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB"
]);
});
it('should support replace on empty fragment', async () => {
const r = updateLML([], [{
action: "replace",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
"NEW-NODE"
]);
const r2 = updateLML([], [{
action: "replace",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB"
]);
});
it('should support delete on empty fragment', async () => {
const r = updateLML([], [{
action: "delete",
}]);
expect(print(r)).toMatchObject([]);
});
it('should support delete on non-empty fragment', async () => {
const r = updateLML(["Hello"], [{
action: "delete"
}]);
expect(print(r)).toMatchObject([]);
});
});
describe('non-fragment root', () => {
it('should support append', async () => {
const r = updateLML(["#span", "Hello"], [{
action: "append",
content: "NEW-NODE",
path: "foobar" // will be ignored
}]);
expect(print(r)).toMatchObject([
"<span>",
" Hello",
"</span>",
"NEW-NODE"
]);
const r2 = updateLML("Hello", [{
action: "append",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"Hello", "AA", "BB"
]);
});
it('should support prepend', async () => {
const r = updateLML(["#span", "Hello"], [{
action: "prepend",
content: "NEW-NODE",
path: "foobar" // will be ignored
}]);
expect(print(r)).toMatchObject([
"NEW-NODE",
"<span>",
" Hello",
"</span>"
]);
const r2 = updateLML("Hello", [{
action: "prepend",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB", "Hello"
]);
});
it('should support replace', async () => {
const r = updateLML(["#span", "Hello"], [{
action: "replace",
content: "NEW-NODE",
path: "foobar" // will be ignored
}]);
expect(print(r)).toMatchObject([
"NEW-NODE"
]);
const r2 = updateLML("Hello", [{
action: "replace",
content: ["AA", "BB"]
}]);
expect(print(r2)).toMatchObject([
"AA", "BB"
]);
});
it('should support delete', async () => {
const r = updateLML(["#span", "Hello"], [{
action: "delete"
}]);
expect(print(r)).toMatchObject([]);
const r2 = updateLML("Hello", [{
action: "delete"
}]);
expect(print(r2)).toMatchObject([]);
});
});
});
describe('delete', () => {
it('should support delete - node in array', async () => {
const r = updateLML(ex1, [{
action: "delete",
node: "FN"
}]);
expect(r === ex1);
expect(print(r)).toMatchObject([
"<div>",
" Hello",
" <span class=\"lastName\">",
" Simpson",
" </span>",
"</div>",
]);
});
it('should append content', async () => {
const r = updateLML(ex4, [{
action: "delete",
node: "CPT",
path: "footer/sections"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":[]}>',
" Hello",
"</mycpt>",
]);
});
});
describe('children path', () => {
describe('node content', () => {
it('should support insertBefore', async () => {
const r = updateLML(ex4, [{
action: "insertBefore",
node: "CPT",
path: "children",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" <span>",
" NEW-NODE",
" </span>",
" Hello",
"</mycpt>",
]);
});
it('should support prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "children",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" NEW-NODE",
" Hello",
"</mycpt>",
]);
});
it('should support insertAfter', async () => {
const r = updateLML(ex4, [{
action: "insertAfter",
node: "CPT",
path: "children",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" Hello",
" <span>",
" NEW-NODE",
" </span>",
"</mycpt>",
]);
});
it('should support append', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "children",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" Hello",
" NEW-NODE",
"</mycpt>",
]);
});
it('should support replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "children",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" <span>",
" NEW-NODE",
" </span>",
"</mycpt>",
]);
});
it('should support delete', async () => {
const r = updateLML(ex4, [{
action: "delete",
node: "CPT",
path: "children"
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}/>',
]);
});
});
describe('fragment content', () => {
it('should support insertBefore', async () => {
const r = updateLML(ex4, [{
action: "insertBefore",
node: "CPT",
path: "children",
content: ["XYZ", ["#span", "NEW-NODE"]]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" XYZ",
" <span>",
" NEW-NODE",
" </span>",
" Hello",
"</mycpt>",
]);
});
it('should support prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "children",
content: ["AA", "BB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" AA",
" BB",
" Hello",
"</mycpt>",
]);
});
it('should support insertAfter', async () => {
const r = updateLML(ex4, [{
action: "insertAfter",
node: "CPT",
path: "children",
content: [["#span", "NEW-NODE"], "BB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" Hello",
" <span>",
" NEW-NODE",
" </span>",
" BB",
"</mycpt>",
]);
});
it('should support append', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "children",
content: ["AA", "BB"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" Hello",
" AA",
" BB",
"</mycpt>",
]);
});
it('should support replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "children",
content: ["AA", ["#span", "NEW-NODE"], "CC"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" AA",
" <span>",
" NEW-NODE",
" </span>",
" CC",
"</mycpt>",
]);
});
});
describe('default for append and prepend', () => {
it('should work for append', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" Hello",
" <span>",
" NEW-NODE",
" </span>",
"</mycpt>",
]);
});
it('should work for prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'<mycpt() footer={"sections":["First",["#span","Second"]]}>',
" <span>",
" NEW-NODE",
" </span>",
" Hello",
"</mycpt>",
]);
});
});
});
});