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);
});
});