UNPKG

docxtemplater

Version:

docx and pptx generator working with templates and data (like Mustache, for Word and Powerpoint documents)

1,408 lines (1,398 loc) 39.1 kB
"use strict"; var _require = require("./utils.js"), expectToThrow = _require.expectToThrow, createDoc = _require.createDoc, createDocV4 = _require.createDocV4, shouldBeSame = _require.shouldBeSame, expect = _require.expect, resolveSoon = _require.resolveSoon, createXmlTemplaterDocxNoRender = _require.createXmlTemplaterDocxNoRender, cleanRecursive = _require.cleanRecursive; var printy = require("./printy.js"); var _require2 = require("lodash"), cloneDeep = _require2.cloneDeep; var _require3 = require("./data-fixtures.js"), expectedPrintedPostParsed = _require3.expectedPrintedPostParsed, rawXMLValue = _require3.rawXMLValue; var angularParser = require("./angular-parser.js"); var Errors = require("../errors.js"); describe("Simple templating", function () { describe("text templating", function () { it("should change values with template data", function () { var tags = { first_name: "Hipp", last_name: "Edgar", phone: "0652455478", description: "New Website" }; var doc = createDocV4("tag-example.docx"); doc.setData(tags); doc.render(); expect(doc.getFullText()).to.be.equal("Edgar Hipp"); expect(doc.getFullText("word/header1.xml")).to.be.equal("Edgar Hipp0652455478New Website"); expect(doc.getFullText("word/footer1.xml")).to.be.equal("EdgarHipp0652455478"); shouldBeSame({ doc: doc, expectedName: "expected-tag-example.docx" }); }); }); it("should replace custom properties text", function () { var doc = createDoc("properties.docx"); var app = doc.getZip().files["docProps/app.xml"].asText(); var core = doc.getZip().files["docProps/core.xml"].asText(); expect(app).to.contain("{tag1}"); expect(core).to.contain("{tag1}"); expect(core).to.contain("{tag2}"); expect(core).to.contain("{tag3}"); expect(app).to.contain("{tag4}"); expect(app).to.contain("{tag5}"); expect(core).to.contain("{tag6}"); expect(core).to.contain("{tag7}"); expect(core).to.contain("{tag8}"); expect(app).to.contain("{tag9}"); doc.setData({ tag1: "resolvedvalue1", tag2: "resolvedvalue2", tag3: "resolvedvalue3", tag4: "resolvedvalue4", tag5: "resolvedvalue5", tag6: "resolvedvalue6", tag7: "resolvedvalue7", tag8: "resolvedvalue8", tag9: "resolvedvalue9" }).render(); app = doc.getZip().files["docProps/app.xml"].asText(); core = doc.getZip().files["docProps/core.xml"].asText(); expect(app).to.contain("resolvedvalue1"); expect(core).to.contain("resolvedvalue1"); expect(core).to.contain("resolvedvalue2"); expect(core).to.contain("resolvedvalue3"); expect(app).to.contain("resolvedvalue4"); expect(app).to.contain("resolvedvalue5"); expect(core).to.contain("resolvedvalue6"); expect(core).to.contain("resolvedvalue7"); expect(core).to.contain("resolvedvalue8"); expect(app).to.contain("resolvedvalue9"); }); }); describe("Spacing/Linebreaks", function () { it("should show spaces with linebreak option", function () { var doc = createDoc("tag-multiline.docx"); doc.setData({ description: "hello there\n deep indentation\n goes here\n end" }); doc.setOptions({ linebreaks: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-multiline-indent.docx" }); }); it("should be possible to have linebreaks if setting the option", function () { var doc = createDoc("tag-multiline.docx"); doc.setData({ description: "The description,\nmultiline" }); doc.setOptions({ linebreaks: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-multiline.docx" }); }); it("should work with linebreaks without changing the style", function () { var doc = createDoc("multi-tags.docx"); doc.setData({ test: "The tag1,\nmultiline\nfoobaz", test2: "The tag2,\nmultiline\nfoobar" }); doc.setOptions({ linebreaks: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-two-multiline.docx" }); }); it("should be possible to have linebreaks if setting the option", function () { var doc = createDoc("tag-multiline.pptx"); doc.setData({ description: "The description,\nmultiline" }); doc.setOptions({ linebreaks: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-multiline.pptx" }); }); it("should not fail when using linebreaks and tagvalue not a string", function () { var doc = createDoc("tag-multiline.pptx"); doc.setData({ description: true }); doc.setOptions({ linebreaks: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-regression-multiline.pptx" }); }); }); describe("Assignment", function () { it("should be possible to assign a value from the template", function () { var doc = createDoc("assignment.docx"); doc.setData({ first_name: "Jane", last_name: "Doe" }); doc.setOptions({ paragraphLoop: true, parser: angularParser }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-assignment.docx" }); }); }); describe("Docm/Pptm generation", function () { it("should work with docm", function () { var tags = { user: "John" }; var doc = createDoc("input.docm"); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-docm.docx" }); }); it("should work with pptm", function () { var tags = { user: "John" }; var doc = createDoc("input.pptm"); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-pptm.pptx" }); }); }); describe("Dotm/dotx generation", function () { it("should work with dotx", function () { var tags = { user: "John" }; var doc = createDoc("input.dotx"); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-dotx.docx" }); }); it("should work with dotm", function () { var tags = { user: "John" }; var doc = createDoc("input.dotm"); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-dotm.docx" }); }); }); describe("Pptx generation", function () { it("should work with title", function () { var doc = createDoc("title-example.pptx"); var con = doc.getZip().files["docProps/app.xml"].asText(); expect(con).not.to.contain("Edgar"); doc.setData({ name: "Edgar" }).render(); con = doc.getZip().files["docProps/app.xml"].asText(); expect(con).to.contain("Edgar"); }); it("should work with simple pptx", function () { var doc = createDoc("simple-example.pptx"); doc.setData({ name: "Edgar" }).render(); expect(doc.getFullText()).to.be.equal("Hello Edgar"); }); it("should work with table pptx", function () { var doc = createDoc("table-example.pptx"); doc.setData({ users: [{ msg: "hello", name: "mary" }, { msg: "hello", name: "john" }] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-table-example.pptx" }); }); it("should work with loop pptx", function () { var doc = createDoc("loop-example.pptx"); doc.setData({ users: [{ name: "Doe" }, { name: "John" }] }).render(); expect(doc.getFullText()).to.be.equal(" Doe John "); shouldBeSame({ doc: doc, expectedName: "expected-loop-example.pptx" }); }); it("should work with simple raw pptx", function () { var doc = createDoc("raw-xml-example.pptx"); var scope, meta, tag; var calls = 0; doc.setOptions({ parser: function parser(t) { tag = t; return { get: function get(s, m) { scope = s; meta = m.meta; calls++; return scope[tag]; } }; } }); doc.setData({ raw: rawXMLValue }).render(); expect(calls).to.equal(1); expect(scope.raw).to.be.a("string"); expect(meta).to.be.an("object"); expect(meta.part).to.be.an("object"); expect(meta.part.expanded).to.be.an("array"); expect(doc.getFullText()).to.be.equal("Hello World"); shouldBeSame({ doc: doc, expectedName: "expected-raw-xml-example.pptx" }); }); it("should work with simple raw pptx async", function () { var doc = createDoc("raw-xml-example.pptx"); var scope, meta, tag; var calls = 0; doc.setOptions({ parser: function parser(t) { tag = t; return { get: function get(s, m) { scope = s; meta = m.meta; calls++; return scope[tag]; } }; } }); doc.compile(); return doc.resolveData({ raw: rawXMLValue }).then(function () { doc.render(); expect(calls).to.equal(1); expect(scope.raw).to.be.a("string"); expect(meta).to.be.an("object"); expect(meta.part).to.be.an("object"); expect(meta.part.expanded).to.be.an("array"); expect(doc.getFullText()).to.be.equal("Hello World"); shouldBeSame({ doc: doc, expectedName: "expected-raw-xml-example.pptx" }); }); }); }); describe("Table", function () { it("should work with selfclosing tag inside table with paragraphLoop", function () { var tags = { a: [{ b: { c: "Foo", d: "Hello " } }, { b: { c: "Foo", d: "Hello " } }] }; var doc = createDoc("loop-valid.docx"); doc.setData(tags); doc.setOptions({ paragraphLoop: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-loop-valid.docx" }); }); it("should work with tables", function () { var tags = { clients: [{ first_name: "John", last_name: "Doe", phone: "+33647874513" }, { first_name: "Jane", last_name: "Doe", phone: "+33454540124" }, { first_name: "Phil", last_name: "Kiel", phone: "+44578451245" }, { first_name: "Dave", last_name: "Sto", phone: "+44548787984" }] }; var doc = createDoc("tag-intelligent-loop-table.docx"); doc.setData(tags); doc.render(); var expectedText = "JohnDoe+33647874513JaneDoe+33454540124PhilKiel+44578451245DaveSto+44548787984"; var text = doc.getFullText(); expect(text).to.be.equal(expectedText); shouldBeSame({ doc: doc, expectedName: "expected-tag-intelligent-loop-table.docx" }); }); it("should work with simple table", function () { var doc = createDoc("table-complex2-example.docx"); doc.setData({ table1: [{ t1data1: "t1-1row-data1", t1data2: "t1-1row-data2", t1data3: "t1-1row-data3", t1data4: "t1-1row-data4" }, { t1data1: "t1-2row-data1", t1data2: "t1-2row-data2", t1data3: "t1-2row-data3", t1data4: "t1-2row-data4" }, { t1data1: "t1-3row-data1", t1data2: "t1-3row-data2", t1data3: "t1-3row-data3", t1data4: "t1-3row-data4" }], t1total1: "t1total1-data", t1total2: "t1total2-data", t1total3: "t1total3-data" }); doc.render(); var fullText = doc.getFullText(); expect(fullText).to.be.equal("TABLE1COLUMN1COLUMN2COLUMN3COLUMN4t1-1row-data1t1-1row-data2t1-1row-data3t1-1row-data4t1-2row-data1t1-2row-data2t1-2row-data3t1-2row-data4t1-3row-data1t1-3row-data2t1-3row-data3t1-3row-data4TOTALt1total1-datat1total2-datat1total3-data"); }); it("should work with more complex table", function () { var doc = createDoc("table-complex-example.docx"); doc.setData({ table2: [{ t2data1: "t2-1row-data1", t2data2: "t2-1row-data2", t2data3: "t2-1row-data3", t2data4: "t2-1row-data4" }, { t2data1: "t2-2row-data1", t2data2: "t2-2row-data2", t2data3: "t2-2row-data3", t2data4: "t2-2row-data4" }], t1total1: "t1total1-data", t1total2: "t1total2-data", t1total3: "t1total3-data", t2total1: "t2total1-data", t2total2: "t2total2-data", t2total3: "t2total3-data" }); doc.render(); var fullText = doc.getFullText(); expect(fullText).to.be.equal("TABLE1COLUMN1COLUMN2COLUMN3COLUMN4TOTALt1total1-datat1total2-datat1total3-dataTABLE2COLUMN1COLUMN2COLUMN3COLUMN4t2-1row-data1t2-1row-data2t2-1row-data3t2-1row-data4t2-2row-data1t2-2row-data2t2-2row-data3t2-2row-data4TOTALt2total1-datat2total2-datat2total3-data"); }); it("should work when looping around tables", function () { var doc = createDoc("table-repeat.docx"); doc.setData({ table: [1, 2, 3, 4] }); doc.render(); var fullText = doc.getFullText(); expect(fullText).to.be.equal("1234123412341234"); }); it("should not corrupt table with empty rawxml", function () { var doc = createDoc("table-raw-xml.docx"); doc.setData({}); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-raw-xml.docx" }); }); it("should not corrupt document with empty rawxml after a table, at the end of the document", function () { var doc = createDoc("raw-xml-after-table.docx"); doc.setData({}); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-raw-xml-after-table.docx" }); }); it("should not corrupt document with selfclosing w:sdtContent tag", function () { var doc = createDoc("self-closing-w-sdtcontent.docx"); doc.setData({}); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-self-closing-w-sdtcontent.docx" }); }); it("should not corrupt loop containing section", function () { var doc = createDoc("loop-with-section.docx"); doc.setData({ loop1: [{ loop2: [1, 2] }, { loop2: [] }, { loop2: [3, 4, 5] }] }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-multi-section.docx" }); }); it("should not corrupt sdtcontent", function () { var doc = createDoc("regression-sdtcontent-paragraph.docx"); doc.setData({ loop: { name: "foo", Id: "bar" } }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-sdtcontent-valid.docx" }); }); it("should not corrupt table with empty rawxml within loop", function () { var doc = createDoc("loops-with-table-raw-xml.docx"); doc.setData({ loop: [{ loop2: [] }, { loop2: {}, raw: "<w:p><w:r><w:t>RAW</w:t></w:r></w:p>" }] }); doc.setOptions({ paragraphLoop: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-loop-raw-xml.docx" }); }); it("should not corrupt table with empty loop", function () { var doc = createDoc("table-loop.docx"); doc.setData({}); doc.setOptions({ paragraphLoop: true }); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-empty-table.docx" }); }); }); describe("Dash Loop", function () { it("should work on simple table -> w:tr", function () { var tags = { os: [{ type: "linux", price: "0", reference: "Ubuntu10" }, { type: "DOS", price: "500", reference: "Win7" }, { type: "apple", price: "1200", reference: "MACOSX" }] }; var doc = createDoc("tag-dash-loop.docx"); doc.setData(tags); doc.render(); var expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX"; var text = doc.getFullText(); expect(text).to.be.equal(expectedText); }); it("should work on simple table -> w:table", function () { var tags = { os: [{ type: "linux", price: "0", reference: "Ubuntu10" }, { type: "DOS", price: "500", reference: "Win7" }, { type: "apple", price: "1200", reference: "MACOSX" }] }; var doc = createDoc("tag-dash-loop-table.docx"); doc.setData(tags); doc.render(); var expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX"; var text = doc.getFullText(); expect(text).to.be.equal(expectedText); }); it("should work on simple list -> w:p", function () { var tags = { os: [{ type: "linux", price: "0", reference: "Ubuntu10" }, { type: "DOS", price: "500", reference: "Win7" }, { type: "apple", price: "1200", reference: "MACOSX" }] }; var doc = createDoc("tag-dash-loop-list.docx"); doc.setData(tags); doc.render(); var expectedText = "linux 0 Ubuntu10 DOS 500 Win7 apple 1200 MACOSX "; var text = doc.getFullText(); expect(text).to.be.equal(expectedText); }); it("should not corrupt document if using empty {-a:p} inside table cell", function () { var doc = createDoc("regression-dash-loop-in-table-cell.pptx"); doc.setData().render(); shouldBeSame({ doc: doc, expectedName: "expected-table-3-cells.pptx" }); }); it("should not corrupt document if using empty {-a:p} inside table cell", function () { var doc = createDoc("regression-dash-loop-in-table-cell.pptx"); doc.setData({ cond: [1, 2, 3] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-table-3-true-cells.pptx" }); }); }); describe("Section breaks inside loops", function () { it("should work at beginning of paragraph loop with 3 elements", function () { // Warning : In libreoffice, this is not rendered correctly, use WPS or Word var doc = createDoc("page-break-inside-condition.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ cond: [1, 2, 3] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-with-page-break-3-els.docx" }); }); it("should work at beginning of paragraph loop with false", function () { // Warning : In libreoffice, this is not rendered correctly, use WPS or Word var doc = createDoc("page-break-inside-condition.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ cond: false }).render(); shouldBeSame({ doc: doc, expectedName: "expected-with-page-break-falsy.docx" }); }); it("should work at beginning of std loop with false", function () { // Warning : In libreoffice, this is not rendered correctly, use WPS or Word var doc = createDoc("page-break-inside-condition.docx"); doc.setData({ cond: false }).render(); shouldBeSame({ doc: doc, expectedName: "expected-page-break-falsy-std-loop.docx" }); }); it("should work at beginning of std loop with 3 elements", function () { // Warning : In libreoffice, this is not rendered correctly, use WPS or Word var doc = createDoc("page-break-inside-condition.docx"); doc.setData({ cond: [1, 2, 3] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-page-break-3-els-std-loop.docx" }); }); it("should work at beginning of std loop with truthy", function () { // Warning : In libreoffice, this is not rendered correctly, use WPS or Word var doc = createDoc("page-break-inside-condition.docx"); doc.setData({ cond: true }).render(); shouldBeSame({ doc: doc, expectedName: "expected-page-break-truthy-std-loop.docx" }); }); it("should work with table inside paragraph loop", function () { var doc = createDoc("pagebreak-table-loop.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ loop: [1, 2, 3] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-pagebreak-table-loop.docx" }); }); it("should work at end of std loop", function () { var doc = createDoc("paragraph-loop-with-pagebreak.docx"); doc.setData({ users: [{ name: "Bar" }, { name: "John" }, { name: "Baz" }] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-noparagraph-loop-with-pagebreak.docx" }); }); it("should work at end of paragraph loop", function () { var doc = createDoc("paragraph-loop-with-pagebreak.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ users: [{ name: "Bar" }, { name: "John" }, { name: "Baz" }] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-paragraph-loop-with-pagebreak.docx" }); }); it("should work with pagebreak afterwards with falsy value", function () { var doc = createDoc("paragraph-loop-with-pagebreak.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ users: false }).render(); shouldBeSame({ doc: doc, expectedName: "expected-paragraph-loop-empty-with-pagebreak.docx" }); }); it("should make first section break of the file continuous", function () { var tags = { loop: [1, 2, 3] }; var doc = createDocV4("loop-with-section-break.docx", { paragraphLoop: true, parser: angularParser }); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-loop-with-section-break.docx" }); }); it("should work with delimiters << >> when saved in word as &gt;&gt;test>>", function () { var tags = { my_tag: "Hello John" }; var doc = createDocV4("gt-delimiters.docx", { parser: angularParser, delimiters: { start: "<<", end: ">>" } }); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-rendered-hello.docx" }); }); it("should make first section break of the file continuous", function () { var tags = { loop: [1, 2, 3] }; var doc = createDocV4("loop-with-section-break.docx", { parser: angularParser }); doc.setData(tags); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-loop-with-section-break-2.docx" }); }); }); describe("ParagraphLoop", function () { it("should work with docx", function () { var doc = createDoc("users.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ users: ["John", "Jane", "Louis"] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-users.docx" }); }); it("should work without removing extra text", function () { var doc = createDoc("paragraph-loops.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ condition: [1, 2], l1: [{ l2: ["a", "b", "c"] }, { l2: ["d", "e", "f"] }], placeholder: "placeholder-value" }).render(); shouldBeSame({ doc: doc, expectedName: "expected-paragraph-loop.docx" }); }); it("should work with pptx", function () { var doc = createDoc("paragraph-loop.pptx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ users: [{ age: 10, name: "Bar" }, { age: 18, name: "Bar" }, { age: 22, name: "Bar" }] }).render(); shouldBeSame({ doc: doc, expectedName: "expected-paragraph-loop.pptx" }); }); it("should not fail when having paragraph in paragraph", function () { var doc = createDoc("regression-par-in-par.docx"); var printedPostparsed = []; var filePath = ""; doc.attachModule({ set: function set(obj) { if (obj.inspect) { if (obj.inspect.filePath) { filePath = obj.inspect.filePath; } if (obj.inspect.postparsed) { printedPostparsed[filePath] = printy(obj.inspect.postparsed); } } } }); doc.setOptions({ paragraphLoop: true, parser: function parser() { return { get: function get() { return "foo"; } }; } }); doc.setData({}); doc.render(); expect(printedPostparsed["word/document.xml"]).to.be.equal(expectedPrintedPostParsed); shouldBeSame({ doc: doc, expectedName: "expected-rendered-par-in-par.docx" }); }); it("should work with spacing at the end", function () { var doc = createDoc("spacing-end.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ name: "John" }).render(); shouldBeSame({ doc: doc, expectedName: "expected-spacing-end.docx" }); }); it("should throw specific error if calling .render() on document with invalid tags", function () { var doc = createDoc("errors-footer-and-header.docx"); doc.setOptions({ paragraphLoop: true, parser: angularParser }); var catched = false; try { doc.compile(); } catch (e) { catched = true; var expectedError = { name: "InternalError", message: "You should not call .render on a document that had compilation errors", properties: { id: "render_on_invalid_template" } }; expectToThrow(function () { return doc.render(); }, Errors.XTInternalError, expectedError); /* handle error */ } expect(catched).to.equal(true); }); it("should fail with errors from header and footer", function () { var doc = createDoc("errors-footer-and-header.docx"); doc.setOptions({ paragraphLoop: true, parser: angularParser }); doc.setData({}); var expectedError = { message: "Multi error", name: "TemplateError", properties: { id: "multi_error", errors: [{ name: "TemplateError", message: "Unclosed tag", properties: { file: "word/footer1.xml", xtag: "footer", id: "unclosed_tag", context: "{footer", offset: 2 } }, { name: "TemplateError", message: "Duplicate close tag, expected one close tag", properties: { file: "word/header1.xml", xtag: "itle}}", id: "duplicate_close_tag", context: "itle}}", offset: 15 } }, { name: "TemplateError", message: "Closing tag does not match opening tag", properties: { closingtag: "bang", openingtag: "users", file: "word/document.xml", id: "closing_tag_does_not_match_opening_tag", offset: [8, 16] } }] } }; var create = doc.render.bind(doc); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail properly when having lexed + postparsed errors", function () { var doc = createDoc("multi-errors.docx"); doc.setOptions({ paragraphLoop: true, parser: angularParser }); doc.setData({ users: [{ age: 10, name: "Bar" }, { age: 18, name: "Bar" }, { age: 22, name: "Bar" }] }); var expectedError = { message: "Multi error", name: "TemplateError", properties: { id: "multi_error", errors: [{ name: "TemplateError", message: "Unclosed tag", properties: { file: "word/document.xml", xtag: "firstName", id: "unclosed_tag", context: "{firstName ", offset: 0 } }, { name: "TemplateError", message: "Unclosed tag", properties: { file: "word/document.xml", xtag: "error", id: "unclosed_tag", context: "{error ", offset: 22 } }, { name: "TemplateError", message: "Duplicate close tag, expected one close tag", properties: { file: "word/document.xml", xtag: "{tag}}", id: "duplicate_close_tag", context: "{tag}}", offset: 34 } }, { name: "TemplateError", message: "Duplicate open tag, expected one open tag", properties: { file: "word/document.xml", xtag: "{{bar}", id: "duplicate_open_tag", context: "{{bar}", offset: 42 } }] } }; var create = doc.render.bind(doc); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when placing paragraph loop inside normal loop", function () { var doc = createDoc("paragraph-loop-error.docx"); doc.setData({}); var expectedError = { message: "Multi error", name: "TemplateError", properties: { id: "multi_error", errors: [{ name: "TemplateError", message: 'No tag "w:p" was found at the left', properties: { file: "word/document.xml", id: "no_xml_tag_found_at_left", element: "w:p", index: 1, parsedLength: 4, offset: 12, part: { endLindex: 17, expandTo: "w:p", inverted: false, lIndex: 17, location: "start", module: "loop", offset: 12, raw: "-w:p loop", type: "placeholder", value: "loop" } } }, { name: "TemplateError", message: 'No tag "w:p" was found at the right', properties: { file: "word/document.xml", id: "no_xml_tag_found_at_right", element: "w:p", index: 3, parsedLength: 4, offset: 26, part: { endLindex: 21, lIndex: 21, location: "end", module: "loop", offset: 26, raw: "/", type: "placeholder", value: "" } } }] } }; var create = doc.compile.bind(doc); expectToThrow(create, Errors.XTTemplateError, expectedError); }); }); describe("Prefixes", function () { it("should be possible to change the prefix of the loop module", function () { var content = "<w:t>{##tables}{user}{/tables}</w:t>"; var scope = { tables: [{ user: "John" }, { user: "Jane" }] }; var doc = createXmlTemplaterDocxNoRender(content, { tags: scope }); doc.modules.forEach(function (module) { if (module.name === "LoopModule") { module.prefix.start = "##"; } }); doc.render(); expect(doc.getFullText()).to.be.equal("JohnJane"); }); it("should be possible to change the prefix of the loop module to a regexp", function () { var content = "<w:t>{##tables}{user}{/tables}{#tables}{user}{/tables}</w:t>"; var scope = { tables: [{ user: "A" }, { user: "B" }] }; var doc = createXmlTemplaterDocxNoRender(content, { tags: scope }); doc.modules.forEach(function (module) { if (module.name === "LoopModule") { module.prefix.start = /^##?(.*)$/; } }); doc.render(); expect(doc.getFullText()).to.be.equal("ABAB"); }); it("should be possible to change the prefix of the raw xml module to a regexp", function () { var content = "<w:p><w:t>{!!raw}</w:t></w:p>"; var scope = { raw: "<w:p><w:t>HoHo</w:t></w:p>" }; var doc = createXmlTemplaterDocxNoRender(content, { tags: scope }); doc.modules.forEach(function (module) { if (module.name === "RawXmlModule") { module.prefix = /^!!?(.*)$/; } }); doc.render(); expect(doc.getFullText()).to.be.equal("HoHo"); }); }); describe("Load Office 365 file", function () { it("should handle files with word/document2.xml", function () { var doc = createDoc("office365.docx"); doc.setOptions({ paragraphLoop: true }); doc.setData({ test: "Value", test2: "Value2" }).render(); expect(doc.getFullText()).to.be.equal("Value Value2"); shouldBeSame({ doc: doc, expectedName: "expected-office365.docx" }); }); }); describe("Resolver", function () { it("should work", function () { var doc = createDoc("office365.docx"); doc.setOptions({ paragraphLoop: true }); doc.compile(); return doc.resolveData({ test: resolveSoon("Value"), test2: "Value2" }).then(function () { doc.render(); expect(doc.getFullText()).to.be.equal("Value Value2"); shouldBeSame({ doc: doc, expectedName: "expected-office365.docx" }); }); }); it("should work at parent level", function () { var doc = createDoc("office365.docx"); doc.setOptions({ paragraphLoop: true }); doc.compile(); return doc.resolveData(resolveSoon({ test: resolveSoon("Value"), test2: "Value2" })).then(function () { doc.render(); expect(doc.getFullText()).to.be.equal("Value Value2"); shouldBeSame({ doc: doc, expectedName: "expected-office365.docx" }); }); }); it("should resolve loops", function () { var doc = createDoc("multi-loop.docx"); doc.setOptions({ paragraphLoop: true }); doc.compile(); return doc.resolveData({ companies: resolveSoon([{ name: "Acme", users: resolveSoon([{ name: "John" }, { name: "James" }]) }, { name: resolveSoon("Emca"), users: resolveSoon([{ name: "Mary" }, { name: "Liz" }]) }]), test2: "Value2" }).then(function () { doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-multi-loop.docx" }); }); }); it("should resolve with simple table", function () { var doc = createDoc("table-complex2-example.docx"); doc.compile(); return doc.resolveData({ table1: [{ t1data1: "t1-1row-data1", t1data2: "t1-1row-data2", t1data3: "t1-1row-data3", t1data4: "t1-1row-data4" }, { t1data1: "t1-2row-data1", t1data2: "t1-2row-data2", t1data3: "t1-2row-data3", t1data4: "t1-2row-data4" }, { t1data1: "t1-3row-data1", t1data2: "t1-3row-data2", t1data3: "t1-3row-data3", t1data4: "t1-3row-data4" }], t1total1: "t1total1-data", t1total2: "t1total2-data", t1total3: "t1total3-data" }).then(function (resolved) { var myresolved = cloneDeep(resolved); cleanRecursive(myresolved); expect(myresolved).to.be.deep.equal([{ tag: "t1total1", value: "t1total1-data" }, { tag: "t1total2", value: "t1total2-data" }, { tag: "t1total3", value: "t1total3-data" }, { tag: "table1", value: [[{ tag: "t1data1", value: "t1-1row-data1" }, { tag: "t1data2", value: "t1-1row-data2" }, { tag: "t1data3", value: "t1-1row-data3" }, { tag: "t1data4", value: "t1-1row-data4" }], [{ tag: "t1data1", value: "t1-2row-data1" }, { tag: "t1data2", value: "t1-2row-data2" }, { tag: "t1data3", value: "t1-2row-data3" }, { tag: "t1data4", value: "t1-2row-data4" }], [{ tag: "t1data1", value: "t1-3row-data1" }, { tag: "t1data2", value: "t1-3row-data2" }, { tag: "t1data3", value: "t1-3row-data3" }, { tag: "t1data4", value: "t1-3row-data4" }]] }]); doc.render(); var fullText = doc.getFullText(); expect(fullText).to.be.equal("TABLE1COLUMN1COLUMN2COLUMN3COLUMN4t1-1row-data1t1-1row-data2t1-1row-data3t1-1row-data4t1-2row-data1t1-2row-data2t1-2row-data3t1-2row-data4t1-3row-data1t1-3row-data2t1-3row-data3t1-3row-data4TOTALt1total1-datat1total2-datat1total3-data"); }); }); var dataNestedLoops = { a: [{ d: "Hello world" }] }; it("should not regress with nested loops sync", function () { var doc = createDoc("regression-complex-loops.docx"); doc.compile(); doc.setData(dataNestedLoops); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-regression-complex-loops.docx" }); }); it("should not regress when having [Content_Types.xml] contain Default instead of Override", function () { var doc = createDoc("with-default-contenttype.docx"); doc.compile(); doc.setData({}); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-with-default-contenttype.docx" }); }); it("should not regress with nested loops async", function () { var doc = createDoc("regression-complex-loops.docx"); doc.compile(); return doc.resolveData(dataNestedLoops).then(function () { doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-regression-complex-loops.docx" }); }); }); var regress2Data = { amount_wheels_car_1: "4", amount_wheels_motorcycle_1: "2", amount_wheels_car_2: "6", amount_wheels_motorcycle_2: "3", id: [{ car: "1", motorcycle: "" }] }; it("should not regress with multiple loops sync", function () { var doc = createDoc("regression-loops-resolve.docx"); doc.compile(); doc.setData(regress2Data); doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-regression-loops-resolve.docx" }); }); it("should not regress with multiple loops async", function () { var doc = createDoc("regression-loops-resolve.docx"); doc.compile(); return doc.resolveData(regress2Data).then(function () { doc.render(); shouldBeSame({ doc: doc, expectedName: "expected-regression-loops-resolve.docx" }); }); }); });