2021-09-04 10:15:02 +05:00

349 lines
11 KiB
JavaScript

const {
expectToThrow,
createDoc,
shouldBeSame,
isNode12,
createDocV4,
} = require("./utils");
const Errors = require("../errors.js");
const { expect } = require("chai");
const { xml2str, traits } = require("../doc-utils");
describe("Verify apiversion", function () {
it("should work with valid api version", function () {
const module = {
requiredAPIVersion: "3.23.0",
render(part) {
return part.value;
},
};
const doc = createDoc("loop-valid.docx");
doc.attachModule(module);
});
it("should fail with invalid api version", function () {
const module = {
requiredAPIVersion: "3.92.0",
render(part) {
return part.value;
},
};
const doc = createDoc("loop-valid.docx");
expectToThrow(() => doc.attachModule(module), Errors.XTAPIVersionError, {
message:
"The minor api version is not uptodate, you probably have to update docxtemplater with npm install --save docxtemplater",
name: "APIVersionError",
properties: {
id: "api_version_error",
currentModuleApiVersion: [3, 24, 0],
neededVersion: [3, 92, 0],
},
});
});
});
describe("Module attachment", function () {
it("should not allow to attach the same module twice", function () {
const module = {
name: "TestModule",
requiredAPIVersion: "3.0.0",
render(part) {
return part.value;
},
};
const doc1 = createDoc("loop-valid.docx");
doc1.attachModule(module);
const doc2 = createDoc("tag-example.docx");
let errMessage = null;
try {
doc2.attachModule(module);
} catch (e) {
errMessage = e.message;
}
expect(errMessage).to.equal(
'Cannot attach a module that was already attached : "TestModule". Maybe you are instantiating the module at the root level, and using it for multiple instances of Docxtemplater'
);
});
});
describe("Module xml parse", function () {
it("should not mutate options (regression for issue #526)", function () {
const module = {
requiredAPIVersion: "3.0.0",
optionsTransformer(options, docxtemplater) {
const relsFiles = docxtemplater.zip
.file(/document.xml.rels/)
.map((file) => file.name);
options.xmlFileNames = options.xmlFileNames.concat(relsFiles);
return options;
},
};
const doc = createDoc("tag-example.docx");
const opts = {};
doc.setOptions(opts);
doc.attachModule(module);
doc.compile();
expect(opts).to.deep.equal({});
});
it("should be possible to parse xml files", function () {
let xmlDocuments;
const module = {
requiredAPIVersion: "3.0.0",
optionsTransformer(options, docxtemplater) {
const relsFiles = docxtemplater.zip
.file(/document.xml.rels/)
.map((file) => file.name);
options.xmlFileNames = options.xmlFileNames.concat(relsFiles);
return options;
},
set(options) {
if (options.xmlDocuments) {
xmlDocuments = options.xmlDocuments;
}
},
};
const doc = createDoc("tag-example.docx");
doc.attachModule(module);
doc.compile();
const xmlKeys = Object.keys(xmlDocuments);
expect(xmlKeys).to.deep.equal(["word/_rels/document.xml.rels"]);
const rels = xmlDocuments[
"word/_rels/document.xml.rels"
].getElementsByTagName("Relationship");
expect(rels.length).to.equal(10);
const str = xml2str(xmlDocuments["word/_rels/document.xml.rels"]);
if (isNode12()) {
expect(str).to
.equal(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer1.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header1.xml"/><Relationship Id="rId2" Type="http://schemas.microsoft.com/office/2007/relationships/stylesWithEffects" Target="stylesWithEffects.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes" Target="endnotes.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes" Target="footnotes.xml"/><Relationship Id="rId10" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/><Relationship Id="rId9" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/></Relationships>`);
rels[5].setAttribute("Foobar", "Baz");
doc.render();
shouldBeSame({ doc, expectedName: "expected-module-change-rels.docx" });
}
});
});
describe("Module unique tags xml", function () {
it("should not cause an issue if tagsXmlLexedArray contains duplicates", function () {
const module = {
requiredAPIVersion: "3.0.0",
optionsTransformer(options, docxtemplater) {
docxtemplater.fileTypeConfig.tagsXmlLexedArray.push(
"w:p",
"w:r",
"w:p"
);
return options;
},
};
const doc = createDoc("tag-example.docx");
doc.attachModule(module);
doc.setData({
first_name: "Hipp",
last_name: "Edgar",
phone: "0652455478",
description: "New Website",
});
doc.compile();
doc.render();
shouldBeSame({ doc, expectedName: "expected-tag-example.docx" });
});
});
describe("Module traits", function () {
it("should not cause an issue if using traits.expandTo containing loop", function () {
const moduleName = "comment-module";
function getInner({ part, leftParts, rightParts, postparse }) {
part.subparsed = postparse([].concat(leftParts).concat(rightParts), {
basePart: part,
});
return part;
}
const module = {
name: "Test module",
requiredAPIVersion: "3.0.0",
parse(placeHolderContent) {
if (placeHolderContent[0] === "£") {
const type = "placeholder";
return {
type,
value: placeHolderContent.substr(1),
module: moduleName,
};
}
},
postparse(parsed, { postparse }) {
parsed = traits.expandToOne(parsed, {
moduleName,
getInner,
expandTo: ["w:p"],
postparse,
});
return parsed;
},
render(part) {
if (part.module === moduleName) {
return {
value: "",
};
}
},
};
const doc = createDoc("comment-with-loop.docx");
doc.attachModule(module);
doc.setData({}).compile().render();
shouldBeSame({ doc, expectedName: "expected-comment-example.docx" });
});
});
describe("Module errors", function () {
it("should work", function () {
const moduleName = "ErrorModule";
const module = {
name: "Error module",
requiredAPIVersion: "3.0.0",
parse(placeHolderContent) {
const type = "placeholder";
return {
type,
value: placeHolderContent,
module: moduleName,
};
},
render(part) {
if (part.module === moduleName) {
return {
errors: [new Error(`foobar ${part.value}`)],
};
}
},
};
let error = null;
const doc = createDoc("tag-example.docx");
doc.attachModule(module);
doc.setData({}).compile();
try {
doc.render();
} catch (e) {
error = e;
}
expect(error).to.be.an("object");
expect(error.message).to.equal("Multi error");
expect(error.properties.errors.length).to.equal(9);
expect(error.properties.errors[0].message).to.equal("foobar last_name");
expect(error.properties.errors[1].message).to.equal("foobar first_name");
expect(error.properties.errors[2].message).to.equal("foobar phone");
});
});
describe("Module should pass options to module.parse, module.postparse, module.render, module.postrender", function () {
it("should pass filePath and contentType options", function () {
const doc = createDoc("tag-example.docx");
const filePaths = [];
let renderFP = "",
renderCT = "",
postrenderFP = "",
postrenderCT = "",
postparseFP = "",
postparseCT = "";
const ct = [];
const module = {
name: "Test module",
requiredAPIVersion: "3.0.0",
parse(a, options) {
filePaths.push(options.filePath);
ct.push(options.contentType);
},
postparse(a, options) {
postparseFP = options.filePath;
postparseCT = options.contentType;
return a;
},
render(a, options) {
renderFP = options.filePath;
renderCT = options.contentType;
},
postrender(a, options) {
postrenderFP = options.filePath;
postrenderCT = options.contentType;
return a;
},
};
doc.attachModule(module);
doc.setData({}).compile();
doc.render();
expect(renderFP).to.equal("word/document.xml");
expect(renderCT).to.equal(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
);
expect(postparseFP).to.equal("word/document.xml");
expect(postparseCT).to.equal(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
);
expect(postrenderFP).to.equal("word/document.xml");
expect(postrenderCT).to.equal(
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
);
expect(filePaths).to.deep.equal([
// Header appears 4 times because there are 4 tags in the header
"word/header1.xml",
"word/header1.xml",
"word/header1.xml",
"word/header1.xml",
// Footer appears 3 times because there are 3 tags in the header
"word/footer1.xml",
"word/footer1.xml",
"word/footer1.xml",
// Document appears 2 times because there are 2 tags in the header
"word/document.xml",
"word/document.xml",
]);
expect(ct).to.deep.equal([
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
]);
});
});
describe("Module detachment", function () {
it("should detach the module when the module does not support the document filetype", function () {
let isModuleCalled = false;
let isDetachedCalled = false;
const module = {
optionsTransformer(options) {
isModuleCalled = true;
return options;
},
on(eventName) {
if (eventName === "detached") {
isDetachedCalled = true;
}
},
supportedFileTypes: ["pptx"],
};
createDocV4("tag-example.docx", { modules: [module] });
expect(isDetachedCalled).to.equal(true);
expect(isModuleCalled).to.equal(false);
});
});