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
JavaScript
"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 >>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"
});
});
});
});