1108 lines
33 KiB
JavaScript
1108 lines
33 KiB
JavaScript
"use strict";
|
|
|
|
var _require = require("../utils.js"),
|
|
expectToThrow = _require.expectToThrow,
|
|
createDoc = _require.createDoc,
|
|
createDocV4 = _require.createDocV4,
|
|
shouldBeSame = _require.shouldBeSame,
|
|
expect = _require.expect,
|
|
makeDocxV4 = _require.makeDocxV4,
|
|
captureLogs = _require.captureLogs;
|
|
var printy = require("../printy.js");
|
|
var angularParser = require("../../expressions.js");
|
|
var Errors = require("../../errors.js");
|
|
describe("Simple templating", function () {
|
|
describe("text templating", function () {
|
|
it("should change values with template data", function () {
|
|
var doc = createDocV4("tag-example.docx").render({
|
|
first_name: "Hipp",
|
|
last_name: "Edgar",
|
|
phone: "0652455478",
|
|
description: "New Website"
|
|
});
|
|
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();
|
|
var filePaths = [];
|
|
var module = {
|
|
name: "Test module",
|
|
requiredAPIVersion: "3.0.0",
|
|
render: function render(a, options) {
|
|
if (filePaths.indexOf(options.filePath) === -1) {
|
|
filePaths.push(options.filePath);
|
|
}
|
|
}
|
|
};
|
|
doc.attachModule(module);
|
|
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.render({
|
|
tag1: "resolvedvalue1",
|
|
tag2: "resolvedvalue2",
|
|
tag3: "resolvedvalue3",
|
|
tag4: "resolvedvalue4",
|
|
tag5: "resolvedvalue5",
|
|
tag6: "resolvedvalue6",
|
|
tag7: "resolvedvalue7",
|
|
tag8: "resolvedvalue8",
|
|
tag9: "resolvedvalue9"
|
|
});
|
|
/* The order here is important !
|
|
* We expect the word/settings.xml templating to happen before the
|
|
* "word/document.xml"
|
|
*
|
|
* This way, users can write assignments in the word/settings.xml, and
|
|
* use the exposed variables in the word/document.xml
|
|
*
|
|
* Fixed since 3.37.6
|
|
*/
|
|
expect(filePaths).to.deep.equal(["word/settings.xml", "docProps/core.xml", "docProps/app.xml", "word/document.xml"]);
|
|
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("Docxtemplater internal properties", function () {
|
|
it("should calculate filesContentTypes and invertedContentTypes", function () {
|
|
var doc = createDocV4("tag-example.docx");
|
|
expect(doc.filesContentTypes).to.deep.equal({
|
|
"_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
|
"word/_rels/document.xml.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
|
"docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
|
"docProps/core.xml": "application/vnd.openxmlformats-package.core-properties+xml",
|
|
"word/document.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
|
"word/endnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml",
|
|
"word/fontTable.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml",
|
|
"word/footer1.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
|
"word/footnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
|
"word/header1.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
|
"word/settings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
|
|
"word/styles.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
|
"word/stylesWithEffects.xml": "application/vnd.ms-word.stylesWithEffects+xml",
|
|
"word/theme/theme1.xml": "application/vnd.openxmlformats-officedocument.theme+xml",
|
|
"word/webSettings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"
|
|
});
|
|
expect(doc.invertedContentTypes).to.deep.equal({
|
|
"application/vnd.openxmlformats-package.relationships+xml": ["_rels/.rels", "word/_rels/document.xml.rels"],
|
|
"application/vnd.ms-word.stylesWithEffects+xml": ["word/stylesWithEffects.xml"],
|
|
"application/vnd.openxmlformats-officedocument.extended-properties+xml": ["docProps/app.xml"],
|
|
"application/vnd.openxmlformats-officedocument.theme+xml": ["word/theme/theme1.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": ["word/document.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": ["word/endnotes.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml": ["word/fontTable.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": ["word/footer1.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": ["word/footnotes.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml": ["word/header1.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": ["word/settings.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": ["word/styles.xml"],
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml": ["word/webSettings.xml"],
|
|
"application/vnd.openxmlformats-package.core-properties+xml": ["docProps/core.xml"]
|
|
});
|
|
});
|
|
it("should calculate filesContentTypes and invertedContentTypes", function () {
|
|
var doc = createDocV4("cond-image.docx");
|
|
var jpegImages = doc.invertedContentTypes["image/jpeg"];
|
|
expect(jpegImages).to.deep.equal(["word/media/image1.jpeg"]);
|
|
expect(doc.invertedContentTypes["application/vnd.openxmlformats-package.relationships+xml"]).to.deep.equal(["_rels/.rels", "word/_rels/document.xml.rels"]);
|
|
});
|
|
it("should load relationships with xmlDocuments", function () {
|
|
var xmlDocs = null;
|
|
var mod = {
|
|
name: "XmlDocumentsModule",
|
|
set: function set(options) {
|
|
if (options.xmlDocuments) {
|
|
xmlDocs = options.xmlDocuments;
|
|
}
|
|
}
|
|
};
|
|
createDocV4("with-default-contenttype.docx", {
|
|
modules: [mod]
|
|
});
|
|
var keys = Object.keys(xmlDocs);
|
|
var ct = "[Content_Types].xml";
|
|
expect(keys).to.deep.equal([ct]);
|
|
var mainDoc = xmlDocs[ct];
|
|
expect(mainDoc.getElementsByTagName("Override")[0].getAttribute("PartName")).to.equal("/docProps/core.xml");
|
|
expect(mainDoc.getElementsByTagName("parsererror").length).to.equal(0);
|
|
});
|
|
});
|
|
describe("Special characters", function () {
|
|
it("should not escape tab character", function () {
|
|
return this.renderV4({
|
|
name: "tab-character.pptx",
|
|
expectedName: "expected-tab-character.pptx"
|
|
});
|
|
});
|
|
it("should not double escape loop containing hebrew", function () {
|
|
return this.renderV4({
|
|
name: "loop-hebrew.docx",
|
|
data: {
|
|
title: "Default title",
|
|
products: [{
|
|
title: "Duk",
|
|
name: "DukSoftware",
|
|
reference: "DS0"
|
|
}, {
|
|
title: "Tingerloo",
|
|
name: "Tingerlee",
|
|
reference: "T00"
|
|
}, {
|
|
title: "Tingerloo",
|
|
name: "Tingerlee",
|
|
reference: "T00"
|
|
}, {
|
|
title: "Tingerloo",
|
|
name: "Tingerlee",
|
|
reference: "T00"
|
|
}]
|
|
},
|
|
expectedName: "expected-loop-hebrew.docx"
|
|
});
|
|
});
|
|
});
|
|
describe("Regression", function () {
|
|
it("should not corrupt when having sdt content", function () {
|
|
return this.renderV4({
|
|
name: "sdt-content.docx",
|
|
data: {},
|
|
expectedName: "expected-sdt-content.docx"
|
|
});
|
|
});
|
|
it("should not corrupt document with tag inside title with linebreaks #docprops-linebreak", function () {
|
|
// Fixed in version 3.40.3
|
|
return this.render({
|
|
data: {
|
|
title: "Foo\nBar\nBaz",
|
|
creator: "Foo\nBar\nBaz",
|
|
lmb: "Foo\nBar\nBaz"
|
|
},
|
|
name: "tag-in-title.pptx",
|
|
options: {
|
|
paragraphLoop: true,
|
|
linebreaks: true
|
|
},
|
|
expectedName: "expected-tag-in-title.pptx"
|
|
});
|
|
});
|
|
});
|
|
describe("Spacing/Linebreaks", function () {
|
|
it("should show spaces with linebreak option", function () {
|
|
var doc = createDoc("tag-multiline.docx").setOptions({
|
|
linebreaks: true
|
|
}).render({
|
|
description: "hello there\n deep indentation\n goes here\n end"
|
|
});
|
|
shouldBeSame({
|
|
doc: doc,
|
|
expectedName: "expected-multiline-indent.docx"
|
|
});
|
|
});
|
|
it("should be possible to have linebreaks if setting the option", function () {
|
|
return this.render({
|
|
name: "tag-multiline.docx",
|
|
options: {
|
|
linebreaks: true
|
|
},
|
|
data: {
|
|
description: "The description,\nmultiline"
|
|
},
|
|
expectedName: "expected-multiline.docx"
|
|
});
|
|
});
|
|
it("should not remove section if having normal loop just before", function () {
|
|
return this.render({
|
|
name: "loop-with-section-break-after.docx",
|
|
expectedName: "expected-loop-with-section-break-after.docx"
|
|
});
|
|
});
|
|
it("should not remove section if having paragraph loop just before", function () {
|
|
return this.render({
|
|
name: "paragraph-loop-with-section-break-after.docx",
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "paragraph-loop-with-section-break-after.docx"
|
|
});
|
|
});
|
|
it("should work with linebreaks and copy the run style onto new lines in docx", function () {
|
|
return this.renderV4({
|
|
name: "multi-tags.docx",
|
|
options: {
|
|
linebreaks: true
|
|
},
|
|
data: {
|
|
test: "The tag1,\nmultiline\nfoobaz",
|
|
test2: "The tag2,\nmultiline\nfoobar"
|
|
},
|
|
async: true,
|
|
expectedName: "expected-two-multiline.docx"
|
|
});
|
|
});
|
|
it("should work with linebreaks and copy the run style onto new lines in pptx", function () {
|
|
return this.render({
|
|
name: "tag-multiline.pptx",
|
|
options: {
|
|
linebreaks: true
|
|
},
|
|
data: {
|
|
description: "The description,\nmultiline"
|
|
},
|
|
expectedName: "expected-multiline.pptx"
|
|
});
|
|
});
|
|
it("should not fail when using linebreaks and tagvalue not a string", function () {
|
|
return this.render({
|
|
name: "tag-multiline.pptx",
|
|
options: {
|
|
linebreaks: true
|
|
},
|
|
data: {
|
|
description: true
|
|
},
|
|
expectedName: "expected-regression-multiline.pptx"
|
|
});
|
|
});
|
|
it("should drop empty lines found inside the tags", function () {
|
|
return this.renderV4({
|
|
name: "tag-spanning-multiple-lines.docx",
|
|
options: {
|
|
parser: function parser() {
|
|
return {
|
|
get: function get() {
|
|
return "";
|
|
}
|
|
};
|
|
}
|
|
},
|
|
expectedName: "expected-no-multiline.docx"
|
|
});
|
|
});
|
|
it("should drop empty lines found inside the tags", function () {
|
|
return this.renderV4({
|
|
name: "tag-spanning-multiple-lines.pptx",
|
|
options: {
|
|
parser: function parser() {
|
|
return {
|
|
get: function get() {
|
|
return "";
|
|
}
|
|
};
|
|
}
|
|
},
|
|
expectedName: "expected-no-multiline.pptx"
|
|
});
|
|
});
|
|
it("should keep run props (font-size) for pptx file", function () {
|
|
return this.renderV4({
|
|
name: "run-props-linebreak.pptx",
|
|
data: {
|
|
data: "blabla\nbloblo\nblublu"
|
|
},
|
|
options: {
|
|
linebreaks: true
|
|
},
|
|
expectedName: "expected-run-props-linebreak.pptx"
|
|
});
|
|
});
|
|
});
|
|
describe("Comments", function () {
|
|
it("should be possible to template values in comments", function () {
|
|
return this.renderV4({
|
|
name: "with-comments.docx",
|
|
data: {
|
|
name: "John"
|
|
},
|
|
expectedName: "expected-comments.docx"
|
|
});
|
|
});
|
|
});
|
|
describe("Assignment", function () {
|
|
it("should be possible to assign a value from the template", function () {
|
|
return this.render({
|
|
name: "assignment.docx",
|
|
data: {
|
|
first_name: "Jane",
|
|
last_name: "Doe"
|
|
},
|
|
options: {
|
|
paragraphLoop: true,
|
|
parser: angularParser
|
|
},
|
|
expectedName: "expected-assignment.docx"
|
|
});
|
|
});
|
|
});
|
|
describe("Unusual document extensions", function () {
|
|
it("should work with docm", function () {
|
|
return this.render({
|
|
name: "input.docm",
|
|
data: {
|
|
user: "John"
|
|
},
|
|
expectedName: "expected-docm.docm"
|
|
});
|
|
});
|
|
it("should work with pptm", function () {
|
|
return this.render({
|
|
name: "input.pptm",
|
|
data: {
|
|
user: "John"
|
|
},
|
|
expectedName: "expected-pptm.pptm"
|
|
});
|
|
});
|
|
it("should work with dotx", function () {
|
|
return this.render({
|
|
name: "input.dotx",
|
|
data: {
|
|
user: "John"
|
|
},
|
|
expectedName: "expected-dotx.dotx"
|
|
});
|
|
});
|
|
it("should work with dotm", function () {
|
|
return this.render({
|
|
name: "input.dotm",
|
|
data: {
|
|
user: "John"
|
|
},
|
|
expectedName: "expected-dotm.dotm"
|
|
});
|
|
});
|
|
});
|
|
describe("Dash Loop", function () {
|
|
it("should work on simple table -> w:tr", function () {
|
|
var doc = createDoc("tag-dash-loop.docx").render({
|
|
os: [{
|
|
type: "linux",
|
|
price: "0",
|
|
reference: "Ubuntu10"
|
|
}, {
|
|
type: "DOS",
|
|
price: "500",
|
|
reference: "Win7"
|
|
}, {
|
|
type: "apple",
|
|
price: "1200",
|
|
reference: "MACOSX"
|
|
}]
|
|
});
|
|
var expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX";
|
|
var text = doc.getFullText();
|
|
expect(text).to.be.equal(expectedText);
|
|
});
|
|
it("should work on simple table -> w:table", function () {
|
|
var doc = createDoc("tag-dash-loop-table.docx").render({
|
|
os: [{
|
|
type: "linux",
|
|
price: "0",
|
|
reference: "Ubuntu10"
|
|
}, {
|
|
type: "DOS",
|
|
price: "500",
|
|
reference: "Win7"
|
|
}, {
|
|
type: "apple",
|
|
price: "1200",
|
|
reference: "MACOSX"
|
|
}]
|
|
});
|
|
var expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX";
|
|
var text = doc.getFullText();
|
|
expect(text).to.be.equal(expectedText);
|
|
});
|
|
it("should work on simple list -> w:p", function () {
|
|
var doc = createDoc("tag-dash-loop-list.docx").render({
|
|
os: [{
|
|
type: "linux",
|
|
price: "0",
|
|
reference: "Ubuntu10"
|
|
}, {
|
|
type: "DOS",
|
|
price: "500",
|
|
reference: "Win7"
|
|
}, {
|
|
type: "apple",
|
|
price: "1200",
|
|
reference: "MACOSX"
|
|
}]
|
|
});
|
|
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 () {
|
|
return this.render({
|
|
name: "regression-dash-loop-in-table-cell.pptx",
|
|
expectedName: "expected-table-3-cells.pptx"
|
|
});
|
|
});
|
|
it("should not corrupt document if using empty {-a:p} inside table cell", function () {
|
|
return this.render({
|
|
name: "regression-dash-loop-in-table-cell.pptx",
|
|
data: {
|
|
cond: [1, 2, 3]
|
|
},
|
|
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
|
|
return this.renderV4({
|
|
name: "page-break-inside-condition.docx",
|
|
data: {
|
|
cond: [1, 2, 3]
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
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
|
|
return this.renderV4({
|
|
name: "page-break-inside-condition.docx",
|
|
data: {
|
|
cond: false
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
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
|
|
return this.render({
|
|
name: "page-break-inside-condition.docx",
|
|
data: {
|
|
cond: false
|
|
},
|
|
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
|
|
return this.render({
|
|
name: "page-break-inside-condition.docx",
|
|
data: {
|
|
cond: [1, 2, 3]
|
|
},
|
|
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
|
|
return this.render({
|
|
name: "page-break-inside-condition.docx",
|
|
data: {
|
|
cond: true
|
|
},
|
|
expectedName: "expected-page-break-truthy-std-loop.docx"
|
|
});
|
|
});
|
|
it("should work with table inside paragraph loop", function () {
|
|
return this.renderV4({
|
|
name: "pagebreak-table-loop.docx",
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
data: {
|
|
loop: [1, 2, 3]
|
|
},
|
|
expectedName: "expected-pagebreak-table-loop.docx"
|
|
});
|
|
});
|
|
it("should work at end of std loop", function () {
|
|
return this.render({
|
|
name: "paragraph-loop-with-pagebreak.docx",
|
|
data: {
|
|
users: [{
|
|
name: "Bar"
|
|
}, {
|
|
name: "John"
|
|
}, {
|
|
name: "Baz"
|
|
}]
|
|
},
|
|
expectedName: "expected-noparagraph-loop-with-pagebreak.docx"
|
|
});
|
|
});
|
|
it("should work at end of paragraph loop", function () {
|
|
return this.renderV4({
|
|
name: "paragraph-loop-with-pagebreak.docx",
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
data: {
|
|
users: [{
|
|
name: "Bar"
|
|
}, {
|
|
name: "John"
|
|
}, {
|
|
name: "Baz"
|
|
}]
|
|
},
|
|
expectedName: "expected-paragraph-loop-with-pagebreak.docx"
|
|
});
|
|
});
|
|
it("should work with pagebreak afterwards with falsy value", function () {
|
|
return this.renderV4({
|
|
name: "paragraph-loop-with-pagebreak.docx",
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
data: {
|
|
users: false
|
|
},
|
|
expectedName: "expected-paragraph-loop-empty-with-pagebreak.docx"
|
|
});
|
|
});
|
|
it("should make first section break of the file continuous", function () {
|
|
return this.renderV4({
|
|
name: "loop-with-continuous-section-break.docx",
|
|
data: {
|
|
loop: [1, 2, 3]
|
|
},
|
|
options: {
|
|
paragraphLoop: true,
|
|
parser: angularParser
|
|
},
|
|
expectedName: "expected-loop-with-continuous-section-break.docx"
|
|
});
|
|
});
|
|
it("should work with delimiters << >> when saved in word as >>test>>", function () {
|
|
return this.renderV4({
|
|
name: "gt-delimiters.docx",
|
|
data: {
|
|
my_tag: "Hello John"
|
|
},
|
|
options: {
|
|
parser: angularParser,
|
|
delimiters: {
|
|
start: "<<",
|
|
end: ">>"
|
|
}
|
|
},
|
|
expectedName: "expected-rendered-hello.docx"
|
|
});
|
|
});
|
|
it("should work with quotes in tag with angular parser", function () {
|
|
return this.renderV4({
|
|
name: "quotes-in-tag.docx",
|
|
data: {
|
|
user: "John",
|
|
hobbies: ["hiking", "reading"]
|
|
},
|
|
options: {
|
|
parser: angularParser,
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-quotes-in-tag.docx"
|
|
});
|
|
});
|
|
it("should make first section break of the file continuous", function () {
|
|
return this.renderV4({
|
|
name: "loop-with-continuous-section-break.docx",
|
|
data: {
|
|
loop: [1, 2, 3]
|
|
},
|
|
options: {
|
|
parser: angularParser
|
|
},
|
|
expectedName: "expected-loop-with-continuous-section-break-2.docx"
|
|
});
|
|
});
|
|
it('should work with w:type val="nextPage" section break', function () {
|
|
return this.render({
|
|
name: "nextpage-section-break.docx",
|
|
data: {
|
|
items: [1, 2, 3, 4]
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-nextpage-section-break.docx"
|
|
});
|
|
});
|
|
});
|
|
describe("ParagraphLoop", function () {
|
|
it("should work with docx", function () {
|
|
return this.render({
|
|
name: "users.docx",
|
|
data: {
|
|
users: ["John", "Jane", "Louis"]
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-users.docx"
|
|
});
|
|
});
|
|
it("should not drop image with text", function () {
|
|
return this.render({
|
|
name: "cond-image.docx",
|
|
data: {
|
|
cond: true
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-cond-image.docx"
|
|
});
|
|
});
|
|
it("should not drop image without text", function () {
|
|
return this.render({
|
|
name: "cond-image-no-innertext.docx",
|
|
data: {
|
|
cond: true
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-cond-image-no-innertext.docx"
|
|
});
|
|
});
|
|
it("should not drop image without text at beginning", function () {
|
|
return this.render({
|
|
name: "cond-image-no-innertext-before.docx",
|
|
data: {
|
|
cond: true
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-cond-image-no-innertext-before.docx"
|
|
});
|
|
});
|
|
it("should work without removing extra text", function () {
|
|
return this.render({
|
|
name: "paragraph-loops.docx",
|
|
data: {
|
|
condition: [1, 2],
|
|
l1: [{
|
|
l2: ["a", "b", "c"]
|
|
}, {
|
|
l2: ["d", "e", "f"]
|
|
}],
|
|
placeholder: "placeholder-value"
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-paragraph-loop.docx"
|
|
});
|
|
});
|
|
it("should work with pptx", function () {
|
|
return this.render({
|
|
name: "paragraph-loop.pptx",
|
|
data: {
|
|
users: [{
|
|
age: 10,
|
|
name: "Bar"
|
|
}, {
|
|
age: 18,
|
|
name: "Bar"
|
|
}, {
|
|
age: 22,
|
|
name: "Bar"
|
|
}]
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-paragraph-loop.pptx"
|
|
});
|
|
});
|
|
it("should fail if trying to attach a module that has none of the properties", function () {
|
|
var expectedError = {
|
|
name: "InternalError",
|
|
message: "This module cannot be wrapped, because it doesn't define any of the necessary functions",
|
|
properties: {
|
|
id: "module_cannot_be_wrapped"
|
|
}
|
|
};
|
|
expectToThrow(function () {
|
|
createDocV4("regression-par-in-par.docx", {
|
|
modules: [Promise.resolve(1)]
|
|
});
|
|
}, Error, expectedError);
|
|
});
|
|
it("should not produce corrupt document when having comment inside tag", function () {
|
|
return this.renderV4({
|
|
name: "tag-with-comment.docx",
|
|
expectedName: "expected-tag-with-comment.docx"
|
|
});
|
|
});
|
|
it("should not fail when having paragraph in paragraph", function () {
|
|
var printedPostparsed = [];
|
|
var filePath = "";
|
|
var doc = createDoc("regression-par-in-par.docx").attachModule({
|
|
name: "MyModule",
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}).setOptions({
|
|
paragraphLoop: true,
|
|
parser: function parser() {
|
|
return {
|
|
get: function get() {
|
|
return "foo";
|
|
}
|
|
};
|
|
}
|
|
}).render();
|
|
shouldBeSame({
|
|
doc: doc,
|
|
expectedName: "expected-rendered-par-in-par.docx"
|
|
});
|
|
expect(printedPostparsed["word/document.xml"]).to.matchSnapshot();
|
|
});
|
|
it("should work with spacing at the end", function () {
|
|
return this.render({
|
|
name: "spacing-end.docx",
|
|
data: {
|
|
name: "John"
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
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", {
|
|
paragraphLoop: true,
|
|
parser: angularParser
|
|
});
|
|
var catched = false;
|
|
var capture = captureLogs();
|
|
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"
|
|
}
|
|
};
|
|
capture.stop();
|
|
expectToThrow(function () {
|
|
return doc.render();
|
|
}, Errors.XTInternalError, expectedError);
|
|
}
|
|
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,
|
|
errorLogging: false,
|
|
parser: angularParser
|
|
});
|
|
var expectedError = {
|
|
message: "Multi error",
|
|
name: "TemplateError",
|
|
properties: {
|
|
id: "multi_error",
|
|
errors: [{
|
|
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]
|
|
}
|
|
}, {
|
|
name: "TemplateError",
|
|
message: "Unclosed tag",
|
|
properties: {
|
|
file: "word/footer1.xml",
|
|
xtag: "footer",
|
|
id: "unclosed_tag",
|
|
context: "{footer",
|
|
offset: 2
|
|
}
|
|
}]
|
|
}
|
|
};
|
|
expectToThrow(function () {
|
|
return doc.render();
|
|
}, Errors.XTTemplateError, expectedError);
|
|
});
|
|
it("should fail properly when having lexed + postparsed errors", function () {
|
|
var doc = createDoc("multi-errors.docx", {
|
|
paragraphLoop: true,
|
|
errorLogging: false,
|
|
parser: angularParser
|
|
});
|
|
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
|
|
}
|
|
}]
|
|
}
|
|
};
|
|
expectToThrow(function () {
|
|
return doc.compile();
|
|
}, Errors.XTTemplateError, expectedError);
|
|
});
|
|
it("should fail when placing paragraph loop inside normal loop", function () {
|
|
var doc = createDoc("paragraph-loop-error.docx", {
|
|
errorLogging: false
|
|
});
|
|
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: 19,
|
|
expandTo: "w:p",
|
|
inverted: false,
|
|
lIndex: 19,
|
|
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: 23,
|
|
lIndex: 23,
|
|
location: "end",
|
|
module: "loop",
|
|
offset: 26,
|
|
raw: "/",
|
|
type: "placeholder",
|
|
value: ""
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
};
|
|
expectToThrow(function () {
|
|
return doc.compile();
|
|
}, 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 = makeDocxV4(content, {
|
|
modules: [{
|
|
optionsTransformer: function optionsTransformer(options, doc) {
|
|
doc.modules.forEach(function (module) {
|
|
if (module.name === "LoopModule") {
|
|
module.prefix.start = "##";
|
|
}
|
|
});
|
|
return options;
|
|
}
|
|
}]
|
|
});
|
|
doc.render(scope);
|
|
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 = makeDocxV4(content, {
|
|
modules: [{
|
|
optionsTransformer: function optionsTransformer(options, doc) {
|
|
doc.modules.forEach(function (module) {
|
|
if (module.name === "LoopModule") {
|
|
module.prefix.start = /^##?(.*)$/;
|
|
}
|
|
});
|
|
return options;
|
|
}
|
|
}]
|
|
});
|
|
doc.render(scope);
|
|
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 = makeDocxV4(content, {
|
|
modules: [{
|
|
optionsTransformer: function optionsTransformer(options, doc) {
|
|
doc.modules.forEach(function (module) {
|
|
if (module.name === "RawXmlModule") {
|
|
module.prefix = /^!!?(.*)$/;
|
|
}
|
|
});
|
|
return options;
|
|
}
|
|
}]
|
|
});
|
|
doc.render(scope);
|
|
expect(doc.getFullText()).to.be.equal("HoHo");
|
|
});
|
|
});
|
|
describe("Load Office 365 file", function () {
|
|
it("should handle files with word/document2.xml", function () {
|
|
return this.renderV4({
|
|
name: "office365.docx",
|
|
data: {
|
|
test: "Value",
|
|
test2: "Value2"
|
|
},
|
|
options: {
|
|
paragraphLoop: true
|
|
},
|
|
expectedName: "expected-office365.docx",
|
|
expectedText: "Value Value2"
|
|
});
|
|
});
|
|
it("should template header.xml (without digit like header1.xml)", function () {
|
|
return this.render({
|
|
name: "header-without-digit.docx",
|
|
data: {
|
|
name: "John"
|
|
},
|
|
expectedName: "expected-header-without-digit.docx"
|
|
});
|
|
});
|
|
});
|
|
describe("Loops", function () {
|
|
it("should work with template", function () {
|
|
return this.renderV4({
|
|
name: "empty-loop-regression.docx",
|
|
data: {
|
|
ice: [1, 2, 3]
|
|
},
|
|
expectedName: "expected-loop-regression.docx"
|
|
});
|
|
});
|
|
}); |