const { toXML } = require("./dist/jstoxml"); const assert = require("assert"); describe("toXML", () => { describe("primitives", () => { const vals = ["foo", false, true, 4, 4.56]; vals.forEach((val) => { it(`outputs ${val}`, () => { const result = toXML(val); const expectedResult = `${val}`; assert.equal(result, expectedResult); }); }); }); describe("functions", () => { describe("primitive outputs", () => { const vals = [999, "foo", false, true]; vals.forEach((val) => { it(`${val}`, () => { const result = toXML(() => val); const expectedResult = `${val}`; assert.equal(result, expectedResult); }); }); }); it("fat arrow", () => { const val = 888; const result = toXML(() => val); const expectedResult = val; assert.equal(result, expectedResult); }); it("accessing config within function", () => { const val = { foo: { depth: (config) => config.depth, }, }; const result = toXML(val); const expectedResult = "2"; assert.equal(result, expectedResult); }); it("converts nonprimitive output", () => { const val = { foo: "bar" }; const result = toXML(() => val); const expectedResult = "bar"; assert.equal(result, expectedResult); }); it("converts nested nonprimitive output", () => { const val = { foo: { bar: { baz: 2 } } }; const result = toXML(() => val); const expectedResult = "2"; assert.equal(result, expectedResult); }); it("converts nested nonprimitive output with indent", () => { const val = { foo: { bar: { baz: 2 } } }; const config = { indent: " " }; const result = toXML(() => val, config); const expectedResult = "\n \n 2\n \n"; assert.equal(result, expectedResult); }); }); describe("github issues", () => { it("issue 3", () => { const val = { foo: true, bar: "", foo2: false, ok: "This is ok", ok2: "false", ok3: "true", }; const result = toXML(val); const expectedResult = "truefalseThis is okfalsetrue"; assert.equal(result, expectedResult); }); }); describe("arrays", () => { it("1", () => { const val = [{ foo: "bar" }, { foo: "baz" }, { foo2: "bar2" }]; const result = toXML(val); const expectedResult = "barbazbar2"; assert.equal(result, expectedResult); }); it("attributes in subobject", () => { const val = [ { foo: "bar" }, { foo: "baz" }, { foo: undefined }, { foo: "" }, { foo: null }, { _name: "foo", _content: "bar", _attrs: { a: "b", c: "d", }, }, ]; const result = toXML(val); const expectedResult = 'barbazfoobar'; assert.equal(result, expectedResult); }); it("nesting with indent", () => { const val = { foo: [{ foo: "bar" }, { foo: "baz" }, { foo2: "bar2" }], }; const config = { indent: " " }; const result = toXML(val, config); const expectedResult = ` bar baz bar2 `; assert.equal(result, expectedResult); }); }); describe("special-objects", () => { it("1", () => { const val = { _name: "foo", _content: "bar", _attrs: { a: 1, b: 2, }, }; const result = toXML(val); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("2", () => { const val = { _name: "foo", _content: { foo: "bar", }, _attrs: { a: 1, b: 2, }, }; const result = toXML(val); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("3", () => { const val = { _name: "foo", _content: () => 1 + 2, _attrs: { a: 1, b: 2, }, }; const result = toXML(val); const expectedResult = '3'; assert.equal(result, expectedResult); }); }); describe("objects", () => { it("1", () => { const val = { foo: "bar", foo2: "bar2", }; const result = toXML(val); const expectedResult = "barbar2"; assert.equal(result, expectedResult); }); it("attributes", () => { const val = { _name: "a", _attrs: { foo: "bar", foo2: "bar2", }, }; const result = toXML(val); const expectedResult = ''; assert.equal(result, expectedResult); }); it("attributes 2", () => { const val = { _name: "a", _attrs: { foo: "bar", foo2: "bar2", }, _content: "la dee da", }; const result = toXML(val); const expectedResult = 'la dee da'; assert.equal(result, expectedResult); }); it("attributes nesting", () => { const val = { _name: "foo", _attrs: { a: "b", }, _content: { _name: "bar", _attrs: { c: "d", }, }, }; const result = toXML(val); const expectedResult = ''; assert.equal(result, expectedResult); }); it("with mixed content", () => { const val = { blah: null, foo: "bar", "more blah": null, bar: 0, "more more blah": null, baz: false, }; const result = toXML(val); const expectedResult = "blahbarmore blah0more more blahfalse"; assert.equal(result, expectedResult); }); it("nesting with indent", () => { const val = { foo: { foo: "bar", foo2: "bar2", }, }; const config = { indent: " " }; const result = toXML(val, config); const expectedResult = ` bar bar2 `; assert.equal(result, expectedResult); }); it("deep nesting", () => { const val = { a: { b: { c: { d: { e: { f: { g: { h: { i: { j: { k: { l: { m: { foo: "bar", }, }, }, }, }, }, }, }, }, }, }, }, }, }; const result = toXML(val); const expectedResult = "bar"; assert.equal(result, expectedResult); }); }); describe("header", () => { it("default header", () => { const val = { foo: "bar", }; const config = { header: true, }; const result = toXML(val, config); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("no header", () => { const val = { foo: "bar", }; const config = { header: false, }; const result = toXML(val, config); const expectedResult = "bar"; assert.equal(result, expectedResult); }); it("no header by default", () => { const val = { foo: "bar", }; const result = toXML(val); const expectedResult = "bar"; assert.equal(result, expectedResult); }); it("default header with indent", () => { const val = { foo: "bar", }; const config = { header: true, indent: " ", }; const result = toXML(val, config); const expectedResult = '\nbar'; assert.equal(result, expectedResult); }); it("custom header", () => { const val = { foo: "bar", }; const config = { header: '', }; const result = toXML(val, config); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("custom header 2", () => { const val = [ { row: 'bar' }, { row: 'bar2' } ]; const config = { header: '', indent: ' ' }; const result = toXML(val, config); const expectedResult = ` bar bar2`; assert.equal(result, expectedResult); }) }); describe("filtering", () => { it("values", () => { const val = { foo: "", bar: '"b"', baz: "'&whee'", }; const config = { filter: { "<": "<", ">": ">", '"': """, "'": "'", "&": "&", }, }; const result = toXML(val, config); const expectedResult = "<a>"b"'&whee'"; assert.equal(result, expectedResult); }); it("attributes", () => { const val = { _name: "foo", _attrs: { a: '<"\'&"foo>' }, }; const config = { attributesFilter: { "<": "<", ">": ">", '"': """, "'": "'", "&": "&", }, }; const result = toXML(val, config); const expectedResult = ''; assert.equal(result, expectedResult); }); const entities = { "&": "&", "<": "<", ">": ">", }; Object.entries(entities).forEach(([entity, entityEncoded]) => { it(`filters '${entity}' entities by default`, () => { const val = { _name: "foo", _attrs: { a: `aaa ${entity} bbb` }, _content: `foo ${entity} bar`, }; const result = toXML(val); const expectedResult = `foo ${entityEncoded} bar`; assert.equal(result, expectedResult); }); }); it(`filters entities by default 2`, () => { const val = { foo: '1 < 2 & 2 > 1' }; const result = toXML(val); const expectedResult = `1 < 2 & 2 > 1`; assert.equal(result, expectedResult); }); it("does not double encode", () => { const val = { _name: "foo", _attrs: { a: "baz & > < bat" }, _content: "foo & > < bar", }; const result = toXML(val); const expectedResult = 'foo & > < bar'; assert.equal(result, expectedResult); }); it("does not double encode 2", () => { const val = { _name: "foo", _attrs: { a: "baz && &> &< bat" }, _content: "foo && &> &< bar", }; const result = toXML(val); const expectedResult = 'foo && &> &< bar'; assert.equal(result, expectedResult); }); it("does not double encode 3", () => { const val = { _name: "foo", _attrs: { a: "¢ ¢ € € &eu ro;" }, _content: "¢ ¢ € € &eu ro;", }; const result = toXML(val); const expectedResult = '¢ ¢ € € &eu ro;'; assert.equal(result, expectedResult); }); it("escapes quotes in attributes by default", () => { const val = { _name: "foo", _attrs: { a: '"bat"' }, }; const result = toXML(val); const expectedResult = ''; assert.equal(result, expectedResult); }); it(`turns off attributes filter`, () => { const val = { _name: "foo", _attrs: { a: "baz & < > \" bat" }, _content: "foo & < > bar", }; const result = toXML(val, { attributesFilter: false }); const expectedResult = `foo & < > bar`; assert.equal(result, expectedResult); }); it(`turns off filter`, () => { const val = { _name: "foo", _attrs: { a: "baz & < > \" bat" }, _content: "foo & < > bar", }; const result = toXML(val, { filter: false }); const expectedResult = `foo & < > bar`; assert.equal(result, expectedResult); }); it(`turns off both filter and attributesFilter`, () => { const val = { _name: "foo", _attrs: { a: "baz & < > \" bat" }, _content: "foo & < > bar", }; const result = toXML(val, { filter: false, attributesFilter: false }); const expectedResult = `foo & < > bar`; assert.equal(result, expectedResult); }); }); describe("misc", () => { it("outputs <_content> if it has no tag name", () => { const val = { _content: "foo", }; const result = toXML(val); const expectedResult = "<_content>foo"; assert.equal(result, expectedResult); }); it("outputs emoji attributes", () => { const val = { html: { _attrs: [{ "⚡": true }, { lang: "en" }, { lang: "klingon" }], }, }; const result = toXML(val, { attributesFilter: {} }); const expectedResult = ''; assert.equal(result, expectedResult); }); it("outputs emoji attributes 2", () => { const val = { html: { _attrs: { "⚡": true, lang: "en" }, }, }; const result = toXML(val, { attributesFilter: {} }); const expectedResult = ''; assert.equal(result, expectedResult); }); it("does not force self close if tag has content", () => { const val = { _name: "foo", _selfCloseTag: true, _content: "bar", }; const result = toXML(val); const expectedResult = "bar"; assert.equal(result, expectedResult); }); it("nested elements with self-closing sibling", () => { const val = { people: { students: [ { student: { name: "Joe" }, }, { student: { name: "Jane" }, }, ], teacher: { _selfCloseTag: true, _attrs: { name: "Yoda", }, }, }, }; const result = toXML(val); const expectedResult = 'JoeJane'; assert.equal(result, expectedResult); }); it("sibling _content tag", () => { const val = { foo: { bar: "baz", _content: { bar2: "baz2", }, }, }; const result = toXML(val); const expectedResult = "bazbaz2"; assert.equal(result, expectedResult); }); }); describe("examples", () => { it("1 simple object", () => { const val = { foo: "bar", foo2: "bar2", }; const result = toXML(val); const expectedResult = "barbar2"; assert.equal(result, expectedResult); }); it("2 simple array", () => { const val = [{ foo: "bar" }, { foo: "bar2" }]; const result = toXML(val); const expectedResult = "barbar2"; assert.equal(result, expectedResult); }); it("3 simple function", () => { const date = new Date(); const val = { currentTime: () => date, }; const result = toXML(val); const expectedResult = `${date}`; assert.equal(result, expectedResult); }); it("4 attributes", () => { const val = { _name: "foo", _content: "bar", _attrs: { a: "b", c: "d", }, }; const result = toXML(val); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("5 tags with mixed content", () => { const val = { text1: null, foo: "bar", text2: null, }; const result = toXML(val); const expectedResult = "text1bartext2"; assert.equal(result, expectedResult); }); it("6 nested tags with indent", () => { const val = { a: { foo: "bar", foo2: "bar2", }, }; const config = { header: false, indent: " ", }; const result = toXML(val, config); const expectedResult = ` bar bar2 `; assert.equal(result, expectedResult); }); it("7 nested tags attributes", () => { const val = { ooo: { _name: "foo", _attrs: { a: "b", }, _content: { _name: "bar", _attrs: { c: "d", }, }, }, }; const config = { header: false, indent: " ", }; const result = toXML(val, config); const expectedResult = ` `; assert.equal(result, expectedResult); }); it("8 complex functions", () => { const val = { someNestedXML: () => ({ foo: "bar" }), }; const result = toXML(val); const expectedResult = "bar"; assert.equal(result, expectedResult); }); it("9 RSS feed", () => { const date = new Date(); const val = { _name: "rss", _attrs: { version: "2.0", }, _content: { channel: [ { title: "RSS Example" }, { description: "Description" }, { link: "google.com" }, { lastBuildDate: () => date }, { pubDate: () => date }, { language: "en" }, { item: { title: "Item title", link: "Item link", description: "Item Description", pubDate: () => date, }, }, { item: { title: "Item2 title", link: "Item2 link", description: "Item2 Description", pubDate: () => date, }, }, ], }, }; const config = { header: true, indent: " ", }; const result = toXML(val, config); const expectedResult = ` RSS Example Description google.com ${date} ${date} en Item title Item link Item Description ${date} Item2 title Item2 link Item2 Description ${date} `; assert.equal(result, expectedResult); }); it("10 podcast RSS", () => { const val = { _name: "rss", _attrs: { "xmlns:itunes": "http://www.itunes.com/dtds/podcast-1.0.dtd", version: "2.0", }, _content: { channel: [ { title: "Title" }, { link: "google.com" }, { language: "en-us" }, { copyright: "Copyright 2011" }, { "itunes:subtitle": "Subtitle" }, { "itunes:author": "Author" }, { "itunes:summary": "Summary" }, { description: "Description" }, { "itunes:owner": { "itunes:name": "Name", "itunes:email": "Email", }, }, { _name: "itunes:image", _attrs: { href: "image.jpg", }, }, { _name: "itunes:category", _attrs: { text: "Technology", }, _content: { _name: "itunes:category", _attrs: { text: "Gadgets", }, }, }, { _name: "itunes:category", _attrs: { text: "TV & Film", }, }, { item: [ { title: "Podcast Title" }, { "itunes:author": "Author" }, { "itunes:subtitle": "Subtitle" }, { "itunes:summary": "Summary" }, { "itunes:image": "image.jpg" }, { _name: "enclosure", _attrs: { url: "http://example.com/podcast.m4a", length: "8727310", type: "audio/x-m4a", }, }, { guid: "http://example.com/archive/aae20050615.m4a" }, { pubDate: "Wed, 15 Jun 2011 19:00:00 GMT" }, { "itunes:duration": "7:04" }, { "itunes:keywords": "salt, pepper, shaker, exciting" }, ], }, { item: [ { title: "Podcast2 Title" }, { "itunes:author": "Author2" }, { "itunes:subtitle": "Subtitle2" }, { "itunes:summary": "Summary2" }, { "itunes:image": "image2.jpg" }, { _name: "enclosure", _attrs: { url: "http://example.com/podcast2.m4a", length: "655555", type: "audio/x-m4a", }, }, { guid: "http://example.com/archive/aae2.m4a" }, { pubDate: "Wed, 15 Jul 2011 19:00:00 GMT" }, { "itunes:duration": "11:20" }, { "itunes:keywords": "foo, bar" }, ], }, ], }, }; const config = { header: true, indent: " ", }; const result = toXML(val, config); const expectedResult = ` Title google.com en-us Copyright 2011 Subtitle Author Summary Description Name Email Podcast Title Author Subtitle Summary image.jpg http://example.com/archive/aae20050615.m4a Wed, 15 Jun 2011 19:00:00 GMT 7:04 salt, pepper, shaker, exciting Podcast2 Title Author2 Subtitle2 Summary2 image2.jpg http://example.com/archive/aae2.m4a Wed, 15 Jul 2011 19:00:00 GMT 11:20 foo, bar `; assert.equal(result, expectedResult); }); it("11 filter", () => { const val = { foo: "", bar: '"b"', baz: "'&whee'", }; const config = { filter: { "<": "<", ">": ">", '"': """, "'": "'", "&": "&", }, }; const result = toXML(val, config); const expectedResult = "<a>"b"'&whee'"; assert.equal(result, expectedResult); }); it("11b attributes filter", () => { const val = { _name: "foo", _content: "bar", _attrs: { a: "http://example.com/?test='1'&foo=&whee=\"sha\"", b: "http://example2.com/?test='2'&md=<5>&sum=\"sha\"", }, }; const config = { attributesFilter: { "<": "<", ">": ">", '"': """, "'": "'", "&": "&", }, }; const result = toXML(val, config); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("12 avoiding self closing tags", () => { const val = [ { _name: "foo", _content: "", _selfCloseTag: false, }, { _name: "bar", _content: undefined, _selfCloseTag: false, }, ]; const result = toXML(val); const expectedResult = ""; assert.equal(result, expectedResult); }); it("13 custom xml header", () => { const val = { foo: "bar", }; const config = { header: '', }; const result = toXML(val, config); const expectedResult = 'bar'; assert.equal(result, expectedResult); }); it("14 emoji attributes", () => { const val = { html: { _attrs: { "⚡": true, }, }, }; const result = toXML(val); const expectedResult = ""; assert.equal(result, expectedResult); }); it("15 duplicate attribute keys", () => { const val = { html: { _attrs: [{ lang: "en" }, { lang: "klingon" }], }, }; const result = toXML(val); const expectedResult = ''; assert.equal(result, expectedResult); }); }); describe("issues", () => { it("issue #33: array of primitives", () => { const val = { x: [1, 2, 3], }; const result = toXML(val); const expectedResult = "123"; assert.equal(result, expectedResult); }); it("issue #33: array of primitives 2", () => { const val = { a: { x: [1, 2, 3], }, }; const result = toXML(val); const expectedResult = "123"; assert.equal(result, expectedResult); }); it("issue #33: array of primitives 2 with indent", () => { const val = { a: { x: [1, 2, 3], }, }; const config = { indent: " " }; const result = toXML(val, config); const expectedResult = "\n 1\n 2\n 3\n"; assert.equal(result, expectedResult); }); it("issue #33: array of objects", () => { const val = { a: { x: [ { b: 1, c: 2 }, { d: 3, e: 4 }, { f: 5, g: 6 }, ], }, }; const result = toXML(val); const expectedResult = "123456"; assert.equal(result, expectedResult); }); it("issue #33: array of objects jstoxml format", () => { const val = { a: [ { _name: "foo", _content: "1", }, { _name: "foo", _content: "2", }, ], }; const result = toXML(val); const expectedResult = "12"; assert.equal(result, expectedResult); }); it("issue #34: array of array", () => { const val = { Response: [ [ { _name: "Play", _content: "first sound", }, { _name: "Play", _content: "second sound", }, ], ], }; const result = toXML(val); const expectedResult = "first soundsecond sound"; assert.equal(result, expectedResult); }); it("issue #34", () => { const val = { t: [{ foo: "bar" }, { foo: "bar2" }] }; const result = toXML(val); const expectedResult = "barbar2"; assert.equal(result, expectedResult); }); it("issue #34", () => { const val = { t: [ { _name: "foo", _content: "bar" }, { _name: "foo", _content: "bar2" }, ], }; const result = toXML(val); const expectedResult = "barbar2"; assert.equal(result, expectedResult); }); it("issue #38", () => { const getFooVal = (iteration) => iteration; const getCurrentTime = (iterations) => { return Array(iterations) .fill(null) .map((foo, index) => { return { currentTime: { foo: getFooVal.bind(null, index + 1), }, }; }); }; const val = { invoice1: [ { invoice: "a", }, getCurrentTime.bind(null, 3), { foo2: "a", }, ], }; const config = { indent: " " }; const result = toXML(val, config); const expectedResult = ` a 1 2 3 a `; assert.equal(result, expectedResult); }); it("issue #40 forced separator, no indent", () => { const val = [{ a: "A Value" }, "\n", { b: "B Value" }]; const result = toXML(val); const expectedResult = `A Value B Value`; assert.equal(result, expectedResult); }); it("issue #40 array with indent", () => { const val = [{ a: "A Value" }, { b: "B Value" }]; const result = toXML(val, { indent: " " }); const expectedResult = `A Value B Value`; assert.equal(result, expectedResult); }); it("issue #40 array without indent", () => { const val = [{ a: "A Value" }, { b: "B Value" }]; const result = toXML(val); const expectedResult = `A ValueB Value`; assert.equal(result, expectedResult); }); it("issue #40 object with indent", () => { const val = { a: "A Value", b: "B Value", }; const result = toXML(val, { indent: " " }); const expectedResult = `A Value B Value`; assert.equal(result, expectedResult); }); it("issue #40 object without indent", () => { const val = { a: "A Value", b: "B Value", }; const result = toXML(val); const expectedResult = `A ValueB Value`; assert.equal(result, expectedResult); }); it("comments 1", () => { const val = { _comment: "test comment", a: "foo" }; const result = toXML(val); const expectedResult = `foo`; assert.equal(result, expectedResult); }); it("comments 2", () => { const val = { _comment: "test comment", a: "foo" }; const result = toXML(val, { indent: ' ' }); const expectedResult = ` foo`; assert.equal(result, expectedResult); }); it("comments 3", () => { const val = { _comment: "comment 1", b: { _comment: "comment 2", a: "foo" } }; const result = toXML(val, { indent: ' ' }); const expectedResult = ` foo `; assert.equal(result, expectedResult); }); it("comments 4", () => { const val = { _comment: "comment 1", b: [ { _comment: "comment 2" }, { _comment: "comment 3" }, { a: "foo" } ] }; const result = toXML(val, { indent: ' ' }); const expectedResult = ` foo `; assert.equal(result, expectedResult); }); }); it("comments 5", () => { const val = { _comment: 'Some important comment', a: { b: [1, 2, 3] } }; const result = toXML(val, { indent: ' ' }); const expectedResult = ` 1 2 3 `; assert.equal(result, expectedResult); }); it("comments 6", () => { const val = [ { _comment: 'Some important comment' }, { _comment: 'This is a very long comment!' }, { _comment: 'More important exposition!' }, { a: { b: [1, 2, 3] } } ]; const result = toXML(val, { indent: ' ' }); const expectedResult = ` 1 2 3 `; assert.equal(result, expectedResult); }); });