UNPKG

docxtemplater

Version:

.docx generator working with templates and data (like Mustache)

318 lines (303 loc) 9.18 kB
"use strict"; const testUtils = require("./utils"); const expect = testUtils.expect; const _ = require("lodash"); const expressions = require("angular-expressions"); function angularParser(tag) { const expr = expressions.compile(tag); return { get(scope) { return expr(scope); }, }; } const Errors = require("../errors.js"); function expectToThrow(fn, type, expectedError) { let e = null; try { fn(); } catch (error) { e = error; } expect(e, "No error has been thrown").not.to.be.equal(null); const toShowOnFail = e.stack; expect(e, toShowOnFail).to.be.instanceOf(Error); expect(e, toShowOnFail).to.be.instanceOf(type); expect(e, toShowOnFail).to.be.an("object"); expect(e, toShowOnFail).to.have.property("properties"); expect(e.properties, toShowOnFail).to.be.an("object"); expect(e.properties, toShowOnFail).to.have.property("explanation"); expect(e.properties.explanation, toShowOnFail).to.be.a("string"); expect(e.properties, toShowOnFail).to.have.property("id"); expect(e.properties.id, toShowOnFail).to.be.a("string"); expect(e.properties.explanation, toShowOnFail).to.be.a("string"); delete e.properties.explanation; e = _.omit(e, ["line", "sourceURL", "stack"]); if (e.properties.rootError) { expect(e.properties.rootError.message).to.equal(expectedError.properties.rootError.message); delete e.properties.rootError; delete expectedError.properties.rootError; } if (e.properties.paragraphParts) { expect(e.properties.paragraphParts.length).to.equal(expectedError.properties.paragraphPartsLength); delete e.properties.paragraphParts; delete expectedError.properties.paragraphPartsLength; } if (e.stack) { expect(e.stack).to.contain("Error: " + expectedError.message); delete e.stack; } expect(JSON.parse(JSON.stringify(e))).to.be.deep.equal(expectedError); } describe("errors", function () { it("should be thrown when unclosedtag", function () { const content = "<w:t>{unclosedtag my text</w:t>"; const tags = {}; const expectedError = { name: "TemplateError", message: "Unclosed tag", properties: { context: "{unclosedtag my text", id: "unclosed_tag", xtag: "unclosedtag", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should not be possible to close {#users} with {/foo}", function () { const content = "<w:t>{#users}User {name}{/foo}</w:t>"; const tags = {users: [{name: "John"}]}; const expectedError = { name: "TemplateError", message: "Closing tag does not match opening tag", properties: { id: "closing_tag_does_not_match_opening_tag", openingtag: "users", closingtag: "foo", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should be thrown when unopenedloop", function () { const content = "<w:t>{/loop} {foobar}</w:t>"; const scope = {}; const expectedError = { name: "TemplateError", message: "Unopened loop", properties: { id: "unopened_loop", xtag: "loop", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags: scope}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should be thrown when unclosedloop", function () { const content = "<w:t>{#loop} {foobar}</w:t>"; const scope = {}; const expectedError = { name: "TemplateError", message: "Unclosed loop", properties: { id: "unclosed_loop", xtag: "loop", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags: scope}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when rawtag not in paragraph", function () { const content = "<w:t>{@myrawtag}</w:t>"; const scope = {myrawtag: "<w:p><w:t>foobar</w:t></w:p>"}; const expectedError = { name: "TemplateError", message: "Raw tag not in paragraph", properties: { expandTo: "w:p", id: "raw_tag_outerxml_invalid", index: 1, postparsed: [ { position: "start", text: true, type: "tag", value: "<w:t>", }, { module: "rawxml", type: "placeholder", value: "myrawtag", }, { position: "end", text: true, type: "tag", value: "</w:t>", }, ], xtag: "myrawtag", rootError: { message: "No tag 'w:p' was found at the right", }, }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags: scope}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when rawtag not in paragraph (in table)", function () { const content = "<w:table><w:t>{@myrawtag}</w:t></w:p></w:table>"; const scope = {myrawtag: "<w:p><w:t>foobar</w:t></w:p>"}; const expectedError = { name: "TemplateError", message: "Raw tag not in paragraph", properties: { id: "raw_tag_outerxml_invalid", xtag: "myrawtag", postparsed: [ { type: "tag", position: "start", text: false, value: "<w:table>", }, { type: "tag", position: "start", text: true, value: "<w:t>", }, { type: "placeholder", value: "myrawtag", module: "rawxml", }, { type: "tag", position: "end", text: true, value: "</w:t>", }, { type: "tag", position: "end", text: false, value: "</w:p>", }, { type: "tag", position: "end", text: false, value: "</w:table>", }, ], rootError: { message: "No tag 'w:p' was found at the left", }, expandTo: "w:p", index: 2, }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags: scope}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when tag already opened", function () { const content = "<w:t>{user {name}</w:t>"; const expectedError = { name: "TemplateError", message: "Unclosed tag", properties: { id: "unclosed_tag", context: "{user ", xtag: "user", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when tag already closed", function () { const content = "<w:t>foobar}age</w:t>"; const expectedError = { name: "TemplateError", message: "Unopened tag", properties: { id: "unopened_tag", context: "foobar", xtag: "foobar", }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content); expectToThrow(create, Errors.XTTemplateError, expectedError); }); it("should fail when customparser fails to compile", function () { const content = "<w:t>{name++}</w:t>"; const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: {name: 3}, parser: angularParser}); const expectedError = { name: "ScopeParserError", message: "Scope parser compilation failed", properties: { id: "scopeparser_compilation_failed", tag: "name++", rootError: { message: "Syntax Error: Token 'undefined' not a primary expression at column NaN of the expression [name++] starting at [name++].", }, }, }; const create = xmlTemplater.render.bind(xmlTemplater); expectToThrow(create, Errors.XTScopeParserError, expectedError); }); it("should fail when customparser fails to execute", function () { const content = "<w:t>{name|upper}</w:t>"; function errorParser() { return { get() { throw new Error("foo bar"); }, }; } const xmlTemplater = testUtils.createXmlTemplaterDocx(content, {tags: {name: 3}, parser: errorParser}); const expectedError = { name: "ScopeParserError", message: "Scope parser execution failed", properties: { id: "scopeparser_execution_failed", tag: "name|upper", scope: {name: 3}, rootError: { message: "foo bar", }, }, }; const create = xmlTemplater.render.bind(xmlTemplater); expectToThrow(create, Errors.XTScopeParserError, expectedError); }); it("should fail when rawtag is not only text in paragraph", function () { const content = "<w:p><w:t>{@myrawtag}</w:t><w:t>foobar</w:t></w:p>"; const scope = {myrawtag: "<w:p><w:t>foobar</w:t></w:p>"}; const expectedError = { name: "TemplateError", message: "Raw tag should be the only text in paragraph", properties: { id: "raw_xml_tag_should_be_only_text_in_paragraph", xtag: "myrawtag", paragraphPartsLength: 6, }, }; const create = testUtils.createXmlTemplaterDocx.bind(null, content, {tags: scope}); expectToThrow(create, Errors.XTTemplateError, expectedError); }); describe("internal errors", function () { it("should fail", function () { const expectedError = { name: "InternalError", message: "Content must be a string", properties: {id: "xmltemplater_content_must_be_string"}, }; function test() { testUtils.createXmlTemplaterDocx(1); } expectToThrow(test, Errors.XTInternalError, expectedError); }); }); });