| 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 = "<foo><depth>2</depth></foo>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("converts nonprimitive output", () => { | |
|       const val = { foo: "bar" }; | |
|       const result = toXML(() => val); | |
|       const expectedResult = "<foo>bar</foo>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("converts nested nonprimitive output", () => { | |
|       const val = { foo: { bar: { baz: 2 } } }; | |
|       const result = toXML(() => val); | |
|       const expectedResult = "<foo><bar><baz>2</baz></bar></foo>"; | |
|       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 = | |
|         "<foo>\n  <bar>\n    <baz>2</baz>\n  </bar>\n</foo>"; | |
|       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 = | |
|         "<foo>true</foo><bar/><foo2>false</foo2><ok>This is ok</ok><ok2>false</ok2><ok3>true</ok3>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|   }); | |
| 
 | |
|   describe("arrays", () => { | |
|     it("1", () => { | |
|       const val = [{ foo: "bar" }, { foo: "baz" }, { foo2: "bar2" }]; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo>bar</foo><foo>baz</foo><foo2>bar2</foo2>"; | |
|       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 = | |
|         '<foo>bar</foo><foo>baz</foo><foo/><foo/>foo<foo a="b" c="d">bar</foo>'; | |
|       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 = `<foo> | |
|   <foo>bar</foo> | |
|   <foo>baz</foo> | |
|   <foo2>bar2</foo2> | |
| </foo>`; | |
|       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 = '<foo a="1" b="2">bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("2", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _content: { | |
|           foo: "bar", | |
|         }, | |
|         _attrs: { | |
|           a: 1, | |
|           b: 2, | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<foo a="1" b="2"><foo>bar</foo></foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("3", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _content: () => 1 + 2, | |
|         _attrs: { | |
|           a: 1, | |
|           b: 2, | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<foo a="1" b="2">3</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|   }); | |
| 
 | |
|   describe("objects", () => { | |
|     it("1", () => { | |
|       const val = { | |
|         foo: "bar", | |
|         foo2: "bar2", | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo>bar</foo><foo2>bar2</foo2>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("attributes", () => { | |
|       const val = { | |
|         _name: "a", | |
|         _attrs: { | |
|           foo: "bar", | |
|           foo2: "bar2", | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<a foo="bar" foo2="bar2"/>'; | |
|       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 = '<a foo="bar" foo2="bar2">la dee da</a>'; | |
|       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 = '<foo a="b"><bar c="d"/></foo>'; | |
|       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 = | |
|         "blah<foo>bar</foo>more blah<bar>0</bar>more more blah<baz>false</baz>"; | |
|       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 = `<foo> | |
|   <foo>bar</foo> | |
|   <foo2>bar2</foo2> | |
| </foo>`; | |
|       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 = | |
|         "<a><b><c><d><e><f><g><h><i><j><k><l><m><foo>bar</foo></m></l></k></j></i></h></g></f></e></d></c></b></a>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|   }); | |
| 
 | |
|   describe("header", () => { | |
|     it("default header", () => { | |
|       const val = { | |
|         foo: "bar", | |
|       }; | |
|       const config = { | |
|         header: true, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = | |
|         '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("no header", () => { | |
|       const val = { | |
|         foo: "bar", | |
|       }; | |
|       const config = { | |
|         header: false, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = "<foo>bar</foo>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("no header by default", () => { | |
|       const val = { | |
|         foo: "bar", | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo>bar</foo>"; | |
|       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 = | |
|         '<?xml version="1.0" encoding="UTF-8"?>\n<foo>bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("custom header", () => { | |
|       const val = { | |
|         foo: "bar", | |
|       }; | |
|       const config = { | |
|         header: '<?FOO BAR="123" BAZ="XX"?>', | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = '<?FOO BAR="123" BAZ="XX"?><foo>bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("custom header 2", () => { | |
|       const val = [ | |
|         { | |
|           row: 'bar' | |
|         }, | |
|         { | |
|           row: 'bar2' | |
|         } | |
|       ]; | |
|       const config = { | |
|         header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>', | |
|         indent: ' ' | |
|       }; | |
| 
 | |
|       const result = toXML(val, config); | |
| 
 | |
|       const expectedResult = `<?xml version="1.0" encoding="UTF-16" standalone="yes"?> | |
| <row>bar</row> | |
| <row>bar2</row>`; | |
|       assert.equal(result, expectedResult); | |
|     }) | |
|   }); | |
| 
 | |
|   describe("filtering", () => { | |
|     it("values", () => { | |
|       const val = { | |
|         foo: "<a>", | |
|         bar: '"b"', | |
|         baz: "'&whee'", | |
|       }; | |
|       const config = { | |
|         filter: { | |
|           "<": "<", | |
|           ">": ">", | |
|           '"': """, | |
|           "'": "'", | |
|           "&": "&", | |
|         }, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = | |
|         "<foo><a></foo><bar>"b"</bar><baz>'&whee'</baz>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("attributes", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _attrs: { a: '<"\'&"foo>' }, | |
|       }; | |
|       const config = { | |
|         attributesFilter: { | |
|           "<": "<", | |
|           ">": ">", | |
|           '"': """, | |
|           "'": "'", | |
|           "&": "&", | |
|         }, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = '<foo a="<"'&"foo>"/>'; | |
|       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 a="aaa ${entityEncoded} bbb">foo ${entityEncoded} bar</foo>`; | |
|         assert.equal(result, expectedResult); | |
|       }); | |
|     }); | |
| 
 | |
|     it(`filters entities by default 2`, () => { | |
|       const val = { | |
|         foo: '1 < 2 & 2 > 1' | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = `<foo>1 < 2 & 2 > 1</foo>`; | |
|       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 a="baz & > < bat">foo & > < bar</foo>'; | |
|       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 a="baz && &> &< bat">foo && &> &< bar</foo>'; | |
|       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 = | |
|         '<foo a="¢ ¢ € € &eu ro;">¢ ¢ € € &eu ro;</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("escapes quotes in attributes by default", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _attrs: { a: '"bat"' }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<foo a=""bat""/>'; | |
|       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 a="baz & < > \" bat">foo & < > bar</foo>`; | |
|       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 a="baz & < > " bat">foo & < > bar</foo>`; | |
|       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 a="baz & < > \" bat">foo & < > bar</foo>`; | |
|       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</_content>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("outputs emoji attributes", () => { | |
|       const val = { | |
|         html: { | |
|           _attrs: [{ "⚡": true }, { lang: "en" }, { lang: "klingon" }], | |
|         }, | |
|       }; | |
|       const result = toXML(val, { attributesFilter: {} }); | |
|       const expectedResult = '<html ⚡ lang="en" lang="klingon"/>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("outputs emoji attributes 2", () => { | |
|       const val = { | |
|         html: { | |
|           _attrs: { "⚡": true, lang: "en" }, | |
|         }, | |
|       }; | |
|       const result = toXML(val, { attributesFilter: {} }); | |
|       const expectedResult = '<html ⚡ lang="en"/>'; | |
|       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 = "<foo>bar</foo>"; | |
|       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 = | |
|         '<people><students><student><name>Joe</name></student><student><name>Jane</name></student></students><teacher name="Yoda"/></people>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("sibling _content tag", () => { | |
|       const val = { | |
|         foo: { | |
|           bar: "baz", | |
|           _content: { | |
|             bar2: "baz2", | |
|           }, | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo><bar>baz</bar><bar2>baz2</bar2></foo>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|   }); | |
| 
 | |
|   describe("examples", () => { | |
|     it("1 simple object", () => { | |
|       const val = { | |
|         foo: "bar", | |
|         foo2: "bar2", | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo>bar</foo><foo2>bar2</foo2>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("2 simple array", () => { | |
|       const val = [{ foo: "bar" }, { foo: "bar2" }]; | |
|       const result = toXML(val); | |
|       const expectedResult = "<foo>bar</foo><foo>bar2</foo>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("3 simple function", () => { | |
|       const date = new Date(); | |
|       const val = { | |
|         currentTime: () => date, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = `<currentTime>${date}</currentTime>`; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("4 attributes", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _content: "bar", | |
|         _attrs: { | |
|           a: "b", | |
|           c: "d", | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<foo a="b" c="d">bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("5 tags with mixed content", () => { | |
|       const val = { | |
|         text1: null, | |
|         foo: "bar", | |
|         text2: null, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "text1<foo>bar</foo>text2"; | |
|       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 = `<a> | |
|   <foo>bar</foo> | |
|   <foo2>bar2</foo2> | |
| </a>`; | |
|       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 = `<ooo> | |
|   <foo a="b"> | |
|     <bar c="d"/> | |
|   </foo> | |
| </ooo>`; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("8 complex functions", () => { | |
|       const val = { | |
|         someNestedXML: () => ({ foo: "bar" }), | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<someNestedXML><foo>bar</foo></someNestedXML>"; | |
|       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 = `<?xml version="1.0" encoding="UTF-8"?> | |
| <rss version="2.0"> | |
|   <channel> | |
|     <title>RSS Example</title> | |
|     <description>Description</description> | |
|     <link>google.com</link> | |
|     <lastBuildDate>${date}</lastBuildDate> | |
|     <pubDate>${date}</pubDate> | |
|     <language>en</language> | |
|     <item> | |
|       <title>Item title</title> | |
|       <link>Item link</link> | |
|       <description>Item Description</description> | |
|       <pubDate>${date}</pubDate> | |
|     </item> | |
|     <item> | |
|       <title>Item2 title</title> | |
|       <link>Item2 link</link> | |
|       <description>Item2 Description</description> | |
|       <pubDate>${date}</pubDate> | |
|     </item> | |
|   </channel> | |
| </rss>`; | |
|       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 = `<?xml version="1.0" encoding="UTF-8"?> | |
| <rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0"> | |
|   <channel> | |
|     <title>Title</title> | |
|     <link>google.com</link> | |
|     <language>en-us</language> | |
|     <copyright>Copyright 2011</copyright> | |
|     <itunes:subtitle>Subtitle</itunes:subtitle> | |
|     <itunes:author>Author</itunes:author> | |
|     <itunes:summary>Summary</itunes:summary> | |
|     <description>Description</description> | |
|     <itunes:owner> | |
|       <itunes:name>Name</itunes:name> | |
|       <itunes:email>Email</itunes:email> | |
|     </itunes:owner> | |
|     <itunes:image href="image.jpg"/> | |
|     <itunes:category text="Technology"> | |
|       <itunes:category text="Gadgets"/> | |
|     </itunes:category> | |
|     <itunes:category text="TV & Film"/> | |
|     <item> | |
|       <title>Podcast Title</title> | |
|       <itunes:author>Author</itunes:author> | |
|       <itunes:subtitle>Subtitle</itunes:subtitle> | |
|       <itunes:summary>Summary</itunes:summary> | |
|       <itunes:image>image.jpg</itunes:image> | |
|       <enclosure url="http://example.com/podcast.m4a" length="8727310" type="audio/x-m4a"/> | |
|       <guid>http://example.com/archive/aae20050615.m4a</guid> | |
|       <pubDate>Wed, 15 Jun 2011 19:00:00 GMT</pubDate> | |
|       <itunes:duration>7:04</itunes:duration> | |
|       <itunes:keywords>salt, pepper, shaker, exciting</itunes:keywords> | |
|     </item> | |
|     <item> | |
|       <title>Podcast2 Title</title> | |
|       <itunes:author>Author2</itunes:author> | |
|       <itunes:subtitle>Subtitle2</itunes:subtitle> | |
|       <itunes:summary>Summary2</itunes:summary> | |
|       <itunes:image>image2.jpg</itunes:image> | |
|       <enclosure url="http://example.com/podcast2.m4a" length="655555" type="audio/x-m4a"/> | |
|       <guid>http://example.com/archive/aae2.m4a</guid> | |
|       <pubDate>Wed, 15 Jul 2011 19:00:00 GMT</pubDate> | |
|       <itunes:duration>11:20</itunes:duration> | |
|       <itunes:keywords>foo, bar</itunes:keywords> | |
|     </item> | |
|   </channel> | |
| </rss>`; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("11 filter", () => { | |
|       const val = { | |
|         foo: "<a>", | |
|         bar: '"b"', | |
|         baz: "'&whee'", | |
|       }; | |
|       const config = { | |
|         filter: { | |
|           "<": "<", | |
|           ">": ">", | |
|           '"': """, | |
|           "'": "'", | |
|           "&": "&", | |
|         }, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = | |
|         "<foo><a></foo><bar>"b"</bar><baz>'&whee'</baz>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("11b attributes filter", () => { | |
|       const val = { | |
|         _name: "foo", | |
|         _content: "bar", | |
|         _attrs: { | |
|           a: "http://example.com/?test='1'&foo=<bar>&whee=\"sha\"", | |
|           b: "http://example2.com/?test='2'&md=<5>&sum=\"sha\"", | |
|         }, | |
|       }; | |
|       const config = { | |
|         attributesFilter: { | |
|           "<": "<", | |
|           ">": ">", | |
|           '"': """, | |
|           "'": "'", | |
|           "&": "&", | |
|         }, | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = | |
|         '<foo a="http://example.com/?test='1'&foo=<bar>&whee="sha"" b="http://example2.com/?test='2'&md=<5>&sum="sha"">bar</foo>'; | |
|       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 = "<foo></foo><bar></bar>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("13 custom xml header", () => { | |
|       const val = { | |
|         foo: "bar", | |
|       }; | |
|       const config = { | |
|         header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>', | |
|       }; | |
|       const result = toXML(val, config); | |
|       const expectedResult = | |
|         '<?xml version="1.0" encoding="UTF-16" standalone="yes"?><foo>bar</foo>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("14 emoji attributes", () => { | |
|       const val = { | |
|         html: { | |
|           _attrs: { | |
|             "⚡": true, | |
|           }, | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<html ⚡/>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("15 duplicate attribute keys", () => { | |
|       const val = { | |
|         html: { | |
|           _attrs: [{ lang: "en" }, { lang: "klingon" }], | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = '<html lang="en" lang="klingon"/>'; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|   }); | |
| 
 | |
|   describe("issues", () => { | |
|     it("issue #33: array of primitives", () => { | |
|       const val = { | |
|         x: [1, 2, 3], | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<x>1</x><x>2</x><x>3</x>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("issue #33: array of primitives 2", () => { | |
|       const val = { | |
|         a: { | |
|           x: [1, 2, 3], | |
|         }, | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<a><x>1</x><x>2</x><x>3</x></a>"; | |
|       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 = "<a>\n  <x>1</x>\n  <x>2</x>\n  <x>3</x>\n</a>"; | |
|       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 = | |
|         "<a><x><b>1</b><c>2</c><d>3</d><e>4</e><f>5</f><g>6</g></x></a>"; | |
|       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 = "<a><foo>1</foo><foo>2</foo></a>"; | |
|       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 = | |
|         "<Response><Play>first sound</Play><Play>second sound</Play></Response>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|     it("issue #34", () => { | |
|       const val = { t: [{ foo: "bar" }, { foo: "bar2" }] }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<t><foo>bar</foo><foo>bar2</foo></t>"; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
|     it("issue #34", () => { | |
|       const val = { | |
|         t: [ | |
|           { _name: "foo", _content: "bar" }, | |
|           { _name: "foo", _content: "bar2" }, | |
|         ], | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = "<t><foo>bar</foo><foo>bar2</foo></t>"; | |
|       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 = `<invoice1> | |
|   <invoice>a</invoice> | |
|   <currentTime> | |
|     <foo>1</foo> | |
|   </currentTime> | |
|   <currentTime> | |
|     <foo>2</foo> | |
|   </currentTime> | |
|   <currentTime> | |
|     <foo>3</foo> | |
|   </currentTime> | |
|   <foo2>a</foo2> | |
| </invoice1>`; | |
|       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>A Value</a> | |
| <b>B Value</b>`; | |
|       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>A Value</a> | |
| <b>B Value</b>`; | |
|       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>A Value</a><b>B Value</b>`; | |
|       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>A Value</a> | |
| <b>B Value</b>`; | |
|       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>A Value</a><b>B Value</b>`; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("comments 1", () => { | |
|       const val = { | |
|         _comment: "test comment", | |
|         a: "foo" | |
|       }; | |
|       const result = toXML(val); | |
|       const expectedResult = `<!-- test comment --><a>foo</a>`; | |
|       assert.equal(result, expectedResult); | |
|     }); | |
| 
 | |
|     it("comments 2", () => { | |
|       const val = { | |
|         _comment: "test comment", | |
|         a: "foo" | |
|       }; | |
|       const result = toXML(val, { indent: '    ' }); | |
|       const expectedResult = `<!-- test comment --> | |
| <a>foo</a>`; | |
|       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 = `<!-- comment 1 --> | |
| <b> | |
|     <!-- comment 2 --> | |
|     <a>foo</a> | |
| </b>`; | |
|       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 = `<!-- comment 1 --> | |
| <b> | |
|     <!-- comment 2 --> | |
|     <!-- comment 3 --> | |
|     <a>foo</a> | |
| </b>`; | |
|       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 = `<!-- Some important comment --> | |
| <a> | |
|     <b>1</b> | |
|     <b>2</b> | |
|     <b>3</b> | |
| </a>`; | |
|     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 = `<!-- Some important comment --> | |
| <!-- This is a very long comment! --> | |
| <!-- More important exposition! --> | |
| <a> | |
|     <b>1</b> | |
|     <b>2</b> | |
|     <b>3</b> | |
| </a>`; | |
|     assert.equal(result, expectedResult); | |
|   }); | |
| });
 |