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([
"
",
" Hello",
' ',
' Mr',
' ',
' ',
' Bart',
' ',
' ',
" Simpson",
" ",
"
",
]);
});
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([
'',
' Hello',
' ',
' Mr',
' ',
' ',
' Bart',
' ',
' ',
' Simpson',
' ',
'
',
]);
});
it('should support insertAfter - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertAfter",
node: "FN",
content: " / "
}]);
expect(print(r)).toMatchObject([
"",
" Hello",
' ',
' Bart',
' ',
' / ',
' ',
" Simpson",
" ",
"
",
]);
});
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([
"",
" Hello",
' Lisa',
' ',
" Simpson",
" ",
"
",
]);
});
it('should support insertBefore - root', async () => {
const r = updateLML(ex2, [{
action: "insertBefore",
node: "ROOT",
content: ["#span", "AAA"]
}]);
expect(print(r)).toMatchObject([
"",
" AAA",
"",
"",
" Hello World",
"
",
]);
});
it('should support insertAfter - root', async () => {
const r = updateLML(ex2, [{
action: "insertAfter",
node: "ROOT",
content: ["#span", "ZZZ"]
}]);
expect(print(r)).toMatchObject([
"",
" Hello World",
"
",
"",
" ZZZ",
"",
]);
});
it('should support replace - root', async () => {
const r = updateLML(ex2, [{
action: "replace",
node: "ROOT",
content: ["#span", "HELLO"]
}]);
expect(print(r)).toMatchObject([
"",
" HELLO",
"",
]);
});
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([
''
]);
});
it('should support insertAfter - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "NAME",
content: "!!!"
}]);
expect(print(r)).toMatchObject([
''
]);
});
it('should support replace - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "NAME",
content: "Marge"
}]);
expect(print(r)).toMatchObject([
''
]);
});
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([
''
]);
});
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([
''
]);
});
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([
''
]);
});
});
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([
"",
" Hello",
" AAA",
' ',
' Mr',
' ',
' ',
' Bart',
' ',
' ',
" Simpson",
" ",
"
",
]);
});
it('should support insertAfter - node in array', async () => {
const r = updateLML(ex1, [{
action: "insertAfter",
node: "FN",
content: ["XXX", "YYY", "ZZZ"]
}]);
expect(print(r)).toMatchObject([
"",
" Hello",
' ',
' Bart',
' ',
' XXX',
' YYY',
' ZZZ',
' ',
" Simpson",
" ",
"
",
]);
});
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([
"",
" Hello",
' Lisa,',
' Bart',
' ',
" Simpson",
" ",
"
",
]);
});
it('should support insertBefore - root', async () => {
const r = updateLML(ex2, [{
action: "insertBefore",
node: "ROOT",
content: ["000", ["#span", "AAA"]]
}]);
expect(print(r)).toMatchObject([
"000",
"",
" AAA",
"",
"",
" Hello World",
"
",
]);
});
it('should support insertAfter - root', async () => {
const r = updateLML(ex2, [{
action: "insertAfter",
node: "ROOT",
content: ["AAA", ["#span", "ZZZ"]]
}]);
expect(print(r)).toMatchObject([
"",
" Hello World",
"
",
"AAA",
"",
" ZZZ",
"",
]);
});
it('should support replace - root', async () => {
const r = updateLML(ex2, [{
action: "replace",
node: "ROOT",
content: ["AAA", ["#span", "HELLO"]]
}]);
expect(print(r)).toMatchObject([
"AAA",
"",
" HELLO",
"",
]);
});
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([
''
]);
});
it('should support insertAfter - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "insertAfter",
node: "NAME",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
''
]);
});
it('should support replace - node as root argument', async () => {
const r = updateLML(ex3, [{
action: "replace",
node: "NAME",
content: ["Marge", "Simpson"]
}]);
expect(print(r)).toMatchObject([
''
]);
});
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([
''
]);
});
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([
''
]);
});
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([
''
]);
});
});
});
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([
'',
" Hello",
"",
]);
});
it('should prepend content', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "footer/sections",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
"",
]);
});
it('should prepend replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "footer/sections",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
"",
]);
});
});
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([
'',
" Hello",
"",
]);
});
it('should prepend content', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "footer/sections",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
"",
]);
});
it('should prepend replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "footer/sections",
content: ["AAA", "BBB"]
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
"",
]);
});
});
});
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([
"",
" Hello",
"",
"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",
"",
" Hello",
""
]);
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([
"",
" Hello",
" ",
" Simpson",
" ",
"
",
]);
});
it('should append content', async () => {
const r = updateLML(ex4, [{
action: "delete",
node: "CPT",
path: "footer/sections"
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
"",
]);
});
});
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([
'',
" ",
" NEW-NODE",
" ",
" Hello",
"",
]);
});
it('should support prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "children",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'',
" NEW-NODE",
" Hello",
"",
]);
});
it('should support insertAfter', async () => {
const r = updateLML(ex4, [{
action: "insertAfter",
node: "CPT",
path: "children",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
" ",
" NEW-NODE",
" ",
"",
]);
});
it('should support append', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "children",
content: "NEW-NODE"
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
" NEW-NODE",
"",
]);
});
it('should support replace', async () => {
const r = updateLML(ex4, [{
action: "replace",
node: "CPT",
path: "children",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'',
" ",
" NEW-NODE",
" ",
"",
]);
});
it('should support delete', async () => {
const r = updateLML(ex4, [{
action: "delete",
node: "CPT",
path: "children"
}]);
expect(print(r)).toMatchObject([
'',
]);
});
});
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([
'',
" XYZ",
" ",
" NEW-NODE",
" ",
" Hello",
"",
]);
});
it('should support prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
path: "children",
content: ["AA", "BB"]
}]);
expect(print(r)).toMatchObject([
'',
" AA",
" BB",
" Hello",
"",
]);
});
it('should support insertAfter', async () => {
const r = updateLML(ex4, [{
action: "insertAfter",
node: "CPT",
path: "children",
content: [["#span", "NEW-NODE"], "BB"]
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
" ",
" NEW-NODE",
" ",
" BB",
"",
]);
});
it('should support append', async () => {
const r = updateLML(ex4, [{
action: "append",
node: "CPT",
path: "children",
content: ["AA", "BB"]
}]);
expect(print(r)).toMatchObject([
'',
" Hello",
" AA",
" BB",
"",
]);
});
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([
'',
" AA",
" ",
" NEW-NODE",
" ",
" CC",
"",
]);
});
});
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([
'',
" Hello",
" ",
" NEW-NODE",
" ",
"",
]);
});
it('should work for prepend', async () => {
const r = updateLML(ex4, [{
action: "prepend",
node: "CPT",
content: ["#span", "NEW-NODE"]
}]);
expect(print(r)).toMatchObject([
'',
" ",
" NEW-NODE",
" ",
" Hello",
"",
]);
});
});
});
});