用工小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1326 lines
35 KiB

6 months ago
  1. const { toXML } = require("./dist/jstoxml");
  2. const assert = require("assert");
  3. describe("toXML", () => {
  4. describe("primitives", () => {
  5. const vals = ["foo", false, true, 4, 4.56];
  6. vals.forEach((val) => {
  7. it(`outputs ${val}`, () => {
  8. const result = toXML(val);
  9. const expectedResult = `${val}`;
  10. assert.equal(result, expectedResult);
  11. });
  12. });
  13. });
  14. describe("functions", () => {
  15. describe("primitive outputs", () => {
  16. const vals = [999, "foo", false, true];
  17. vals.forEach((val) => {
  18. it(`${val}`, () => {
  19. const result = toXML(() => val);
  20. const expectedResult = `${val}`;
  21. assert.equal(result, expectedResult);
  22. });
  23. });
  24. });
  25. it("fat arrow", () => {
  26. const val = 888;
  27. const result = toXML(() => val);
  28. const expectedResult = val;
  29. assert.equal(result, expectedResult);
  30. });
  31. it("accessing config within function", () => {
  32. const val = {
  33. foo: {
  34. depth: (config) => config.depth,
  35. },
  36. };
  37. const result = toXML(val);
  38. const expectedResult = "<foo><depth>2</depth></foo>";
  39. assert.equal(result, expectedResult);
  40. });
  41. it("converts nonprimitive output", () => {
  42. const val = { foo: "bar" };
  43. const result = toXML(() => val);
  44. const expectedResult = "<foo>bar</foo>";
  45. assert.equal(result, expectedResult);
  46. });
  47. it("converts nested nonprimitive output", () => {
  48. const val = { foo: { bar: { baz: 2 } } };
  49. const result = toXML(() => val);
  50. const expectedResult = "<foo><bar><baz>2</baz></bar></foo>";
  51. assert.equal(result, expectedResult);
  52. });
  53. it("converts nested nonprimitive output with indent", () => {
  54. const val = { foo: { bar: { baz: 2 } } };
  55. const config = { indent: " " };
  56. const result = toXML(() => val, config);
  57. const expectedResult =
  58. "<foo>\n <bar>\n <baz>2</baz>\n </bar>\n</foo>";
  59. assert.equal(result, expectedResult);
  60. });
  61. });
  62. describe("github issues", () => {
  63. it("issue 3", () => {
  64. const val = {
  65. foo: true,
  66. bar: "",
  67. foo2: false,
  68. ok: "This is ok",
  69. ok2: "false",
  70. ok3: "true",
  71. };
  72. const result = toXML(val);
  73. const expectedResult =
  74. "<foo>true</foo><bar/><foo2>false</foo2><ok>This is ok</ok><ok2>false</ok2><ok3>true</ok3>";
  75. assert.equal(result, expectedResult);
  76. });
  77. });
  78. describe("arrays", () => {
  79. it("1", () => {
  80. const val = [{ foo: "bar" }, { foo: "baz" }, { foo2: "bar2" }];
  81. const result = toXML(val);
  82. const expectedResult = "<foo>bar</foo><foo>baz</foo><foo2>bar2</foo2>";
  83. assert.equal(result, expectedResult);
  84. });
  85. it("attributes in subobject", () => {
  86. const val = [
  87. { foo: "bar" },
  88. { foo: "baz" },
  89. { foo: undefined },
  90. { foo: "" },
  91. { foo: null },
  92. {
  93. _name: "foo",
  94. _content: "bar",
  95. _attrs: {
  96. a: "b",
  97. c: "d",
  98. },
  99. },
  100. ];
  101. const result = toXML(val);
  102. const expectedResult =
  103. '<foo>bar</foo><foo>baz</foo><foo/><foo/>foo<foo a="b" c="d">bar</foo>';
  104. assert.equal(result, expectedResult);
  105. });
  106. it("nesting with indent", () => {
  107. const val = {
  108. foo: [{ foo: "bar" }, { foo: "baz" }, { foo2: "bar2" }],
  109. };
  110. const config = { indent: " " };
  111. const result = toXML(val, config);
  112. const expectedResult = `<foo>
  113. <foo>bar</foo>
  114. <foo>baz</foo>
  115. <foo2>bar2</foo2>
  116. </foo>`;
  117. assert.equal(result, expectedResult);
  118. });
  119. });
  120. describe("special-objects", () => {
  121. it("1", () => {
  122. const val = {
  123. _name: "foo",
  124. _content: "bar",
  125. _attrs: {
  126. a: 1,
  127. b: 2,
  128. },
  129. };
  130. const result = toXML(val);
  131. const expectedResult = '<foo a="1" b="2">bar</foo>';
  132. assert.equal(result, expectedResult);
  133. });
  134. it("2", () => {
  135. const val = {
  136. _name: "foo",
  137. _content: {
  138. foo: "bar",
  139. },
  140. _attrs: {
  141. a: 1,
  142. b: 2,
  143. },
  144. };
  145. const result = toXML(val);
  146. const expectedResult = '<foo a="1" b="2"><foo>bar</foo></foo>';
  147. assert.equal(result, expectedResult);
  148. });
  149. it("3", () => {
  150. const val = {
  151. _name: "foo",
  152. _content: () => 1 + 2,
  153. _attrs: {
  154. a: 1,
  155. b: 2,
  156. },
  157. };
  158. const result = toXML(val);
  159. const expectedResult = '<foo a="1" b="2">3</foo>';
  160. assert.equal(result, expectedResult);
  161. });
  162. });
  163. describe("objects", () => {
  164. it("1", () => {
  165. const val = {
  166. foo: "bar",
  167. foo2: "bar2",
  168. };
  169. const result = toXML(val);
  170. const expectedResult = "<foo>bar</foo><foo2>bar2</foo2>";
  171. assert.equal(result, expectedResult);
  172. });
  173. it("attributes", () => {
  174. const val = {
  175. _name: "a",
  176. _attrs: {
  177. foo: "bar",
  178. foo2: "bar2",
  179. },
  180. };
  181. const result = toXML(val);
  182. const expectedResult = '<a foo="bar" foo2="bar2"/>';
  183. assert.equal(result, expectedResult);
  184. });
  185. it("attributes 2", () => {
  186. const val = {
  187. _name: "a",
  188. _attrs: {
  189. foo: "bar",
  190. foo2: "bar2",
  191. },
  192. _content: "la dee da",
  193. };
  194. const result = toXML(val);
  195. const expectedResult = '<a foo="bar" foo2="bar2">la dee da</a>';
  196. assert.equal(result, expectedResult);
  197. });
  198. it("attributes nesting", () => {
  199. const val = {
  200. _name: "foo",
  201. _attrs: {
  202. a: "b",
  203. },
  204. _content: {
  205. _name: "bar",
  206. _attrs: {
  207. c: "d",
  208. },
  209. },
  210. };
  211. const result = toXML(val);
  212. const expectedResult = '<foo a="b"><bar c="d"/></foo>';
  213. assert.equal(result, expectedResult);
  214. });
  215. it("with mixed content", () => {
  216. const val = {
  217. blah: null,
  218. foo: "bar",
  219. "more blah": null,
  220. bar: 0,
  221. "more more blah": null,
  222. baz: false,
  223. };
  224. const result = toXML(val);
  225. const expectedResult =
  226. "blah<foo>bar</foo>more blah<bar>0</bar>more more blah<baz>false</baz>";
  227. assert.equal(result, expectedResult);
  228. });
  229. it("nesting with indent", () => {
  230. const val = {
  231. foo: {
  232. foo: "bar",
  233. foo2: "bar2",
  234. },
  235. };
  236. const config = { indent: " " };
  237. const result = toXML(val, config);
  238. const expectedResult = `<foo>
  239. <foo>bar</foo>
  240. <foo2>bar2</foo2>
  241. </foo>`;
  242. assert.equal(result, expectedResult);
  243. });
  244. it("deep nesting", () => {
  245. const val = {
  246. a: {
  247. b: {
  248. c: {
  249. d: {
  250. e: {
  251. f: {
  252. g: {
  253. h: {
  254. i: {
  255. j: {
  256. k: {
  257. l: {
  258. m: {
  259. foo: "bar",
  260. },
  261. },
  262. },
  263. },
  264. },
  265. },
  266. },
  267. },
  268. },
  269. },
  270. },
  271. },
  272. },
  273. };
  274. const result = toXML(val);
  275. const expectedResult =
  276. "<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>";
  277. assert.equal(result, expectedResult);
  278. });
  279. });
  280. describe("header", () => {
  281. it("default header", () => {
  282. const val = {
  283. foo: "bar",
  284. };
  285. const config = {
  286. header: true,
  287. };
  288. const result = toXML(val, config);
  289. const expectedResult =
  290. '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>';
  291. assert.equal(result, expectedResult);
  292. });
  293. it("no header", () => {
  294. const val = {
  295. foo: "bar",
  296. };
  297. const config = {
  298. header: false,
  299. };
  300. const result = toXML(val, config);
  301. const expectedResult = "<foo>bar</foo>";
  302. assert.equal(result, expectedResult);
  303. });
  304. it("no header by default", () => {
  305. const val = {
  306. foo: "bar",
  307. };
  308. const result = toXML(val);
  309. const expectedResult = "<foo>bar</foo>";
  310. assert.equal(result, expectedResult);
  311. });
  312. it("default header with indent", () => {
  313. const val = {
  314. foo: "bar",
  315. };
  316. const config = {
  317. header: true,
  318. indent: " ",
  319. };
  320. const result = toXML(val, config);
  321. const expectedResult =
  322. '<?xml version="1.0" encoding="UTF-8"?>\n<foo>bar</foo>';
  323. assert.equal(result, expectedResult);
  324. });
  325. it("custom header", () => {
  326. const val = {
  327. foo: "bar",
  328. };
  329. const config = {
  330. header: '<?FOO BAR="123" BAZ="XX"?>',
  331. };
  332. const result = toXML(val, config);
  333. const expectedResult = '<?FOO BAR="123" BAZ="XX"?><foo>bar</foo>';
  334. assert.equal(result, expectedResult);
  335. });
  336. it("custom header 2", () => {
  337. const val = [
  338. {
  339. row: 'bar'
  340. },
  341. {
  342. row: 'bar2'
  343. }
  344. ];
  345. const config = {
  346. header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>',
  347. indent: ' '
  348. };
  349. const result = toXML(val, config);
  350. const expectedResult = `<?xml version="1.0" encoding="UTF-16" standalone="yes"?>
  351. <row>bar</row>
  352. <row>bar2</row>`;
  353. assert.equal(result, expectedResult);
  354. })
  355. });
  356. describe("filtering", () => {
  357. it("values", () => {
  358. const val = {
  359. foo: "<a>",
  360. bar: '"b"',
  361. baz: "'&whee'",
  362. };
  363. const config = {
  364. filter: {
  365. "<": "&lt;",
  366. ">": "&gt;",
  367. '"': "&quot;",
  368. "'": "&apos;",
  369. "&": "&amp;",
  370. },
  371. };
  372. const result = toXML(val, config);
  373. const expectedResult =
  374. "<foo>&lt;a&gt;</foo><bar>&quot;b&quot;</bar><baz>&apos;&amp;whee&apos;</baz>";
  375. assert.equal(result, expectedResult);
  376. });
  377. it("attributes", () => {
  378. const val = {
  379. _name: "foo",
  380. _attrs: { a: '<"\'&"foo>' },
  381. };
  382. const config = {
  383. attributesFilter: {
  384. "<": "&lt;",
  385. ">": "&gt;",
  386. '"': "&quot;",
  387. "'": "&apos;",
  388. "&": "&amp;",
  389. },
  390. };
  391. const result = toXML(val, config);
  392. const expectedResult = '<foo a="&lt;&quot;&apos;&amp;&quot;foo&gt;"/>';
  393. assert.equal(result, expectedResult);
  394. });
  395. const entities = {
  396. "&": "&amp;",
  397. "<": "&lt;",
  398. ">": "&gt;",
  399. };
  400. Object.entries(entities).forEach(([entity, entityEncoded]) => {
  401. it(`filters '${entity}' entities by default`, () => {
  402. const val = {
  403. _name: "foo",
  404. _attrs: { a: `aaa ${entity} bbb` },
  405. _content: `foo ${entity} bar`,
  406. };
  407. const result = toXML(val);
  408. const expectedResult = `<foo a="aaa ${entityEncoded} bbb">foo ${entityEncoded} bar</foo>`;
  409. assert.equal(result, expectedResult);
  410. });
  411. });
  412. it(`filters entities by default 2`, () => {
  413. const val = {
  414. foo: '1 < 2 & 2 > 1'
  415. };
  416. const result = toXML(val);
  417. const expectedResult = `<foo>1 &lt; 2 &amp; 2 &gt; 1</foo>`;
  418. assert.equal(result, expectedResult);
  419. });
  420. it("does not double encode", () => {
  421. const val = {
  422. _name: "foo",
  423. _attrs: { a: "baz &amp; &gt; &lt; bat" },
  424. _content: "foo &amp; &gt; &lt; bar",
  425. };
  426. const result = toXML(val);
  427. const expectedResult =
  428. '<foo a="baz &amp; &gt; &lt; bat">foo &amp; &gt; &lt; bar</foo>';
  429. assert.equal(result, expectedResult);
  430. });
  431. it("does not double encode 2", () => {
  432. const val = {
  433. _name: "foo",
  434. _attrs: { a: "baz &&amp; &&gt; &&lt; bat" },
  435. _content: "foo &&amp; &&gt; &&lt; bar",
  436. };
  437. const result = toXML(val);
  438. const expectedResult =
  439. '<foo a="baz &amp;&amp; &amp;&gt; &amp;&lt; bat">foo &amp;&amp; &amp;&gt; &amp;&lt; bar</foo>';
  440. assert.equal(result, expectedResult);
  441. });
  442. it("does not double encode 3", () => {
  443. const val = {
  444. _name: "foo",
  445. _attrs: { a: "&cent; &#162; &euro; &#8364; &eu ro;" },
  446. _content: "&cent; &#162; &euro; &#8364; &eu ro;",
  447. };
  448. const result = toXML(val);
  449. const expectedResult =
  450. '<foo a="&cent; &#162; &euro; &#8364; &amp;eu ro;">&cent; &#162; &euro; &#8364; &amp;eu ro;</foo>';
  451. assert.equal(result, expectedResult);
  452. });
  453. it("escapes quotes in attributes by default", () => {
  454. const val = {
  455. _name: "foo",
  456. _attrs: { a: '"bat"' },
  457. };
  458. const result = toXML(val);
  459. const expectedResult = '<foo a="&quot;bat&quot;"/>';
  460. assert.equal(result, expectedResult);
  461. });
  462. it(`turns off attributes filter`, () => {
  463. const val = {
  464. _name: "foo",
  465. _attrs: { a: "baz & < > \" bat" },
  466. _content: "foo & < > bar",
  467. };
  468. const result = toXML(val, { attributesFilter: false });
  469. const expectedResult = `<foo a="baz & < > \" bat">foo &amp; &lt; &gt; bar</foo>`;
  470. assert.equal(result, expectedResult);
  471. });
  472. it(`turns off filter`, () => {
  473. const val = {
  474. _name: "foo",
  475. _attrs: { a: "baz & < > \" bat" },
  476. _content: "foo & < > bar",
  477. };
  478. const result = toXML(val, { filter: false });
  479. const expectedResult = `<foo a="baz &amp; &lt; &gt; &quot; bat">foo & < > bar</foo>`;
  480. assert.equal(result, expectedResult);
  481. });
  482. it(`turns off both filter and attributesFilter`, () => {
  483. const val = {
  484. _name: "foo",
  485. _attrs: { a: "baz & < > \" bat" },
  486. _content: "foo & < > bar",
  487. };
  488. const result = toXML(val, { filter: false, attributesFilter: false });
  489. const expectedResult = `<foo a="baz & < > \" bat">foo & < > bar</foo>`;
  490. assert.equal(result, expectedResult);
  491. });
  492. });
  493. describe("misc", () => {
  494. it("outputs <_content> if it has no tag name", () => {
  495. const val = {
  496. _content: "foo",
  497. };
  498. const result = toXML(val);
  499. const expectedResult = "<_content>foo</_content>";
  500. assert.equal(result, expectedResult);
  501. });
  502. it("outputs emoji attributes", () => {
  503. const val = {
  504. html: {
  505. _attrs: [{ "⚡": true }, { lang: "en" }, { lang: "klingon" }],
  506. },
  507. };
  508. const result = toXML(val, { attributesFilter: {} });
  509. const expectedResult = '<html ⚡ lang="en" lang="klingon"/>';
  510. assert.equal(result, expectedResult);
  511. });
  512. it("outputs emoji attributes 2", () => {
  513. const val = {
  514. html: {
  515. _attrs: { "⚡": true, lang: "en" },
  516. },
  517. };
  518. const result = toXML(val, { attributesFilter: {} });
  519. const expectedResult = '<html ⚡ lang="en"/>';
  520. assert.equal(result, expectedResult);
  521. });
  522. it("does not force self close if tag has content", () => {
  523. const val = {
  524. _name: "foo",
  525. _selfCloseTag: true,
  526. _content: "bar",
  527. };
  528. const result = toXML(val);
  529. const expectedResult = "<foo>bar</foo>";
  530. assert.equal(result, expectedResult);
  531. });
  532. it("nested elements with self-closing sibling", () => {
  533. const val = {
  534. people: {
  535. students: [
  536. {
  537. student: { name: "Joe" },
  538. },
  539. {
  540. student: { name: "Jane" },
  541. },
  542. ],
  543. teacher: {
  544. _selfCloseTag: true,
  545. _attrs: {
  546. name: "Yoda",
  547. },
  548. },
  549. },
  550. };
  551. const result = toXML(val);
  552. const expectedResult =
  553. '<people><students><student><name>Joe</name></student><student><name>Jane</name></student></students><teacher name="Yoda"/></people>';
  554. assert.equal(result, expectedResult);
  555. });
  556. it("sibling _content tag", () => {
  557. const val = {
  558. foo: {
  559. bar: "baz",
  560. _content: {
  561. bar2: "baz2",
  562. },
  563. },
  564. };
  565. const result = toXML(val);
  566. const expectedResult = "<foo><bar>baz</bar><bar2>baz2</bar2></foo>";
  567. assert.equal(result, expectedResult);
  568. });
  569. });
  570. describe("examples", () => {
  571. it("1 simple object", () => {
  572. const val = {
  573. foo: "bar",
  574. foo2: "bar2",
  575. };
  576. const result = toXML(val);
  577. const expectedResult = "<foo>bar</foo><foo2>bar2</foo2>";
  578. assert.equal(result, expectedResult);
  579. });
  580. it("2 simple array", () => {
  581. const val = [{ foo: "bar" }, { foo: "bar2" }];
  582. const result = toXML(val);
  583. const expectedResult = "<foo>bar</foo><foo>bar2</foo>";
  584. assert.equal(result, expectedResult);
  585. });
  586. it("3 simple function", () => {
  587. const date = new Date();
  588. const val = {
  589. currentTime: () => date,
  590. };
  591. const result = toXML(val);
  592. const expectedResult = `<currentTime>${date}</currentTime>`;
  593. assert.equal(result, expectedResult);
  594. });
  595. it("4 attributes", () => {
  596. const val = {
  597. _name: "foo",
  598. _content: "bar",
  599. _attrs: {
  600. a: "b",
  601. c: "d",
  602. },
  603. };
  604. const result = toXML(val);
  605. const expectedResult = '<foo a="b" c="d">bar</foo>';
  606. assert.equal(result, expectedResult);
  607. });
  608. it("5 tags with mixed content", () => {
  609. const val = {
  610. text1: null,
  611. foo: "bar",
  612. text2: null,
  613. };
  614. const result = toXML(val);
  615. const expectedResult = "text1<foo>bar</foo>text2";
  616. assert.equal(result, expectedResult);
  617. });
  618. it("6 nested tags with indent", () => {
  619. const val = {
  620. a: {
  621. foo: "bar",
  622. foo2: "bar2",
  623. },
  624. };
  625. const config = {
  626. header: false,
  627. indent: " ",
  628. };
  629. const result = toXML(val, config);
  630. const expectedResult = `<a>
  631. <foo>bar</foo>
  632. <foo2>bar2</foo2>
  633. </a>`;
  634. assert.equal(result, expectedResult);
  635. });
  636. it("7 nested tags attributes", () => {
  637. const val = {
  638. ooo: {
  639. _name: "foo",
  640. _attrs: {
  641. a: "b",
  642. },
  643. _content: {
  644. _name: "bar",
  645. _attrs: {
  646. c: "d",
  647. },
  648. },
  649. },
  650. };
  651. const config = {
  652. header: false,
  653. indent: " ",
  654. };
  655. const result = toXML(val, config);
  656. const expectedResult = `<ooo>
  657. <foo a="b">
  658. <bar c="d"/>
  659. </foo>
  660. </ooo>`;
  661. assert.equal(result, expectedResult);
  662. });
  663. it("8 complex functions", () => {
  664. const val = {
  665. someNestedXML: () => ({ foo: "bar" }),
  666. };
  667. const result = toXML(val);
  668. const expectedResult = "<someNestedXML><foo>bar</foo></someNestedXML>";
  669. assert.equal(result, expectedResult);
  670. });
  671. it("9 RSS feed", () => {
  672. const date = new Date();
  673. const val = {
  674. _name: "rss",
  675. _attrs: {
  676. version: "2.0",
  677. },
  678. _content: {
  679. channel: [
  680. { title: "RSS Example" },
  681. { description: "Description" },
  682. { link: "google.com" },
  683. { lastBuildDate: () => date },
  684. { pubDate: () => date },
  685. { language: "en" },
  686. {
  687. item: {
  688. title: "Item title",
  689. link: "Item link",
  690. description: "Item Description",
  691. pubDate: () => date,
  692. },
  693. },
  694. {
  695. item: {
  696. title: "Item2 title",
  697. link: "Item2 link",
  698. description: "Item2 Description",
  699. pubDate: () => date,
  700. },
  701. },
  702. ],
  703. },
  704. };
  705. const config = {
  706. header: true,
  707. indent: " ",
  708. };
  709. const result = toXML(val, config);
  710. const expectedResult = `<?xml version="1.0" encoding="UTF-8"?>
  711. <rss version="2.0">
  712. <channel>
  713. <title>RSS Example</title>
  714. <description>Description</description>
  715. <link>google.com</link>
  716. <lastBuildDate>${date}</lastBuildDate>
  717. <pubDate>${date}</pubDate>
  718. <language>en</language>
  719. <item>
  720. <title>Item title</title>
  721. <link>Item link</link>
  722. <description>Item Description</description>
  723. <pubDate>${date}</pubDate>
  724. </item>
  725. <item>
  726. <title>Item2 title</title>
  727. <link>Item2 link</link>
  728. <description>Item2 Description</description>
  729. <pubDate>${date}</pubDate>
  730. </item>
  731. </channel>
  732. </rss>`;
  733. assert.equal(result, expectedResult);
  734. });
  735. it("10 podcast RSS", () => {
  736. const val = {
  737. _name: "rss",
  738. _attrs: {
  739. "xmlns:itunes": "http://www.itunes.com/dtds/podcast-1.0.dtd",
  740. version: "2.0",
  741. },
  742. _content: {
  743. channel: [
  744. { title: "Title" },
  745. { link: "google.com" },
  746. { language: "en-us" },
  747. { copyright: "Copyright 2011" },
  748. { "itunes:subtitle": "Subtitle" },
  749. { "itunes:author": "Author" },
  750. { "itunes:summary": "Summary" },
  751. { description: "Description" },
  752. {
  753. "itunes:owner": {
  754. "itunes:name": "Name",
  755. "itunes:email": "Email",
  756. },
  757. },
  758. {
  759. _name: "itunes:image",
  760. _attrs: {
  761. href: "image.jpg",
  762. },
  763. },
  764. {
  765. _name: "itunes:category",
  766. _attrs: {
  767. text: "Technology",
  768. },
  769. _content: {
  770. _name: "itunes:category",
  771. _attrs: {
  772. text: "Gadgets",
  773. },
  774. },
  775. },
  776. {
  777. _name: "itunes:category",
  778. _attrs: {
  779. text: "TV &amp; Film",
  780. },
  781. },
  782. {
  783. item: [
  784. { title: "Podcast Title" },
  785. { "itunes:author": "Author" },
  786. { "itunes:subtitle": "Subtitle" },
  787. { "itunes:summary": "Summary" },
  788. { "itunes:image": "image.jpg" },
  789. {
  790. _name: "enclosure",
  791. _attrs: {
  792. url: "http://example.com/podcast.m4a",
  793. length: "8727310",
  794. type: "audio/x-m4a",
  795. },
  796. },
  797. { guid: "http://example.com/archive/aae20050615.m4a" },
  798. { pubDate: "Wed, 15 Jun 2011 19:00:00 GMT" },
  799. { "itunes:duration": "7:04" },
  800. { "itunes:keywords": "salt, pepper, shaker, exciting" },
  801. ],
  802. },
  803. {
  804. item: [
  805. { title: "Podcast2 Title" },
  806. { "itunes:author": "Author2" },
  807. { "itunes:subtitle": "Subtitle2" },
  808. { "itunes:summary": "Summary2" },
  809. { "itunes:image": "image2.jpg" },
  810. {
  811. _name: "enclosure",
  812. _attrs: {
  813. url: "http://example.com/podcast2.m4a",
  814. length: "655555",
  815. type: "audio/x-m4a",
  816. },
  817. },
  818. { guid: "http://example.com/archive/aae2.m4a" },
  819. { pubDate: "Wed, 15 Jul 2011 19:00:00 GMT" },
  820. { "itunes:duration": "11:20" },
  821. { "itunes:keywords": "foo, bar" },
  822. ],
  823. },
  824. ],
  825. },
  826. };
  827. const config = {
  828. header: true,
  829. indent: " ",
  830. };
  831. const result = toXML(val, config);
  832. const expectedResult = `<?xml version="1.0" encoding="UTF-8"?>
  833. <rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
  834. <channel>
  835. <title>Title</title>
  836. <link>google.com</link>
  837. <language>en-us</language>
  838. <copyright>Copyright 2011</copyright>
  839. <itunes:subtitle>Subtitle</itunes:subtitle>
  840. <itunes:author>Author</itunes:author>
  841. <itunes:summary>Summary</itunes:summary>
  842. <description>Description</description>
  843. <itunes:owner>
  844. <itunes:name>Name</itunes:name>
  845. <itunes:email>Email</itunes:email>
  846. </itunes:owner>
  847. <itunes:image href="image.jpg"/>
  848. <itunes:category text="Technology">
  849. <itunes:category text="Gadgets"/>
  850. </itunes:category>
  851. <itunes:category text="TV &amp; Film"/>
  852. <item>
  853. <title>Podcast Title</title>
  854. <itunes:author>Author</itunes:author>
  855. <itunes:subtitle>Subtitle</itunes:subtitle>
  856. <itunes:summary>Summary</itunes:summary>
  857. <itunes:image>image.jpg</itunes:image>
  858. <enclosure url="http://example.com/podcast.m4a" length="8727310" type="audio/x-m4a"/>
  859. <guid>http://example.com/archive/aae20050615.m4a</guid>
  860. <pubDate>Wed, 15 Jun 2011 19:00:00 GMT</pubDate>
  861. <itunes:duration>7:04</itunes:duration>
  862. <itunes:keywords>salt, pepper, shaker, exciting</itunes:keywords>
  863. </item>
  864. <item>
  865. <title>Podcast2 Title</title>
  866. <itunes:author>Author2</itunes:author>
  867. <itunes:subtitle>Subtitle2</itunes:subtitle>
  868. <itunes:summary>Summary2</itunes:summary>
  869. <itunes:image>image2.jpg</itunes:image>
  870. <enclosure url="http://example.com/podcast2.m4a" length="655555" type="audio/x-m4a"/>
  871. <guid>http://example.com/archive/aae2.m4a</guid>
  872. <pubDate>Wed, 15 Jul 2011 19:00:00 GMT</pubDate>
  873. <itunes:duration>11:20</itunes:duration>
  874. <itunes:keywords>foo, bar</itunes:keywords>
  875. </item>
  876. </channel>
  877. </rss>`;
  878. assert.equal(result, expectedResult);
  879. });
  880. it("11 filter", () => {
  881. const val = {
  882. foo: "<a>",
  883. bar: '"b"',
  884. baz: "'&whee'",
  885. };
  886. const config = {
  887. filter: {
  888. "<": "&lt;",
  889. ">": "&gt;",
  890. '"': "&quot;",
  891. "'": "&apos;",
  892. "&": "&amp;",
  893. },
  894. };
  895. const result = toXML(val, config);
  896. const expectedResult =
  897. "<foo>&lt;a&gt;</foo><bar>&quot;b&quot;</bar><baz>&apos;&amp;whee&apos;</baz>";
  898. assert.equal(result, expectedResult);
  899. });
  900. it("11b attributes filter", () => {
  901. const val = {
  902. _name: "foo",
  903. _content: "bar",
  904. _attrs: {
  905. a: "http://example.com/?test='1'&foo=<bar>&whee=\"sha\"",
  906. b: "http://example2.com/?test='2'&md=<5>&sum=\"sha\"",
  907. },
  908. };
  909. const config = {
  910. attributesFilter: {
  911. "<": "&lt;",
  912. ">": "&gt;",
  913. '"': "&quot;",
  914. "'": "&apos;",
  915. "&": "&amp;",
  916. },
  917. };
  918. const result = toXML(val, config);
  919. const expectedResult =
  920. '<foo a="http://example.com/?test=&apos;1&apos;&amp;foo=&lt;bar&gt;&amp;whee=&quot;sha&quot;" b="http://example2.com/?test=&apos;2&apos;&amp;md=&lt;5&gt;&amp;sum=&quot;sha&quot;">bar</foo>';
  921. assert.equal(result, expectedResult);
  922. });
  923. it("12 avoiding self closing tags", () => {
  924. const val = [
  925. {
  926. _name: "foo",
  927. _content: "",
  928. _selfCloseTag: false,
  929. },
  930. {
  931. _name: "bar",
  932. _content: undefined,
  933. _selfCloseTag: false,
  934. },
  935. ];
  936. const result = toXML(val);
  937. const expectedResult = "<foo></foo><bar></bar>";
  938. assert.equal(result, expectedResult);
  939. });
  940. it("13 custom xml header", () => {
  941. const val = {
  942. foo: "bar",
  943. };
  944. const config = {
  945. header: '<?xml version="1.0" encoding="UTF-16" standalone="yes"?>',
  946. };
  947. const result = toXML(val, config);
  948. const expectedResult =
  949. '<?xml version="1.0" encoding="UTF-16" standalone="yes"?><foo>bar</foo>';
  950. assert.equal(result, expectedResult);
  951. });
  952. it("14 emoji attributes", () => {
  953. const val = {
  954. html: {
  955. _attrs: {
  956. "⚡": true,
  957. },
  958. },
  959. };
  960. const result = toXML(val);
  961. const expectedResult = "<html ⚡/>";
  962. assert.equal(result, expectedResult);
  963. });
  964. it("15 duplicate attribute keys", () => {
  965. const val = {
  966. html: {
  967. _attrs: [{ lang: "en" }, { lang: "klingon" }],
  968. },
  969. };
  970. const result = toXML(val);
  971. const expectedResult = '<html lang="en" lang="klingon"/>';
  972. assert.equal(result, expectedResult);
  973. });
  974. });
  975. describe("issues", () => {
  976. it("issue #33: array of primitives", () => {
  977. const val = {
  978. x: [1, 2, 3],
  979. };
  980. const result = toXML(val);
  981. const expectedResult = "<x>1</x><x>2</x><x>3</x>";
  982. assert.equal(result, expectedResult);
  983. });
  984. it("issue #33: array of primitives 2", () => {
  985. const val = {
  986. a: {
  987. x: [1, 2, 3],
  988. },
  989. };
  990. const result = toXML(val);
  991. const expectedResult = "<a><x>1</x><x>2</x><x>3</x></a>";
  992. assert.equal(result, expectedResult);
  993. });
  994. it("issue #33: array of primitives 2 with indent", () => {
  995. const val = {
  996. a: {
  997. x: [1, 2, 3],
  998. },
  999. };
  1000. const config = { indent: " " };
  1001. const result = toXML(val, config);
  1002. const expectedResult = "<a>\n <x>1</x>\n <x>2</x>\n <x>3</x>\n</a>";
  1003. assert.equal(result, expectedResult);
  1004. });
  1005. it("issue #33: array of objects", () => {
  1006. const val = {
  1007. a: {
  1008. x: [
  1009. { b: 1, c: 2 },
  1010. { d: 3, e: 4 },
  1011. { f: 5, g: 6 },
  1012. ],
  1013. },
  1014. };
  1015. const result = toXML(val);
  1016. const expectedResult =
  1017. "<a><x><b>1</b><c>2</c><d>3</d><e>4</e><f>5</f><g>6</g></x></a>";
  1018. assert.equal(result, expectedResult);
  1019. });
  1020. it("issue #33: array of objects jstoxml format", () => {
  1021. const val = {
  1022. a: [
  1023. {
  1024. _name: "foo",
  1025. _content: "1",
  1026. },
  1027. {
  1028. _name: "foo",
  1029. _content: "2",
  1030. },
  1031. ],
  1032. };
  1033. const result = toXML(val);
  1034. const expectedResult = "<a><foo>1</foo><foo>2</foo></a>";
  1035. assert.equal(result, expectedResult);
  1036. });
  1037. it("issue #34: array of array", () => {
  1038. const val = {
  1039. Response: [
  1040. [
  1041. {
  1042. _name: "Play",
  1043. _content: "first sound",
  1044. },
  1045. {
  1046. _name: "Play",
  1047. _content: "second sound",
  1048. },
  1049. ],
  1050. ],
  1051. };
  1052. const result = toXML(val);
  1053. const expectedResult =
  1054. "<Response><Play>first sound</Play><Play>second sound</Play></Response>";
  1055. assert.equal(result, expectedResult);
  1056. });
  1057. it("issue #34", () => {
  1058. const val = { t: [{ foo: "bar" }, { foo: "bar2" }] };
  1059. const result = toXML(val);
  1060. const expectedResult = "<t><foo>bar</foo><foo>bar2</foo></t>";
  1061. assert.equal(result, expectedResult);
  1062. });
  1063. it("issue #34", () => {
  1064. const val = {
  1065. t: [
  1066. { _name: "foo", _content: "bar" },
  1067. { _name: "foo", _content: "bar2" },
  1068. ],
  1069. };
  1070. const result = toXML(val);
  1071. const expectedResult = "<t><foo>bar</foo><foo>bar2</foo></t>";
  1072. assert.equal(result, expectedResult);
  1073. });
  1074. it("issue #38", () => {
  1075. const getFooVal = (iteration) => iteration;
  1076. const getCurrentTime = (iterations) => {
  1077. return Array(iterations)
  1078. .fill(null)
  1079. .map((foo, index) => {
  1080. return {
  1081. currentTime: {
  1082. foo: getFooVal.bind(null, index + 1),
  1083. },
  1084. };
  1085. });
  1086. };
  1087. const val = {
  1088. invoice1: [
  1089. {
  1090. invoice: "a",
  1091. },
  1092. getCurrentTime.bind(null, 3),
  1093. {
  1094. foo2: "a",
  1095. },
  1096. ],
  1097. };
  1098. const config = { indent: " " };
  1099. const result = toXML(val, config);
  1100. const expectedResult = `<invoice1>
  1101. <invoice>a</invoice>
  1102. <currentTime>
  1103. <foo>1</foo>
  1104. </currentTime>
  1105. <currentTime>
  1106. <foo>2</foo>
  1107. </currentTime>
  1108. <currentTime>
  1109. <foo>3</foo>
  1110. </currentTime>
  1111. <foo2>a</foo2>
  1112. </invoice1>`;
  1113. assert.equal(result, expectedResult);
  1114. });
  1115. it("issue #40 forced separator, no indent", () => {
  1116. const val = [{ a: "A Value" }, "\n", { b: "B Value" }];
  1117. const result = toXML(val);
  1118. const expectedResult = `<a>A Value</a>
  1119. <b>B Value</b>`;
  1120. assert.equal(result, expectedResult);
  1121. });
  1122. it("issue #40 array with indent", () => {
  1123. const val = [{ a: "A Value" }, { b: "B Value" }];
  1124. const result = toXML(val, { indent: " " });
  1125. const expectedResult = `<a>A Value</a>
  1126. <b>B Value</b>`;
  1127. assert.equal(result, expectedResult);
  1128. });
  1129. it("issue #40 array without indent", () => {
  1130. const val = [{ a: "A Value" }, { b: "B Value" }];
  1131. const result = toXML(val);
  1132. const expectedResult = `<a>A Value</a><b>B Value</b>`;
  1133. assert.equal(result, expectedResult);
  1134. });
  1135. it("issue #40 object with indent", () => {
  1136. const val = {
  1137. a: "A Value",
  1138. b: "B Value",
  1139. };
  1140. const result = toXML(val, { indent: " " });
  1141. const expectedResult = `<a>A Value</a>
  1142. <b>B Value</b>`;
  1143. assert.equal(result, expectedResult);
  1144. });
  1145. it("issue #40 object without indent", () => {
  1146. const val = {
  1147. a: "A Value",
  1148. b: "B Value",
  1149. };
  1150. const result = toXML(val);
  1151. const expectedResult = `<a>A Value</a><b>B Value</b>`;
  1152. assert.equal(result, expectedResult);
  1153. });
  1154. it("comments 1", () => {
  1155. const val = {
  1156. _comment: "test comment",
  1157. a: "foo"
  1158. };
  1159. const result = toXML(val);
  1160. const expectedResult = `<!-- test comment --><a>foo</a>`;
  1161. assert.equal(result, expectedResult);
  1162. });
  1163. it("comments 2", () => {
  1164. const val = {
  1165. _comment: "test comment",
  1166. a: "foo"
  1167. };
  1168. const result = toXML(val, { indent: ' ' });
  1169. const expectedResult = `<!-- test comment -->
  1170. <a>foo</a>`;
  1171. assert.equal(result, expectedResult);
  1172. });
  1173. it("comments 3", () => {
  1174. const val = {
  1175. _comment: "comment 1",
  1176. b: {
  1177. _comment: "comment 2",
  1178. a: "foo"
  1179. }
  1180. };
  1181. const result = toXML(val, { indent: ' ' });
  1182. const expectedResult = `<!-- comment 1 -->
  1183. <b>
  1184. <!-- comment 2 -->
  1185. <a>foo</a>
  1186. </b>`;
  1187. assert.equal(result, expectedResult);
  1188. });
  1189. it("comments 4", () => {
  1190. const val = {
  1191. _comment: "comment 1",
  1192. b: [
  1193. { _comment: "comment 2" },
  1194. { _comment: "comment 3" },
  1195. { a: "foo" }
  1196. ]
  1197. };
  1198. const result = toXML(val, { indent: ' ' });
  1199. const expectedResult = `<!-- comment 1 -->
  1200. <b>
  1201. <!-- comment 2 -->
  1202. <!-- comment 3 -->
  1203. <a>foo</a>
  1204. </b>`;
  1205. assert.equal(result, expectedResult);
  1206. });
  1207. });
  1208. it("comments 5", () => {
  1209. const val = {
  1210. _comment: 'Some important comment',
  1211. a: {
  1212. b: [1, 2, 3]
  1213. }
  1214. };
  1215. const result = toXML(val, { indent: ' ' });
  1216. const expectedResult = `<!-- Some important comment -->
  1217. <a>
  1218. <b>1</b>
  1219. <b>2</b>
  1220. <b>3</b>
  1221. </a>`;
  1222. assert.equal(result, expectedResult);
  1223. });
  1224. it("comments 6", () => {
  1225. const val = [
  1226. { _comment: 'Some important comment' },
  1227. { _comment: 'This is a very long comment!' },
  1228. { _comment: 'More important exposition!' },
  1229. { a: { b: [1, 2, 3] } }
  1230. ];
  1231. const result = toXML(val, { indent: ' ' });
  1232. const expectedResult = `<!-- Some important comment -->
  1233. <!-- This is a very long comment! -->
  1234. <!-- More important exposition! -->
  1235. <a>
  1236. <b>1</b>
  1237. <b>2</b>
  1238. <b>3</b>
  1239. </a>`;
  1240. assert.equal(result, expectedResult);
  1241. });
  1242. });