const { clone, assign } = require("lodash"); const angularParser = require("./angular-parser"); const xmlSpacePreserveTag = { type: "tag", position: "start", value: '', text: true, tag: "w:t", }; const startText = { type: "tag", position: "start", value: "", text: true, tag: "w:t", }; const endText = { type: "tag", value: "", text: true, position: "end", tag: "w:t", }; const startParagraph = { type: "tag", value: "", text: false, position: "start", tag: "w:p", }; const endParagraph = { type: "tag", value: "", text: false, position: "end", tag: "w:p", }; const tableRowStart = { type: "tag", position: "start", text: false, value: "", tag: "w:tr", }; const tableRowEnd = { type: "tag", value: "", text: false, position: "end", tag: "w:tr", }; const delimiters = { start: { type: "delimiter", position: "start" }, end: { type: "delimiter", position: "end" }, }; function content(value) { return { type: "content", value, position: "insidetag" }; } function externalContent(value) { return { type: "content", value, position: "outsidetag" }; } const fixtures = { simple: { it: "should handle {user} with tag", content: "Hi {user}", scope: { user: "Foo", }, result: 'Hi Foo', lexed: [ startText, content("Hi "), delimiters.start, content("user"), delimiters.end, endText, ], parsed: [ startText, content("Hi "), { type: "placeholder", value: "user" }, endText, ], postparsed: [ xmlSpacePreserveTag, content("Hi "), { type: "placeholder", value: "user" }, endText, ], }, dot: { it: "should handle {.} with tag", content: "Hi {.}", scope: "Foo", result: 'Hi Foo', lexed: [ startText, content("Hi "), delimiters.start, content("."), delimiters.end, endText, ], parsed: [ startText, content("Hi "), { type: "placeholder", value: "." }, endText, ], postparsed: [ xmlSpacePreserveTag, content("Hi "), { type: "placeholder", value: "." }, endText, ], }, strangetags: { it: "should xmlparse strange tags", content: "{name} {FOOageBAR}", scope: { name: "Foo", age: 12, }, result: 'Foo 12FOOBAR', parsed: [ startText, { type: "placeholder", value: "name" }, content(" "), { type: "placeholder", value: "age" }, endText, externalContent("FOO"), startText, endText, externalContent("BAR"), startText, endText, ], xmllexed: [ startText, { type: "content", value: "{name} {" }, endText, { type: "content", value: "FOO" }, startText, { type: "content", value: "age" }, endText, { type: "content", value: "BAR" }, startText, { type: "content", value: "}" }, endText, ], lexed: [ startText, delimiters.start, content("name"), delimiters.end, content(" "), delimiters.start, endText, externalContent("FOO"), startText, content("age"), endText, externalContent("BAR"), startText, delimiters.end, endText, ], postparsed: null, }, otherdelimiters: { it: "should work with custom delimiters", content: "Hello [[[name]]", scope: { name: "John Doe", }, result: 'Hello John Doe', delimiters: { start: "[[[", end: "]]", }, lexed: [ startText, content("Hello "), delimiters.start, content("name"), delimiters.end, endText, ], parsed: [ startText, content("Hello "), { type: "placeholder", value: "name" }, endText, ], postparsed: null, }, otherdelimiterssplitted: { it: "should work with custom delimiters splitted", content: 'Hello {name}}, how is it ?', scope: { name: "John Doe", }, result: 'Hello John Doe, how is it ?', delimiters: { start: "{", end: "}}", }, lexed: [ startText, content("Hello "), delimiters.start, content("name"), delimiters.end, endText, { type: "tag", value: '', text: true, position: "start", tag: "w:t", }, content(", how is it ?"), endText, ], parsed: [ startText, content("Hello "), { type: "placeholder", value: "name" }, endText, { type: "tag", value: '', text: true, position: "start", tag: "w:t", }, content(", how is it ?"), endText, ], postparsed: null, }, otherdelimiterssplittedover2tags: { it: "should work with custom delimiters splitted over > 2 tags", content: "Hello {name}}TAG}}}foobar", scope: { name: "John Doe", }, result: 'Hello John DoeTAGfoobar', delimiters: { start: "{", end: "}}}}}", }, lexed: [ startText, content("Hello "), delimiters.start, content("name"), delimiters.end, endText, startText, endText, externalContent("TAG"), startText, endText, startText, content("foobar"), endText, ], parsed: [ startText, content("Hello "), { type: "placeholder", value: "name" }, endText, startText, endText, externalContent("TAG"), startText, endText, startText, content("foobar"), endText, ], postparsed: null, }, looptag: { it: "should work with loops", content: "Hello {#users}{name}, {/users}", scope: { users: [{ name: "John Doe" }, { name: "Jane Doe" }, { name: "Wane Doe" }], }, result: 'Hello John Doe, Jane Doe, Wane Doe, ', lexed: [ startText, content("Hello "), delimiters.start, content("#users"), delimiters.end, delimiters.start, content("name"), delimiters.end, content(", "), delimiters.start, content("/users"), delimiters.end, endText, ], parsed: [ startText, content("Hello "), { type: "placeholder", value: "users", location: "start", module: "loop", inverted: false, expandTo: "auto", }, { type: "placeholder", value: "name" }, content(", "), { type: "placeholder", value: "users", location: "end", module: "loop" }, endText, ], postparsed: [ xmlSpacePreserveTag, content("Hello "), { type: "placeholder", value: "users", module: "loop", inverted: false, sectPrCount: 0, subparsed: [{ type: "placeholder", value: "name" }, content(", ")], }, endText, ], }, paragraphlooptag: { it: "should work with paragraph loops", content: "Hello {#users}User {.}{/users}", scope: { users: ["John Doe", "Jane Doe", "Wane Doe"], }, result: 'Hello User John DoeUser Jane DoeUser Wane Doe', lexed: [ startParagraph, startText, content("Hello "), endText, endParagraph, startParagraph, startText, delimiters.start, content("#users"), delimiters.end, endText, endParagraph, startParagraph, startText, content("User "), delimiters.start, content("."), delimiters.end, endText, endParagraph, startParagraph, startText, delimiters.start, content("/users"), delimiters.end, endText, endParagraph, ], parsed: [ startParagraph, startText, content("Hello "), endText, endParagraph, startParagraph, startText, { type: "placeholder", value: "users", location: "start", module: "loop", inverted: false, expandTo: "auto", }, endText, endParagraph, startParagraph, startText, content("User "), { type: "placeholder", value: "." }, endText, endParagraph, startParagraph, startText, { type: "placeholder", value: "users", location: "end", module: "loop" }, endText, endParagraph, ], postparsed: [ startParagraph, startText, content("Hello "), endText, endParagraph, { type: "placeholder", value: "users", module: "loop", sectPrCount: 0, hasPageBreak: false, hasPageBreakBeginning: false, inverted: false, subparsed: [ startParagraph, xmlSpacePreserveTag, content("User "), { type: "placeholder", value: "." }, endText, endParagraph, ], }, ], options: { paragraphLoop: true, }, }, nestedparagraphlooptag: { it: "should not fail with nested loops if using paragraphLoop", content: "{#users} {#pets}Pet {.}{/pets}{/users}", scope: { users: [ { pets: ["Cat", "Dog"], }, { pets: ["Cat", "Dog"], }, ], }, result: ' Pet CatPet Dog Pet CatPet Dog', lexed: null, parsed: null, postparsed: null, options: { paragraphLoop: true, }, }, spacingloops: { it: "should work with spacing loops", content: "{#condition} hello{/condition}", result: ' hello', scope: { condition: true, }, lexed: [ startText, delimiters.start, content("#condition"), endText, startText, delimiters.end, content(" hello"), delimiters.start, content("/"), endText, startText, content("condition"), delimiters.end, endText, ], parsed: [ startText, { type: "placeholder", value: "condition", location: "start", module: "loop", inverted: false, expandTo: "auto", }, endText, startText, content(" hello"), { type: "placeholder", value: "condition", location: "end", module: "loop", }, endText, startText, endText, ], postparsed: null, }, spacingloops2: { it: "should work with spacing loops 2", content: "{#condition}{text}{/condition}", result: ' hello ', lexed: null, parsed: null, postparsed: null, scope: { condition: [{ text: " hello " }], }, }, spacingloops3: { it: "should work with spacing loops 3", content: "{#condition}{/condition} foo", result: ' foo', lexed: null, parsed: null, postparsed: null, scope: { condition: false, }, }, spacingloops4: { it: "should work with spacing loops 4", content: "{#condition}foo{/condition}", result: "", lexed: null, parsed: null, postparsed: null, scope: { condition: false, }, }, dashlooptag: { it: "should work with dashloops", content: "Hello {-w:p users}{name}, {/users}", scope: { users: [{ name: "John Doe" }, { name: "Jane Doe" }, { name: "Wane Doe" }], }, result: 'Hello John Doe, Hello Jane Doe, Hello Wane Doe, ', lexed: [ startParagraph, startText, content("Hello "), delimiters.start, content("-w:p users"), delimiters.end, delimiters.start, content("name"), delimiters.end, content(", "), delimiters.start, content("/users"), delimiters.end, endText, endParagraph, ], parsed: [ startParagraph, startText, content("Hello "), { type: "placeholder", value: "users", location: "start", module: "loop", inverted: false, expandTo: "w:p", }, { type: "placeholder", value: "name" }, content(", "), { type: "placeholder", value: "users", location: "end", module: "loop" }, endText, endParagraph, ], postparsed: [ { type: "placeholder", value: "users", module: "loop", inverted: false, sectPrCount: 0, subparsed: [ startParagraph, xmlSpacePreserveTag, content("Hello "), { type: "placeholder", value: "name" }, content(", "), endText, endParagraph, ], }, ], }, dashloopnested: { it: "should work with dashloops nested", content: "{-w:tr columns} Hello {-w:p users}{name}, {/users}{/columns}", scope: { columns: [ { users: [ { name: "John Doe" }, { name: "Jane Doe" }, { name: "Wane Doe" }, ], }, ], }, result: ' Hello John Doe, Hello Jane Doe, Hello Wane Doe, ', lexed: [ tableRowStart, startParagraph, startText, delimiters.start, content("-w:tr columns"), delimiters.end, content(" Hello "), delimiters.start, content("-w:p users"), delimiters.end, delimiters.start, content("name"), delimiters.end, content(", "), delimiters.start, content("/users"), delimiters.end, endText, startText, delimiters.start, content("/columns"), delimiters.end, endText, endParagraph, tableRowEnd, ], parsed: [ tableRowStart, startParagraph, startText, { type: "placeholder", value: "columns", location: "start", module: "loop", inverted: false, expandTo: "w:tr", }, content(" Hello "), { type: "placeholder", value: "users", location: "start", module: "loop", inverted: false, expandTo: "w:p", }, { type: "placeholder", value: "name" }, content(", "), { type: "placeholder", value: "users", location: "end", module: "loop" }, endText, startText, { type: "placeholder", value: "columns", location: "end", module: "loop", }, endText, endParagraph, tableRowEnd, ], postparsed: null, }, rawxml: { it: "should work with rawxml", content: "BEFORE{@rawxml}AFTER", scope: { rawxml: 'My customXML', }, result: 'BEFOREMy customXMLAFTER', lexed: [ externalContent("BEFORE"), startParagraph, startText, delimiters.start, content("@rawxml"), delimiters.end, endText, endParagraph, externalContent("AFTER"), ], parsed: [ externalContent("BEFORE"), startParagraph, startText, { type: "placeholder", value: "rawxml", module: "rawxml" }, endText, endParagraph, externalContent("AFTER"), ], postparsed: [ externalContent("BEFORE"), { type: "placeholder", value: "rawxml", module: "rawxml", expanded: [ [startParagraph, startText], [endText, endParagraph], ], }, externalContent("AFTER"), ], }, selfclosing: { it: "should handle selfclose tag", content: "", scope: { user: "Foo", }, result: "", lexed: [ { type: "tag", value: "", text: true, position: "selfclosing", tag: "w:t", }, ], parsed: [ { type: "tag", position: "selfclosing", value: "", text: true, tag: "w:t", }, ], postparsed: [ { type: "tag", position: "selfclosing", value: "", text: true, tag: "w:t", }, ], }, selfclosing_with_placeholderr: { it: "should handle {user} with tag with selfclosing", content: "Hi {user}", scope: { user: "Foo", }, result: 'Hi Foo', lexed: [ { type: "tag", value: "", text: true, position: "selfclosing", tag: "w:t", }, startText, content("Hi "), delimiters.start, content("user"), delimiters.end, endText, ], parsed: [ { type: "tag", position: "selfclosing", value: "", text: true, tag: "w:t", }, startText, content("Hi "), { type: "placeholder", value: "user" }, endText, ], postparsed: [ { type: "tag", position: "selfclosing", value: "", text: true, tag: "w:t", }, xmlSpacePreserveTag, content("Hi "), { type: "placeholder", value: "user" }, endText, ], }, delimiters_change: { it: "should be possible to change the delimiters", content: "Hi {=[[ ]]=}[[user]][[={ }=]] and {user2}", scope: { user: "John", user2: "Jane", }, result: 'Hi John and Jane', lexed: [ startText, content("Hi "), delimiters.start, content("user"), delimiters.end, content(" and "), delimiters.start, content("user2"), delimiters.end, endText, ], parsed: [ startText, content("Hi "), { type: "placeholder", value: "user" }, content(" and "), { type: "placeholder", value: "user2" }, endText, ], postparsed: [ xmlSpacePreserveTag, content("Hi "), { type: "placeholder", value: "user" }, content(" and "), { type: "placeholder", value: "user2" }, endText, ], }, delimiters_change_complex: { it: "should be possible to change the delimiters with complex example", content: "Hi {={{[ ]}}=}{{[user]}}{{[={{ ]=]}} and {{user2]", scope: { user: "John", user2: "Jane", }, result: 'Hi John and Jane', lexed: [ startText, content("Hi "), delimiters.start, content("user"), delimiters.end, content(" and "), delimiters.start, content("user2"), delimiters.end, endText, ], parsed: [ startText, content("Hi "), { type: "placeholder", value: "user" }, content(" and "), { type: "placeholder", value: "user2" }, endText, ], postparsed: [ xmlSpacePreserveTag, content("Hi "), { type: "placeholder", value: "user" }, content(" and "), { type: "placeholder", value: "user2" }, endText, ], }, error_resolve: { it: "should resolve the data correctly", content: "{test}{#test}{label}{/test}{test}", result: 'trueT1true', scope: { label: "T1", test: true, }, resolved: [ { tag: "test", value: true, lIndex: 3, }, { tag: "test", value: true, lIndex: 15, }, { tag: "test", value: [ [ { tag: "label", value: "T1", lIndex: 9, }, ], ], lIndex: 6, }, ], lexed: null, parsed: null, postparsed: null, }, error_resolve_2: { it: "should resolve 2 the data correctly", content: "{^a}{label}{/a}", result: "", scope: { a: true, }, resolved: [ { tag: "a", value: [], lIndex: 3, }, ], lexed: null, parsed: null, postparsed: null, }, error_resolve_3: { it: "should resolve 3 the data correctly", content: "{#frames}{#true}{label}{#false}{label}{/false}{/true}{#false}{label}{/false}{/frames}", result: 'T1', scope: { frames: [ { label: "T1", true: true, }, ], }, resolved: [ { tag: "frames", value: [ [ { tag: "false", value: [], lIndex: 24, }, { tag: "true", value: [ [ { tag: "label", value: "T1", lIndex: 9, }, { tag: "false", value: [], lIndex: 12, }, ], ], lIndex: 6, }, ], ], lIndex: 3, }, ], lexed: null, parsed: null, postparsed: null, }, error_resolve_truthy: { it: "should resolve truthy data correctly", content: "{#loop}L{#cond2}{label}{/cond2}{#cond3}{label}{/cond3}{/loop}", result: 'Linner', scope: { label: "outer", loop: [ { cond2: true, label: "inner", }, ], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, error_resolve_truthy_multi: { it: "should resolve truthy multi data correctly", content: "{#loop}L{#cond2}{label}{/cond2}{#cond3}{label}{/cond3}{/loop}", result: 'LinnerLinnerLinnerLouterouter', scope: { label: "outer", loop: [ { cond2: true, label: "inner", }, { cond2: true, label: "inner", }, { cond3: true, label: "inner", }, { cond2: true, cond3: true, }, ], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, async_loop_issue: { it: "should resolve async loop", content: "{#loop}{#cond1}{label}{/}{#cond2}{label}{/}{/loop}", result: 'innerouterouter', scope: { label: "outer", loop: [ { cond1: true, label: "inner", }, { cond1: true, cond2: true, }, ], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, inversed_loop: { it: "should work well with inversed loop", content: "{#a}{^b}{label}{/}{/}", result: 'hi', scope: { a: [{ b: false, label: "hi" }], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, inversed_loop_nested: { it: "should work well with inversed loop nested", content: "{#a}{^b}{^c}{label}{/}{/}{/}", result: 'hi', scope: { a: [{ b: false, label: "hi" }], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, inversed_loop_nested_resolved: { it: "should work well with inversed loop nested", content: "{#a}{^b}{^c}{label}{/}{/}{/}", result: 'hi', scope: { label: "outer", a: [{ b: false, label: "hi" }], }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_true_value: { it: "should work well with true value for condition", content: "{#cond}{#product.price > 10}high{/}{#product.price <= 10}low{/}{/cond}", result: 'low', scope: { cond: true, product: { price: 2, }, }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_int_value: { it: "should work well with int value for condition", content: "{#cond}{#product.price > 10}high{/}{#product.price <= 10}low{/}{/cond}", result: 'low', scope: { cond: 10, product: { price: 2, }, }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_string_value: { it: "should work well with str value for condition", content: "{#cond}{#product.price > 10}high{/}{#product.price <= 10}low{/}{/cond}", result: 'low', scope: { cond: "cond", product: { price: 2, }, }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_false_value: { it: "should work well with false value for condition", content: "{^cond}{#product.price > 10}high{/}{#product.price <= 10}low{/}{/cond}", result: 'low', scope: { cond: false, product: { price: 2, }, }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_multi_level: { it: "should work well with multi level angular parser", content: "{#users}{name} {date-age} {/}", result: 'John 1975 Mary 1997 Walt 2078 ', scope: { date: 2019, users: [ { name: "John", age: 44 }, { name: "Mary", age: 22 }, { date: 2100, age: 22, name: "Walt" }, ], }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, condition_w_tr: { it: "should work well with -w:tr conditions inside table inside paragraphLoop condition", content: "{#cond}{-w:tc cond}{val}{/}{/}", result: 'yep', scope: { cond: true, val: "yep", }, options: { paragraphLoop: true, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, angular_expressions: { it: "should work well with nested angular expressions", content: "{v}{#c1}{v}{#c2}{v}{#c3}{v}{/}{/}{/}", result: '0123', scope: { v: "0", c1: { v: "1", c2: { v: "2", c3: { v: "3", }, }, }, }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, angular_this: { it: "should work with this with angular expressions", content: "{#hello}{this}{/hello}", result: 'world', scope: { hello: ["world"] }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, angular_get_parent_prop_if_null_child: { it: "should get parent prop if child is null", content: "{#c}{label}{/c}", result: 'hello', scope: { c: { label: null }, label: "hello" }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, double_nested_array: { it: "should work when using double nested arrays", content: "{#a}{this}{/}", result: 'first-part,other-part', scope: { a: [["first-part", "other-part"]] }, options: { parser: angularParser, }, lexed: null, parsed: null, postparsed: null, resolved: null, }, }; fixtures.rawxmlemptycontent = clone(fixtures.rawxml); fixtures.rawxmlemptycontent.it = "should work with rawxml with undefined tags"; fixtures.rawxmlemptycontent.scope = {}; fixtures.rawxmlemptycontent.result = "BEFOREAFTER"; Object.keys(fixtures).forEach(function (key) { const fixture = fixtures[key]; const delimiters = { delimiters: fixture.delimiters || { start: "{", end: "}", }, }; fixture.options = assign({}, fixture.options, delimiters); }); module.exports = fixtures;