docxtemplater
Version:
.docx generator working with templates and data (like Mustache)
600 lines (571 loc) • 29.4 kB
JavaScript
const testUtils = require("./utils");
const expect = testUtils.expect;
const JSZip = require("jszip");
const Docxtemplater = require("../docxtemplater.js");
const DocUtils = require("../doc-utils.js");
const _ = require("lodash");
const inspectModule = require("./inspect-module.js");
const expressions = require("angular-expressions");
function angularParser(tag) {
const expr = expressions.compile(tag);
return {
get(scope) {
return expr(scope);
},
};
}
describe("DocxtemplaterBasis", function () {
it("should be defined", function () {
expect(Docxtemplater).not.to.be.equal(undefined);
});
it("should construct", function () {
const doc = new Docxtemplater();
expect(doc).not.to.be.equal(undefined);
});
});
function getLength(obj) {
if (obj instanceof ArrayBuffer) {
return obj.byteLength;
}
return obj.length;
}
describe("DocxtemplaterLoading", function () {
describe("ajax done correctly", function () {
it("doc and img Data should have the expected length", function () {
const doc = testUtils.createDoc("image-example.docx");
expect(getLength(doc.loadedContent)).to.be.equal(729580);
expect(getLength(testUtils.imageData["image.png"])).to.be.equal(18062);
});
it("should have the right number of files (the docx unzipped)", function () {
const doc = testUtils.createDoc("image-example.docx");
expect(DocUtils.sizeOfObject(doc.zip.files)).to.be.equal(16);
});
});
describe("basic loading", function () {
it("should load file image-example.docx", function () {
const doc = testUtils.createDoc("image-example.docx");
expect(typeof doc).to.be.equal("object");
});
});
describe("content_loading", function () {
it("should load the right content for the footer", function () {
const doc = testUtils.createDoc("image-example.docx");
const fullText = (doc.getFullText("word/footer1.xml"));
expect(fullText.length).not.to.be.equal(0);
expect(fullText).to.be.equal("{last_name}{first_name}{phone}");
});
it("should load the right content for the document", function () {
const doc = testUtils.createDoc("image-example.docx");
// default value document.xml
const fullText = (doc.getFullText());
expect(fullText).to.be.equal("");
});
it("should load the right template files for the document", function () {
const doc = testUtils.createDoc("tag-example.docx");
const templatedFiles = (doc.getTemplatedFiles());
expect(templatedFiles).to.be.eql(["word/header1.xml", "word/footer1.xml", "word/document.xml"]);
});
});
describe("output and input", function () {
it("should be the same", function () {
const zip = new JSZip(testUtils.createDoc("tag-example.docx").loadedContent);
const doc = new Docxtemplater().loadZip(zip);
const output = doc.getZip().generate({type: "base64"});
expect(output.length).to.be.equal(90732);
expect(output.substr(0, 50)).to.be.equal("UEsDBAoAAAAAAAAAIQAMTxYSlgcAAJYHAAATAAAAW0NvbnRlbn");
});
});
});
describe("DocxtemplaterTemplating", function () {
describe("text templating", function () {
it("should change values with template data", function () {
const tags = {
first_name: "Hipp",
last_name: "Edgar",
phone: "0652455478",
description: "New Website",
};
const doc = testUtils.createDoc("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");
testUtils.shouldBeSame({doc, expectedName: "tag-example-expected.docx"});
});
});
});
describe("inspect module", function () {
function getTags(postParsed) {
return postParsed.filter(function (part) {
return part.type === "placeholder";
}).reduce(function (tags, part) {
tags[part.value] = {};
if (part.subparsed) {
tags[part.value] = getTags(part.subparsed);
}
return tags;
}, {});
}
const doc = testUtils.createDoc("tag-loop-example.docx");
const iModule = inspectModule();
doc.attachModule(iModule);
doc.render();
const postParsed = iModule.fullInspected["word/document.xml"].postparsed;
const tags = getTags(postParsed);
expect(tags).to.be.deep.equal({
offre: {
nom: {},
prix: {},
titre: {},
},
nom: {},
prenom: {},
});
});
describe("DocxtemplaterTemplatingForLoop", function () {
describe("textLoop templating", function () {
it("should replace all the tags", function () {
const tags = {
nom: "Hipp",
prenom: "Edgar",
telephone: "0652455478",
description: "New Website",
offre: [{titre: "titre1", prix: "1250"}, {titre: "titre2", prix: "2000"}, {titre: "titre3", prix: "1400", nom: "Offre"}],
};
const doc = testUtils.createDoc("tag-loop-example.docx");
doc.setData(tags);
doc.render();
expect(doc.getFullText()).to.be.equal("Votre proposition commercialeHippPrix: 1250Titre titre1HippPrix: 2000Titre titre2OffrePrix: 1400Titre titre3HippEdgar");
});
it("should work with loops inside loops", function () {
const tags = {products: [{title: "Microsoft", name: "DOS", reference: "Win7", avantages: [{title: "Everyone uses it", proof: [{reason: "it is quite cheap"}, {reason: "it is quit simple"}, {reason: "it works on a lot of different Hardware"}]}]}, {title: "Linux", name: "Ubuntu", reference: "Ubuntu10", avantages: [{title: "It's very powerful", proof: [{reason: "the terminal is your friend"}, {reason: "Hello world"}, {reason: "it's free"}]}]}, {title: "Apple", name: "Mac", reference: "OSX", avantages: [{title: "It's very easy", proof: [{reason: "you can do a lot just with the mouse"}, {reason: "It's nicely designed"}]}]}]};
const doc = testUtils.createDoc("tag-produit-loop.docx");
doc.setData(tags);
doc.render();
const text = doc.getFullText();
const expectedText = "MicrosoftProduct name : DOSProduct reference : Win7Everyone uses itProof that it works nicely : It works because it is quite cheap It works because it is quit simple It works because it works on a lot of different HardwareLinuxProduct name : UbuntuProduct reference : Ubuntu10It's very powerfulProof that it works nicely : It works because the terminal is your friend It works because Hello world It works because it's freeAppleProduct name : MacProduct reference : OSXIt's very easyProof that it works nicely : It works because you can do a lot just with the mouse It works because It's nicely designed";
expect(text.length).to.be.equal(expectedText.length);
expect(text).to.be.equal(expectedText);
});
it("should not have sideeffects with inverted with array length 3", function () {
const content = "<w:t>{^todos}No {/todos}Todos</w:t><w:t>{#todos}{.}{/todos}</w:t>";
const expectedContent = '<w:t>Todos</w:t><w:t xml:space="preserve">ABC</w:t>';
const scope = {todos: ["A", "B", "C"]};
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope});
xmlTemplater.render();
expect(xmlTemplater.content).to.be.deep.equal(expectedContent);
});
it("should not have sideeffects with inverted with empty array", function () {
const content = `<w:t>{^todos}No {/todos}Todos</w:t>
<w:t>{#todos}{.}{/todos}</w:t>`;
const expectedContent = `<w:t>No Todos</w:t>
<w:t xml:space="preserve"></w:t>`;
const scope = {todos: []};
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope});
xmlTemplater.render();
expect(xmlTemplater.content).to.be.deep.equal(expectedContent);
});
it("should provide inverted loops", function () {
const content = "<w:t>{^products}No products found{/products}</w:t>";
[
{products: []},
{products: false},
{},
].forEach(function (tags) {
const doc = testUtils.createXmlTemplaterDocx(content, {tags});
doc.render();
expect(doc.getFullText()).to.be.equal("No products found");
});
[
{products: [{name: "Bread"}]},
{products: true},
{products: "Bread"},
{products: {name: "Bread"}},
].forEach(function (tags) {
const doc = testUtils.createXmlTemplaterDocx(content, {tags});
doc.render();
expect(doc.getFullText()).to.be.equal("");
});
});
it("should be possible to close loops with {/}", function () {
const content = "<w:t>{#products}Product {name}{/}</w:t>";
const tags = {products: [{name: "Bread"}]};
const doc = testUtils.createXmlTemplaterDocx(content, {tags});
doc.render();
expect(doc.getFullText()).to.be.equal("Product Bread");
});
it("should be possible to close double loops with {/}", function () {
const content = "<w:t>{#companies}{#products}Product {name}{/}{/}</w:t>";
const tags = {companies: [{products: [{name: "Bread"}]}]};
const doc = testUtils.createXmlTemplaterDocx(content, {tags});
doc.render();
expect(doc.getFullText()).to.be.equal("Product Bread");
});
it("should work with complex loops", function () {
const content = "<w:t>{title} {#users} {name} friends are : {#friends} {.</w:t>TAG..TAG<w:t>},{/friends} {/users</w:t>TAG2<w:t>}</w:t>";
const scope = {title: "###Title###", users: [{name: "John Doe", friends: ["Jane", "Henry"]}, {}], name: "Default", friends: ["None"]};
const doc = testUtils.createXmlTemplaterDocx(content, {tags: scope});
doc.render();
expect(doc.getFullText()).to.be.equal("###Title### John Doe friends are : Jane, Henry, Default friends are : None, ");
});
});
});
describe("Dash Loop Testing", function () {
it("dash loop ok on simple table -> w:tr", function () {
const tags = {os: [{type: "linux", price: "0", reference: "Ubuntu10"}, {type: "DOS", price: "500", reference: "Win7"}, {type: "apple", price: "1200", reference: "MACOSX"}]};
const doc = testUtils.createDoc("tag-dash-loop.docx");
doc.setData(tags);
doc.render();
const expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX";
const text = doc.getFullText();
expect(text).to.be.equal(expectedText);
});
it("dash loop ok on simple table -> w:table", function () {
const tags = {os: [{type: "linux", price: "0", reference: "Ubuntu10"}, {type: "DOS", price: "500", reference: "Win7"}, {type: "apple", price: "1200", reference: "MACOSX"}]};
const doc = testUtils.createDoc("tag-dash-loop-table.docx");
doc.setData(tags);
doc.render();
const expectedText = "linux0Ubuntu10DOS500Win7apple1200MACOSX";
const text = doc.getFullText();
expect(text).to.be.equal(expectedText);
});
it("dash loop ok on simple list -> w:p", function () {
const tags = {os: [{type: "linux", price: "0", reference: "Ubuntu10"}, {type: "DOS", price: "500", reference: "Win7"}, {type: "apple", price: "1200", reference: "MACOSX"}]};
const doc = testUtils.createDoc("tag-dash-loop-list.docx");
doc.setData(tags);
doc.render();
const expectedText = "linux 0 Ubuntu10 DOS 500 Win7 apple 1200 MACOSX ";
const text = doc.getFullText();
expect(text).to.be.equal(expectedText);
});
});
describe("Intelligent Loop Tagging", function () {
it("should work with tables", function () {
const 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"}]};
const doc = testUtils.createDoc("tag-intelligent-loop-table.docx");
doc.setData(tags);
doc.render();
const expectedText = "JohnDoe+33647874513JaneDoe+33454540124PhilKiel+44578451245DaveSto+44548787984";
const text = doc.getFullText();
expect(text).to.be.equal(expectedText);
testUtils.shouldBeSame({doc, expectedName: "tag-intelligent-loop-table-expected.docx"});
});
it("should not do anything special when loop outside of table", function () {
["<w:t>{#tables}</w:t><w:table><w:tr><w:tc><w:t>{user}</w:t></w:tc></w:tr></w:table><w:t>{/tables}</w:t>"].forEach(function (content) {
const scope = {
tables: [
{user: "John"},
{user: "Jane"},
],
};
const doc = testUtils.createXmlTemplaterDocx(content, {tags: scope});
doc.render();
expect(doc.content).to.be.equal('<w:t></w:t><w:table><w:tr><w:tc><w:t xml:space="preserve">John</w:t></w:tc></w:tr></w:table><w:t></w:t><w:table><w:tr><w:tc><w:t xml:space="preserve">Jane</w:t></w:tc></w:tr></w:table><w:t></w:t>');
});
});
});
describe("Changing the parser", function () {
it("should work with uppercassing", function () {
const content = "<w:t>Hello {name}</w:t>";
const scope = {name: "Edgar"};
function parser(tag) {
return {["get"](scope) { return scope[tag].toUpperCase(); }};
}
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope, parser});
xmlTemplater.render();
expect(xmlTemplater.getFullText()).to.be.equal("Hello EDGAR");
});
it("should work when setting from the Docxtemplater interface", function () {
const doc = testUtils.createDoc("tag-example.docx");
const zip = new JSZip(doc.loadedContent);
const d = new Docxtemplater().loadZip(zip);
const tags = {
first_name: "Hipp",
last_name: "Edgar",
phone: "0652455478",
description: "New Website",
};
d.setData(tags);
d.parser = function (tag) {
return {["get"](scope) { return scope[tag].toUpperCase(); }};
};
d.render();
expect(d.getFullText()).to.be.equal("EDGAR HIPP");
expect(d.getFullText("word/header1.xml")).to.be.equal("EDGAR HIPP0652455478NEW WEBSITE");
expect(d.getFullText("word/footer1.xml")).to.be.equal("EDGARHIPP0652455478");
});
it("should work with angular parser", function () {
const tags = {person: {first_name: "Hipp", last_name: "Edgar", birth_year: 1955, age: 59}};
const doc = testUtils.createDoc("angular-example.docx");
doc.setData(tags);
doc.parser = angularParser;
doc.render();
expect(doc.getFullText()).to.be.equal("Hipp Edgar 2014");
});
it("should work with loops", function () {
const content = "<w:t>Hello {#person.adult}you{/person.adult}</w:t>";
const scope = {person: {name: "Edgar", adult: true}};
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope, parser: angularParser});
xmlTemplater.render();
expect(xmlTemplater.getFullText()).to.be.equal("Hello you");
});
});
describe("Special characters", function () {
it("should parse placeholder containing special characters", function () {
const content = "<w:t>Hello {>name}</w:t>";
const scope = {">name": "Edgar"};
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope});
xmlTemplater.render();
expect(xmlTemplater.content).to.be.deep.equal('<w:t xml:space="preserve">Hello Edgar</w:t>');
});
it("should render placeholder containing special characters", function () {
const content = "<w:t>Hello {name}</w:t>";
const scope = {name: "<Edgar>"};
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope});
xmlTemplater.render();
expect(xmlTemplater.content).to.be.deep.equal('<w:t xml:space="preserve">Hello <Edgar></w:t>');
});
it("should read full text correctly", function () {
const doc = testUtils.createDoc("cyrillic.docx");
const fullText = doc.getFullText();
expect(fullText.charCodeAt(0)).to.be.equal(1024);
expect(fullText.charCodeAt(1)).to.be.equal(1050);
expect(fullText.charCodeAt(2)).to.be.equal(1048);
expect(fullText.charCodeAt(3)).to.be.equal(1046);
expect(fullText.charCodeAt(4)).to.be.equal(1044);
expect(fullText.charCodeAt(5)).to.be.equal(1045);
expect(fullText.charCodeAt(6)).to.be.equal(1039);
expect(fullText.charCodeAt(7)).to.be.equal(1040);
});
it("should still read full text after applying tags", function () {
const doc = testUtils.createDoc("cyrillic.docx");
doc.setData({name: "Edgar"});
doc.render();
const fullText = doc.getFullText();
expect(fullText.charCodeAt(0)).to.be.equal(1024);
expect(fullText.charCodeAt(1)).to.be.equal(1050);
expect(fullText.charCodeAt(2)).to.be.equal(1048);
expect(fullText.charCodeAt(3)).to.be.equal(1046);
expect(fullText.charCodeAt(4)).to.be.equal(1044);
expect(fullText.charCodeAt(5)).to.be.equal(1045);
expect(fullText.charCodeAt(6)).to.be.equal(1039);
expect(fullText.charCodeAt(7)).to.be.equal(1040);
expect(fullText.indexOf("Edgar")).to.be.equal(9);
});
it("should insert russian characters", function () {
const russianText = [1055, 1091, 1087, 1082, 1080, 1085, 1072];
const russian = russianText.map(function (char) {
return String.fromCharCode(char);
}).join("");
const doc = testUtils.createDoc("tag-example.docx");
const zip = new JSZip(doc.loadedContent);
const d = new Docxtemplater().loadZip(zip);
d.setData({last_name: russian});
d.render();
const outputText = d.getFullText();
expect(outputText.substr(0, 7)).to.be.equal(russian);
});
});
describe("Complex table example", function () {
it("should work with simple table", function () {
const doc = testUtils.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();
const 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 () {
// set the templateData
const doc = testUtils.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();
const 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 inside tables", function () {
const tags = {
table1: [1],
key: "value",
};
const template = `<w:tr>
<w:tc><w:t>{#table1}Hi</w:t></w:tc>
<w:tc><w:t>{/table1}</w:t> </w:tc>
</w:tr>
<w:tr>
<w:tc><w:t>{#table1}Ho</w:t></w:tc>
<w:tc><w:p><w:t>{/table1}</w:t> </w:p>
</w:tc>
</w:tr>
<w:t>{key}</w:t>
`;
const doc = testUtils.createXmlTemplaterDocx(template, {tags});
doc.render();
const fullText = doc.getFullText();
expect(fullText).to.be.equal("HiHovalue");
const expected = `<w:tr>
<w:tc><w:t>Hi</w:t></w:tc>
<w:tc><w:t></w:t> </w:tc>
</w:tr>
<w:tr>
<w:tc><w:t>Ho</w:t></w:tc>
<w:tc><w:p><w:t></w:t> </w:p>
</w:tc>
</w:tr>
<w:t xml:space="preserve">value</w:t>
`;
expect(doc.content).to.be.equal(expected);
});
});
describe("Raw Xml Insertion", function () {
it("should work with simple example", function () {
const inner = "<w:p><w:r><w:t>{@complexXml}</w:t></w:r></w:p>";
const content = `<w:document>${inner}</w:document>`;
const scope = {complexXml: "<w:p w:rsidR=\"00612058\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r><w:rPr><w: color w: val=\"FF0000\"/></w:rPr><w:t>My custom XML</w:t></w:r></w:p><w:tbl><w:tblPr><w:tblStyle w: val=\"Grilledutableau\"/><w:tblW w: w=\"0\" w:type=\"auto\"/><w:tblLook w: val=\"04A0\" w: firstRow=\"1\" w: lastRow=\"0\" w: firstColumn=\"1\" w: lastColumn=\"0\" w: noHBand=\"0\" w: noVBand=\"1\"/></w:tblPr><w:tblGrid><w: gridCol w: w=\"2952\"/><w: gridCol w: w=\"2952\"/><w: gridCol w: w=\"2952\"/></w:tblGrid><w:tr w:rsidR=\"00EA4B08\" w:rsidTr=\"00EA4B08\"><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"DDD9C3\" w:themeFill=\"background2\" w:themeFillShade=\"E6\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRPr=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: b/><w: color w: val=\"000000\" w:themeColor=\"text1\"/></w:rPr></w:pPr><w:r><w:rPr><w: b/><w: color w: val=\"000000\" w:themeColor=\"text1\"/></w:rPr><w:t>Test</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"DDD9C3\" w:themeFill=\"background2\" w:themeFillShade=\"E6\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRPr=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: b/><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r><w:rPr><w: b/><w: color w: val=\"FF0000\"/></w:rPr><w:t>Xml</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"DDD9C3\" w:themeFill=\"background2\" w:themeFillShade=\"E6\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r><w:rPr><w: color w: val=\"FF0000\"/></w:rPr><w:t>Generated</w:t></w:r></w:p></w:tc></w:tr><w:tr w:rsidR=\"00EA4B08\" w:rsidTr=\"00EA4B08\"><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"C6D9F1\" w:themeFill=\"text2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRPr=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"000000\" w:themeColor=\"text1\"/><w: u w: val=\"single\"/></w:rPr></w:pPr><w:r w:rsidRPr=\"00EA4B08\"><w:rPr><w: color w: val=\"000000\" w:themeColor=\"text1\"/><w: u w: val=\"single\"/></w:rPr><w:t>Underline</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"C6D9F1\" w:themeFill=\"text2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r w:rsidRPr=\"00EA4B08\"><w:rPr><w: color w: val=\"FF0000\"/><w: highlight w: val=\"yellow\"/></w:rPr><w:t>Highlighting</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"C6D9F1\" w:themeFill=\"text2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRPr=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w:rFonts w: ascii=\"Bauhaus 93\" w: hAnsi=\"Bauhaus 93\"/><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r w:rsidRPr=\"00EA4B08\"><w:rPr><w:rFonts w: ascii=\"Bauhaus 93\" w: hAnsi=\"Bauhaus 93\"/><w: color w: val=\"FF0000\"/></w:rPr><w:t>Font</w:t></w:r></w:p></w:tc></w:tr><w:tr w:rsidR=\"00EA4B08\" w:rsidTr=\"00EA4B08\"><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"F2DBDB\" w:themeFill=\"accent2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00EA4B08\"><w:pPr><w: jc w: val=\"center\"/><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r><w:rPr><w: color w: val=\"FF0000\"/></w:rPr><w:t>Centering</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"F2DBDB\" w:themeFill=\"accent2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRPr=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: i/><w: color w: val=\"FF0000\"/></w:rPr></w:pPr><w:r w:rsidRPr=\"00EA4B08\"><w:rPr><w: i/><w: color w: val=\"FF0000\"/></w:rPr><w:t>Italic</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"F2DBDB\" w:themeFill=\"accent2\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc></w:tr><w:tr w:rsidR=\"00EA4B08\" w:rsidTr=\"00EA4B08\"><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"E5DFEC\" w:themeFill=\"accent4\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"E5DFEC\" w:themeFill=\"accent4\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"E5DFEC\" w:themeFill=\"accent4\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc></w:tr><w:tr w:rsidR=\"00EA4B08\" w:rsidTr=\"00EA4B08\"><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"FDE9D9\" w:themeFill=\"accent6\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"FDE9D9\" w:themeFill=\"accent6\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc><w:tc><w:tcPr><w:tcW w: w=\"2952\" w:type=\"dxa\"/><w: shd w: val=\"clear\" w: color=\"auto\" w: fill=\"FDE9D9\" w:themeFill=\"accent6\" w:themeFillTint=\"33\"/></w:tcPr><w:p w:rsidR=\"00EA4B08\" w:rsidRDefault=\"00EA4B08\" w:rsidP=\"00612058\"><w:pPr><w:rPr><w: color w: val=\"FF0000\"/></w:rPr></w:pPr></w:p></w:tc></w:tr></w:tbl>"};
const doc = testUtils.createXmlTemplaterDocx(content, {tags: scope});
doc.render();
expect(doc.content.length).to.be.equal(content.length + scope.complexXml.length - (inner.length));
expect(doc.content).to.contain(scope.complexXml);
});
it("should work even when tags are after the xml", function () {
const content = `<w:tbl>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>{}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>{name}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>{first_name}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>{#products} {year}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>{name}</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>{company}{/products}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>
`;
const scope = {
complexXml: "<w:p><w:r><w:t>Hello</w:t></w:r></w:p>",
name: "John",
first_name: "Doe",
products: [
{year: 1550, name: "Moto", company: "Fein"},
{year: 1987, name: "Water", company: "Test"},
{year: 2010, name: "Bread", company: "Yu"},
],
};
const doc = testUtils.createXmlTemplaterDocx(content, {tags: scope});
doc.render();
expect(doc.content).to.contain(scope.complexXml);
expect(doc.getFullText()).to.be.equal("HelloJohnDoe 1550MotoFein 1987WaterTest 2010BreadYu");
});
it("should work with closing tag in the form of <w:t>}{/body}</w:t>", function () {
const scope = {body: [{paragraph: "hello"}]};
const content = `<w:t>{#body}</w:t>
<w:t>{paragraph</w:t>
<w:t>}{/body}</w:t>`;
const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: scope});
xmlTemplater.render();
expect(xmlTemplater.content).not.to.contain("</w:t></w:t>");
});
it("should work with simple example and given options", function () {
const scope = {xmlTag: '<w:r><w:rPr><w:color w:val="FF0000"/></w:rPr><w:t>My custom</w:t></w:r><w:r><w:rPr><w:color w:val="00FF00"/></w:rPr><w:t>XML</w:t></w:r>'};
const doc = testUtils.createDoc("one-raw-xml-tag.docx");
doc.setOptions({
fileTypeConfig: _.merge({}, Docxtemplater.FileTypeConfig.docx, {tagRawXml: "w:r"}),
});
doc.setData(scope);
doc.render();
expect(doc.getFullText()).to.be.equal("asdfMy customXMLqwery");
});
});
describe("pptx generation", function () {
it("should work with simple pptx", function () {
const doc = testUtils.createDoc("simple-example.pptx");
const p = doc.setData({name: "Edgar"}).render();
expect(p.getFullText()).to.be.equal("Hello Edgar");
});
});
describe("Serialization", function () {
it("should be serialiazable", function () {
const doc = testUtils.createDoc("tag-example.docx");
JSON.stringify(doc);
});
});