Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d06e7c3

Browse files
committedMar 19, 2025·
QuickJS: added xml module.
1 parent a4379e4 commit d06e7c3

File tree

8 files changed

+2558
-292
lines changed

8 files changed

+2558
-292
lines changed
 

‎.github/workflows/check-pr.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
run: |
3333
sudo dpkg --add-architecture i386
3434
sudo apt-get update
35-
sudo apt-get install -y gcc-multilib libc6:i386 libpcre2-dev:i386 zlib1g-dev:i386
35+
sudo apt-get install -y gcc-multilib libc6:i386 libssl-dev:i386 libpcre2-dev:i386 zlib1g-dev:i386 libxml2-dev:i386
3636
3737
- name: Check out nginx
3838
run: |

‎auto/qjs_modules

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ if [ $NJS_OPENSSL = YES -a $NJS_HAVE_OPENSSL = YES ]; then
3333
. auto/qjs_module
3434
fi
3535

36+
if [ $NJS_LIBXML2 = YES -a $NJS_HAVE_LIBXML2 = YES ]; then
37+
njs_module_name=qjs_xml_module
38+
njs_module_incs=
39+
njs_module_srcs=external/qjs_xml_module.c
40+
41+
. auto/qjs_module
42+
fi
43+
3644
if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then
3745
njs_module_name=qjs_zlib_module
3846
njs_module_incs=

‎external/qjs_xml_module.c

+2,151
Large diffs are not rendered by default.

‎src/qjs.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@
4444
#define QJS_CORE_CLASS_ID_WEBCRYPTO_KEY (QJS_CORE_CLASS_ID_OFFSET + 7)
4545
#define QJS_CORE_CLASS_CRYPTO_HASH (QJS_CORE_CLASS_ID_OFFSET + 8)
4646
#define QJS_CORE_CLASS_CRYPTO_HMAC (QJS_CORE_CLASS_ID_OFFSET + 9)
47-
#define QJS_CORE_CLASS_ID_LAST (QJS_CORE_CLASS_ID_OFFSET + 10)
47+
#define QJS_CORE_CLASS_ID_XML_DOC (QJS_CORE_CLASS_ID_OFFSET + 10)
48+
#define QJS_CORE_CLASS_ID_XML_NODE (QJS_CORE_CLASS_ID_OFFSET + 11)
49+
#define QJS_CORE_CLASS_ID_XML_ATTR (QJS_CORE_CLASS_ID_OFFSET + 12)
50+
#define QJS_CORE_CLASS_ID_LAST (QJS_CORE_CLASS_ID_OFFSET + 13)
4851

4952

5053
typedef JSModuleDef *(*qjs_addon_init_pt)(JSContext *ctx, const char *name);

‎src/test/njs_unit_test.c

-284
Original file line numberDiff line numberDiff line change
@@ -20239,279 +20239,6 @@ static njs_unit_test_t njs_fs_module_test[] =
2023920239
};
2024020240

2024120241

20242-
#define NJS_XML_DOC "const xml = require('xml');" \
20243-
"let data = `<note><to b=\"bar\" a= \"foo\" >Tove</to><from>Jani</from></note>`;" \
20244-
"let doc = xml.parse(data);"
20245-
20246-
20247-
static njs_unit_test_t njs_xml_test[] =
20248-
{
20249-
{ njs_str(NJS_XML_DOC
20250-
"[doc.note.$name,"
20251-
" doc.note.to.$text,"
20252-
" doc.note.$parent,"
20253-
" doc.note.to.$parent.$name,"
20254-
" doc.note.$tag$to.$text,"
20255-
" doc.note.to.$attr$b,"
20256-
" doc.note.$tags[1].$text,"
20257-
" doc.note.$tags$from[0].$text]"),
20258-
njs_str("note,Tove,,note,Tove,bar,Jani,Jani") },
20259-
20260-
{ njs_str("const xml = require('xml');"
20261-
"let doc = xml.parse(`<root><foo>FOO</foo><foo>BAR</foo></root>`);"
20262-
"[doc.root.$tags$foo[0].$text,"
20263-
" doc.root.$tags$foo[1].$text,"
20264-
" doc.root.$tags$bar.length,"
20265-
" doc.root.$tags$.length]"),
20266-
njs_str("FOO,BAR,0,2") },
20267-
20268-
{ njs_str("const xml = require('xml');"
20269-
"let doc = xml.parse(`GARBAGE`)"),
20270-
njs_str("Error: failed to parse XML (libxml2: \"Start tag expected, '<' not found\" at 1:1)") },
20271-
20272-
{ njs_str("const xml = require('xml');"
20273-
"let doc = xml.parse(`<r><a></a>TEXT</r>`);"
20274-
"doc.r.$text"),
20275-
njs_str("TEXT") },
20276-
20277-
{ njs_str("const xml = require('xml');"
20278-
"let doc = xml.parse(`<r>俄语<a></a>данные</r>`);"
20279-
"doc.r.$text[2]"),
20280-
njs_str("д") },
20281-
20282-
{ njs_str("const xml = require('xml');"
20283-
"let doc = xml.parse(`<俄语 լեզու=\"ռուսերեն\">данные</俄语>`);"
20284-
"[doc['俄语'].$name[1],"
20285-
" doc['俄语']['$attr$լեզու'][7],"
20286-
" doc['俄语'].$text[5]]"),
20287-
njs_str("语,ն,е") },
20288-
20289-
{ njs_str("const xml = require('xml');"
20290-
"var doc = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"><n1:elem1 xmlns:n1=\"http://b\">"
20291-
"<!-- comment -->foo</n1:elem1></n0:pdu>`);"
20292-
"[xml.c14n(doc.pdu.elem1),"
20293-
" xml.exclusiveC14n(doc.pdu.elem1),"
20294-
" xml.exclusiveC14n(doc.pdu.elem1, null, 1),"
20295-
" xml.exclusiveC14n(doc.pdu.elem1, null, false, 'n0 n1')]"
20296-
".map(v => (new TextDecoder().decode(v)))"),
20297-
njs_str("<n1:elem1 xmlns:n0=\"http://a\" xmlns:n1=\"http://b\">foo</n1:elem1>,"
20298-
"<n1:elem1 xmlns:n1=\"http://b\">foo</n1:elem1>,"
20299-
"<n1:elem1 xmlns:n1=\"http://b\"><!-- comment -->foo</n1:elem1>,"
20300-
"<n1:elem1 xmlns:n0=\"http://a\" xmlns:n1=\"http://b\">foo</n1:elem1>") },
20301-
20302-
{ njs_str(NJS_XML_DOC
20303-
"let dec = new TextDecoder();"
20304-
"dec.decode(xml.exclusiveC14n(doc.note))"),
20305-
njs_str("<note><to a=\"foo\" b=\"bar\">Tove</to><from>Jani</from></note>") },
20306-
20307-
{ njs_str(NJS_XML_DOC
20308-
"let dec = new TextDecoder();"
20309-
"dec.decode(xml.serialize(doc.note))"),
20310-
njs_str("<note><to a=\"foo\" b=\"bar\">Tove</to><from>Jani</from></note>") },
20311-
20312-
{ njs_str(NJS_XML_DOC
20313-
"xml.serializeToString(doc.note)"),
20314-
njs_str("<note><to a=\"foo\" b=\"bar\">Tove</to><from>Jani</from></note>") },
20315-
20316-
{ njs_str(NJS_XML_DOC
20317-
"let dec = new TextDecoder();"
20318-
"dec.decode(xml.exclusiveC14n(doc.note, doc.note.to))"),
20319-
njs_str("<note><from>Jani</from></note>") },
20320-
20321-
{ njs_str(NJS_XML_DOC
20322-
"njs.dump(doc)"),
20323-
njs_str("XMLDoc {note:XMLNode {$name:'note',"
20324-
"$tags:[XMLNode {$name:'to',"
20325-
"$attrs:XMLAttr {b:'bar',a:'foo'},"
20326-
"$text:'Tove'},"
20327-
"XMLNode {$name:'from',$text:'Jani'}]}}") },
20328-
20329-
{ njs_str(NJS_XML_DOC
20330-
"JSON.stringify(doc)"),
20331-
njs_str("{\"note\":{\"$name\":\"note\",\"$tags\":"
20332-
"[{\"$name\":\"to\",\"$attrs\":{\"b\":\"bar\",\"a\":\"foo\"},"
20333-
"\"$text\":\"Tove\"},{\"$name\":\"from\",\"$text\":\"Jani\"}]}}") },
20334-
20335-
{ njs_str("var xml = require('xml');"
20336-
"var doc = xml.parse(`<r></r>`); xml.exclusiveC14n(doc, 1)"),
20337-
njs_str("TypeError: \"excluding\" argument is not a XMLNode object") },
20338-
20339-
{ njs_str(NJS_XML_DOC
20340-
"doc.$root.$text"),
20341-
njs_str("ToveJani") },
20342-
20343-
{ njs_str(NJS_XML_DOC
20344-
"doc.$root.$text = 'WAKA';"
20345-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20346-
njs_str("WAKA,<note>WAKA</note>") },
20347-
20348-
{ njs_str(NJS_XML_DOC
20349-
"doc.$root.setText('WAKA');"
20350-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20351-
njs_str("WAKA,<note>WAKA</note>") },
20352-
20353-
{ njs_str(NJS_XML_DOC
20354-
"doc.$root.$text = '<WA&KA>';"
20355-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20356-
njs_str("<WA&KA>,<note>&lt;WA&amp;KA&gt;</note>") },
20357-
20358-
{ njs_str(NJS_XML_DOC
20359-
"doc.$root.setText('<WA&KA>');"
20360-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20361-
njs_str("<WA&KA>,<note>&lt;WA&amp;KA&gt;</note>") },
20362-
20363-
{ njs_str(NJS_XML_DOC
20364-
"doc.$root.$text = '\"WAKA\"';"
20365-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20366-
njs_str("\"WAKA\",<note>\"WAKA\"</note>") },
20367-
20368-
{ njs_str(NJS_XML_DOC
20369-
"doc.$root.$text = '';"
20370-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20371-
njs_str(",<note></note>") },
20372-
20373-
{ njs_str(NJS_XML_DOC
20374-
"doc.$root.setText();"
20375-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20376-
njs_str(",<note></note>") },
20377-
20378-
{ njs_str(NJS_XML_DOC
20379-
"doc.$root.setText(null);"
20380-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20381-
njs_str(",<note></note>") },
20382-
20383-
{ njs_str(NJS_XML_DOC
20384-
"doc.$root.removeText();"
20385-
"[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"),
20386-
njs_str(",<note></note>") },
20387-
20388-
{ njs_str(NJS_XML_DOC
20389-
"let to = doc.note.to;"
20390-
"doc.$root.$text = '';"
20391-
"[to.$name, to.$text, to.$attr$b, to.$parent.$name]"),
20392-
njs_str("to,Tove,bar,note") },
20393-
20394-
{ njs_str(NJS_XML_DOC
20395-
"doc.$root.$text = 'WAKA';"
20396-
"doc.$root.$attr$aaa = 'foo';"
20397-
"doc.$root.$attr$bbb = 'bar';"
20398-
"[doc.$root.$attr$aaa, (new TextDecoder).decode(xml.c14n(doc))]"),
20399-
njs_str("foo,<note aaa=\"foo\" bbb=\"bar\">WAKA</note>") },
20400-
20401-
{ njs_str(NJS_XML_DOC
20402-
"doc.$root.$text = 'WAKA';"
20403-
"doc.$root.setAttribute('aaa', 'foo');"
20404-
"doc.$root.setAttribute('bbb', '<bar\"');"
20405-
"doc.$root.setAttribute('aaa', 'foo2');"
20406-
"[doc.$root.$attr$aaa, (new TextDecoder).decode(xml.c14n(doc))]"),
20407-
njs_str("foo2,<note aaa=\"foo2\" bbb=\"&lt;bar&quot;\">WAKA</note>") },
20408-
20409-
{ njs_str(NJS_XML_DOC
20410-
"doc.note.to.setAttribute('a', null);"
20411-
"(new TextDecoder).decode(xml.c14n(doc.note.to))"),
20412-
njs_str("<to b=\"bar\">Tove</to>") },
20413-
20414-
{ njs_str(NJS_XML_DOC
20415-
"doc.$root.setAttribute('<', 'xxx')"),
20416-
njs_str("TypeError: attribute name \"<\" is not valid") },
20417-
20418-
{ njs_str(NJS_XML_DOC
20419-
"doc.$root.$text = 'WAKA';"
20420-
"doc.$root['$attr$' + 'x'.repeat(1024)] = 1;"),
20421-
njs_str("InternalError: njs_xml_str_to_c_string() very long string, length >= 511") },
20422-
20423-
{ njs_str(NJS_XML_DOC
20424-
"delete doc.note.to.$attr$a;"
20425-
"(new TextDecoder).decode(xml.c14n(doc.note.to))"),
20426-
njs_str("<to b=\"bar\">Tove</to>") },
20427-
20428-
{ njs_str(NJS_XML_DOC
20429-
"doc.note.to.removeAttribute('a');"
20430-
"(new TextDecoder).decode(xml.c14n(doc.note.to))"),
20431-
njs_str("<to b=\"bar\">Tove</to>") },
20432-
20433-
{ njs_str(NJS_XML_DOC
20434-
"delete doc.note.to.removeAttribute('c');"
20435-
"(new TextDecoder).decode(xml.c14n(doc.note.to))"),
20436-
njs_str("<to a=\"foo\" b=\"bar\">Tove</to>") },
20437-
20438-
{ njs_str(NJS_XML_DOC
20439-
"delete doc.note.to.removeAllAttributes();"
20440-
"(new TextDecoder).decode(xml.c14n(doc.note.to))"),
20441-
njs_str("<to>Tove</to>") },
20442-
20443-
{ njs_str(NJS_XML_DOC
20444-
"delete doc.note.$tag$to;"
20445-
"(new TextDecoder).decode(xml.c14n(doc))"),
20446-
njs_str("<note><from>Jani</from></note>") },
20447-
20448-
{ njs_str(NJS_XML_DOC
20449-
"delete doc.note.to;"
20450-
"(new TextDecoder).decode(xml.c14n(doc))"),
20451-
njs_str("<note><from>Jani</from></note>") },
20452-
20453-
{ njs_str("var xml = require('xml');"
20454-
"var doc = xml.parse(`<r><a/><b/><a/></r>`);"
20455-
"delete doc.$root.a;"
20456-
"(new TextDecoder).decode(xml.c14n(doc))"),
20457-
njs_str("<r><b></b></r>") },
20458-
20459-
{ njs_str("var xml = require('xml');"
20460-
"var doc = xml.parse(`<r><a/><b/><a/></r>`);"
20461-
"doc.$root.removeChildren('c');"
20462-
"(new TextDecoder).decode(xml.c14n(doc))"),
20463-
njs_str("<r><a></a><b></b><a></a></r>") },
20464-
20465-
{ njs_str("var xml = require('xml');"
20466-
"var doc = xml.parse(`<r><a/><b/><a/></r>`);"
20467-
"doc.$root.removeChildren('a');"
20468-
"(new TextDecoder).decode(xml.c14n(doc))"),
20469-
njs_str("<r><b></b></r>") },
20470-
20471-
{ njs_str("var xml = require('xml');"
20472-
"var doc = xml.parse(`<r><a/><b/><a/></r>`);"
20473-
"doc.$root.removeChildren();"
20474-
"(new TextDecoder).decode(xml.c14n(doc))"),
20475-
njs_str("<r></r>") },
20476-
20477-
{ njs_str(NJS_XML_DOC
20478-
"doc.note.$tags = [];"
20479-
"(new TextDecoder).decode(xml.c14n(doc))"),
20480-
njs_str("<note></note>") },
20481-
20482-
{ njs_str(NJS_XML_DOC
20483-
"var doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);"
20484-
"doc.note.addChild(doc2);"
20485-
"(new TextDecoder).decode(xml.c14n(doc))"),
20486-
njs_str("<note xmlns:n0=\"http://a\"><to a=\"foo\" b=\"bar\">Tove</to><from>Jani</from><n0:pdu></n0:pdu></note>") },
20487-
20488-
{ njs_str(NJS_XML_DOC
20489-
"var doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);"
20490-
"doc.note.addChild(doc2);"
20491-
"doc.note.addChild(doc2);"
20492-
"(new TextDecoder).decode(xml.c14n(doc))"),
20493-
njs_str("<note xmlns:n0=\"http://a\"><to a=\"foo\" b=\"bar\">Tove</to><from>Jani</from>"
20494-
"<n0:pdu></n0:pdu><n0:pdu></n0:pdu></note>") },
20495-
20496-
{ njs_str(NJS_XML_DOC
20497-
"delete doc.note.$tags$;"
20498-
"(new TextDecoder).decode(xml.c14n(doc))"),
20499-
njs_str("<note></note>") },
20500-
20501-
{ njs_str(NJS_XML_DOC
20502-
"var doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);"
20503-
"doc.note.$tags = [doc.note.to, doc2];"
20504-
"(new TextDecoder).decode(xml.c14n(doc))"),
20505-
njs_str("<note xmlns:n0=\"http://a\"><to a=\"foo\" b=\"bar\">Tove</to><n0:pdu></n0:pdu></note>") },
20506-
20507-
{ njs_str(NJS_XML_DOC
20508-
"var doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);"
20509-
"doc.note.$tags = [doc2, doc.note.to];"
20510-
"(new TextDecoder).decode(xml.c14n(doc))"),
20511-
njs_str("<note xmlns:n0=\"http://a\"><n0:pdu></n0:pdu><to a=\"foo\" b=\"bar\">Tove</to></note>") },
20512-
};
20513-
20514-
2051520242
static njs_unit_test_t njs_module_test[] =
2051620243
{
2051720244
{ njs_str("function f(){return 2}; var f; f()"),
@@ -23288,17 +23015,6 @@ static njs_test_suite_t njs_suites[] =
2328823015
njs_nitems(njs_disabled_denormals_test),
2328923016
njs_disabled_denormals_tests },
2329023017

23291-
{
23292-
#if (NJS_HAVE_LIBXML2 && !NJS_HAVE_MEMORY_SANITIZER)
23293-
njs_str("xml"),
23294-
#else
23295-
njs_str(""),
23296-
#endif
23297-
{ .externals = 1, .repeat = 1, .unsafe = 1 },
23298-
njs_xml_test,
23299-
njs_nitems(njs_xml_test),
23300-
njs_unit_test },
23301-
2330223018
{ njs_str("module"),
2330323019
{ .repeat = 1, .module = 1, .unsafe = 1 },
2330423020
njs_module_test,

‎test/xml/external_entity_ignored.t.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
/*---
2-
includes: [compatXml.js, compatNjs.js]
2+
includes: [compatNjs.js]
33
flags: []
44
paths: []
55
---*/
66

7+
import xml from 'xml';
8+
79
let data = `<?xml version="1.0"?>
810
<!DOCTYPE foo [
911
<!ENTITY c PUBLIC "bar" "extern_entity.txt">
1012
]>
1113
<root>&c;</root>
1214
`;
1315

14-
if (has_njs() && has_xml()) {
16+
if (has_njs()) {
1517
let doc = xml.parse(data);
1618
assert.sameValue(doc.$root.$text, "");
1719
}

‎test/xml/saml_verify.t.mjs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/*---
2-
includes: [compatFs.js, compatXml.js, compatWebcrypto.js, compatNjs.js, runTsuite.js]
2+
includes: [compatFs.js, compatWebcrypto.js, compatNjs.js, runTsuite.js]
33
flags: [async]
44
---*/
55

6+
import xml from 'xml';
7+
68
async function verify(params) {
79
let file_data = fs.readFileSync(`test/xml/${params.saml}`);
810
let key_data = fs.readFileSync(`test/webcrypto/${params.key.file}`);
@@ -11,14 +13,13 @@ async function verify(params) {
1113
let sign_key_data = fs.readFileSync(`test/webcrypto/${params.key.sign_file}`);
1214
let signed = await signSAML(xml.parse(file_data), sign_key_data);
1315
file_data = xml.c14n(signed);
14-
//console.log((new TextDecoder()).decode(file_data));
1516
}
1617

1718
let saml = xml.parse(file_data);
1819

1920
let r = await verifySAMLSignature(saml, key_data)
2021
.catch (e => {
21-
if (e.toString().startsWith("Error: EVP_PKEY_CTX_set_signature_md() failed")) {
22+
if (e.message.startsWith("EVP_PKEY_CTX_set_signature_md() failed")) {
2223
/* Red Hat Enterprise Linux: SHA-1 is disabled */
2324
return "SKIPPED";
2425
}
@@ -273,7 +274,7 @@ async function signatureSAML(signature, key_data, produce) {
273274

274275
let saml_verify_tsuite = {
275276
name: "SAML verify",
276-
skip: () => (!has_njs() || !has_webcrypto() || !has_xml()),
277+
skip: () => (!has_njs() || !has_webcrypto()),
277278
T: verify,
278279
opts: {
279280
key: { fmt: "spki", file: "rsa.spki" },

‎test/xml/xml.t.mjs

+385
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
1+
/*---
2+
includes: [compatFs.js, compatNjs.js, runTsuite.js]
3+
flags: [async]
4+
---*/
5+
6+
import xml from 'xml';
7+
8+
let parse_tsuite = {
9+
name: "parse()",
10+
skip: () => (!has_njs()),
11+
T: async (params) => {
12+
let doc = xml.parse(params.doc);
13+
let r = params.get(doc);
14+
15+
if (r !== params.expected) {
16+
throw Error(`unexpected output "${r}" != "${params.expected}"`);
17+
}
18+
19+
return 'SUCCESS';
20+
},
21+
22+
opts: {
23+
doc: `<note><to b=\"bar\" a= \"foo\" >Tove</to><from>Jani</from></note>`,
24+
},
25+
26+
tests: [
27+
{ get: (doc) => doc.nonexist, expected: undefined },
28+
{ get: (doc) => doc.note.$name, expected: 'note' },
29+
{ get: (doc) => doc.note.$text, expected: 'ToveJani' },
30+
{ get: (doc) => doc.note.to.$text, expected: 'Tove' },
31+
{ get: (doc) => doc.note.$tag$to.$text, expected: 'Tove' },
32+
{ get: (doc) => doc.note.$attrs, expected: undefined },
33+
{ get: (doc) => doc.note.to.$attrs.a, expected: 'foo' },
34+
{ get: (doc) => doc.note.to.$attrs.b, expected: 'bar' },
35+
{ get: (doc) => doc.note.to.$attr$b, expected: 'bar' },
36+
{ get: (doc) => doc.note.$attr$a, expected: undefined },
37+
{ get: (doc) => Array.isArray(doc.note.$tags), expected: true },
38+
{ get: (doc) => doc.note.$tags[0].$text, expected: 'Tove' },
39+
{ get: (doc) => doc.note.$tags[1].$text, expected: 'Jani' },
40+
{ get: (doc) => doc.note.$tags$from[0].$text, expected: 'Jani' },
41+
{ get: (doc) => doc.note.$parent, expected: undefined },
42+
{ get: (doc) => doc.note.to.$parent.$name, expected: 'note' },
43+
{ doc: `<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`,
44+
get: (doc) => doc.pdu.$ns,
45+
expected: 'http://a' },
46+
{ doc: `<root><foo>FOO</foo><foo>BAR</foo></root>`,
47+
get: (doc) => doc.root.$tags$foo[0].$text,
48+
expected: 'FOO' },
49+
{ doc: `<root><foo>FOO</foo><foo>BAR</foo></root>`,
50+
get: (doc) => doc.root.$tags$foo[1].$text,
51+
expected: 'BAR' },
52+
{ doc: `<root><foo>FOO</foo><foo>BAR</foo></root>`,
53+
get: (doc) => doc.root.$tags$bar.length,
54+
expected: 0 },
55+
{ doc: `<root><foo>FOO</foo><foo>BAR</foo></root>`,
56+
get: (doc) => doc.root.$tags.length,
57+
expected: 2 },
58+
{ doc: `<r><a></a>TEXT</r>`,
59+
get: (doc) => doc.r.$text,
60+
expected: 'TEXT' },
61+
{ doc: `<r><a></a>TEXT</r>`,
62+
get: (doc) => doc.$root.$text,
63+
expected: 'TEXT' },
64+
{ doc: `<r>俄语<a></a>данные</r>`,
65+
get: (doc) => doc.r.$text[2],
66+
expected: 'д' },
67+
{ doc: `<俄语 լեզու=\"ռուսերեն\">данные</俄语>`,
68+
get: (doc) => JSON.stringify([doc['俄语'].$name[1],doc['俄语']['$attr$լեզու'][7],doc['俄语'].$text[5]]),
69+
expected: '["语","ն","е"]' },
70+
71+
{ get: (doc) => JSON.stringify(Object.getOwnPropertyNames(doc)),
72+
expected: '["note"]' },
73+
{ get: (doc) => JSON.stringify(Object.getOwnPropertyNames(doc.note)),
74+
expected: '["$name","$tags"]' },
75+
{ get: (doc) => JSON.stringify(Object.getOwnPropertyNames(doc.note.to)),
76+
expected: '["$name","$attrs","$text"]' },
77+
78+
{ get: (doc) => JSON.stringify(doc.note.to.$attrs),
79+
expected: '{"b":"bar","a":"foo"}' },
80+
{ get: (doc) => JSON.stringify(doc.note.$tags),
81+
expected: '[{"$name":"to","$attrs":{"b":"bar","a":"foo"},"$text":"Tove"},{"$name":"from","$text":"Jani"}]' },
82+
{ get: (doc) => JSON.stringify(doc),
83+
expected: '{"note":{"$name":"note","$tags":[{"$name":"to","$attrs":{"b":"bar","a":"foo"},"$text":"Tove"},{"$name":"from","$text":"Jani"}]}}' },
84+
{ get: (doc) => JSON.stringify(doc.note),
85+
expected: '{"$name":"note","$tags":[{"$name":"to","$attrs":{"b":"bar","a":"foo"},"$text":"Tove"},{"$name":"from","$text":"Jani"}]}' },
86+
87+
{ doc: `GARBAGE`,
88+
exception: 'Error: failed to parse XML (libxml2: "Start tag expected, \'<\' not found" at 1:1)' },
89+
]};
90+
91+
let c14n_tsuite = {
92+
name: "c14n()",
93+
skip: () => (!has_njs()),
94+
T: async (params) => {
95+
let doc = xml.parse(params.doc);
96+
let r = params.call(doc);
97+
98+
if (params.buffer) {
99+
r = new TextDecoder().decode(r);
100+
}
101+
102+
if (r !== params.expected) {
103+
throw Error(`unexpected output "${r}" != "${params.expected}"`);
104+
}
105+
106+
return 'SUCCESS';
107+
},
108+
109+
opts: {
110+
buffer: true,
111+
doc: `<n0:pdu xmlns:n0=\"http://a\"><n1:elem1 xmlns:n1=\"http://b\"><!-- comment -->foo</n1:elem1></n0:pdu>`,
112+
},
113+
114+
tests: [
115+
{ call: (doc) => xml.c14n(doc.pdu.elem1),
116+
expected: `<n1:elem1 xmlns:n0="http://a" xmlns:n1="http://b">foo</n1:elem1>` },
117+
{ call: (doc) => xml.serialize(doc.pdu.elem1),
118+
expected: `<n1:elem1 xmlns:n0="http://a" xmlns:n1="http://b">foo</n1:elem1>` },
119+
{ call: (doc) => xml.serializeToString(doc.pdu.elem1),
120+
buffer: false,
121+
expected: `<n1:elem1 xmlns:n0="http://a" xmlns:n1="http://b">foo</n1:elem1>` },
122+
{ call: (doc) => xml.exclusiveC14n(doc.pdu.elem1),
123+
expected: `<n1:elem1 xmlns:n1="http://b">foo</n1:elem1>` },
124+
{ call: (doc) => xml.exclusiveC14n(doc.pdu.elem1, null, true),
125+
expected: `<n1:elem1 xmlns:n1="http://b"><!-- comment -->foo</n1:elem1>` },
126+
{ call: (doc) => xml.exclusiveC14n(doc.pdu.elem1, null, false, 'n1'),
127+
expected: `<n1:elem1 xmlns:n1="http://b">foo</n1:elem1>` },
128+
{ call: (doc) => xml.exclusiveC14n(doc.pdu.elem1, null, false, 'a b c d e f g h i j'),
129+
expected: `<n1:elem1 xmlns:n1="http://b">foo</n1:elem1>` },
130+
{ doc: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>`,
131+
call: (doc) => xml.c14n(doc.note),
132+
expected: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>` },
133+
{ doc: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>`,
134+
call: (doc) => xml.exclusiveC14n(doc.note),
135+
expected: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>` },
136+
{ doc: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>`,
137+
call: (doc) => xml.exclusiveC14n(doc.note, doc.note.to),
138+
expected: `<note><from>Jani</from></note>` },
139+
{ doc: `<r></r>`,
140+
call: (doc) => xml.exclusiveC14n(doc, 1),
141+
exception: 'TypeError: "excluding" argument is not a XMLNode object' },
142+
]};
143+
144+
let modify_tsuite = {
145+
name: "modifying XML",
146+
skip: () => (!has_njs()),
147+
T: async (params) => {
148+
let doc = xml.parse(params.doc);
149+
let r = params.get(doc);
150+
151+
if (r !== params.expected) {
152+
throw Error(`unexpected output "${r}" != "${params.expected}"`);
153+
}
154+
155+
return 'SUCCESS';
156+
},
157+
158+
opts: {
159+
doc: `<note><to b=\"bar\" a= \"foo\" >Tove</to><from>Jani</from></note>`,
160+
},
161+
162+
tests: [
163+
{ get: (doc) => {
164+
doc.note.setText('WAKA');
165+
return doc.note.$text;
166+
},
167+
expected: 'WAKA' },
168+
{ get: (doc) => {
169+
doc.note.setText('WAKA');
170+
return xml.serializeToString(doc);
171+
},
172+
expected: `<note>WAKA</note>` },
173+
{ get: (doc) => {
174+
doc.note.$text = '<WA&KA>';
175+
return xml.serializeToString(doc);
176+
},
177+
expected: `<note>&lt;WA&amp;KA&gt;</note>` },
178+
{ get: (doc) => {
179+
doc.note.$text = '';
180+
return xml.serializeToString(doc);
181+
},
182+
expected: `<note></note>` },
183+
{ get: (doc) => {
184+
doc.note.setText('<WA&KA>');
185+
return doc.note.$text;
186+
},
187+
expected: '<WA&KA>' },
188+
{ get: (doc) => {
189+
doc.note.setText('<WA&KA>');
190+
return xml.serializeToString(doc);
191+
},
192+
expected: `<note>&lt;WA&amp;KA&gt;</note>` },
193+
{ get: (doc) => {
194+
doc.note.setText('"WAKA"');
195+
return xml.serializeToString(doc);
196+
},
197+
expected: `<note>"WAKA"</note>` },
198+
{ get: (doc) => {
199+
doc.note.setText('');
200+
return doc.note.$text;
201+
},
202+
expected: '' },
203+
{ get: (doc) => {
204+
doc.note.setText('');
205+
return xml.serializeToString(doc);
206+
},
207+
expected: `<note></note>` },
208+
{ get: (doc) => {
209+
doc.note.setText(null);
210+
return doc.note.$text;
211+
},
212+
expected: '' },
213+
{ get: (doc) => {
214+
doc.note.setText(undefined);
215+
return doc.note.$text;
216+
},
217+
expected: '' },
218+
{ get: (doc) => {
219+
doc.note.removeText();
220+
return doc.note.$text;
221+
},
222+
expected: '' },
223+
{ get: (doc) => {
224+
delete doc.note.$text;
225+
return xml.serializeToString(doc);
226+
},
227+
expected: `<note></note>` },
228+
{ get: (doc) => {
229+
doc.note.removeText();
230+
return xml.serializeToString(doc);
231+
},
232+
expected: `<note></note>` },
233+
{ get: (doc) => {
234+
let to = doc.note.to;
235+
doc.$root.$text = '';
236+
return to.$name;
237+
},
238+
expected: 'to' },
239+
{ get: (doc) => {
240+
let to = doc.note.to;
241+
doc.$root.$text = '';
242+
return [to.$name, to.$text, to.$attr$b, to.$parent.$name].toString();
243+
},
244+
expected: 'to,Tove,bar,note' },
245+
{ get: (doc) => {
246+
doc.note.to.setAttribute('aaa', 'foo');
247+
doc.note.to.setAttribute('bbb', '<bar\"');
248+
return xml.serializeToString(doc.note.to);
249+
},
250+
expected: `<to a="foo" aaa="foo" b="bar" bbb="&lt;bar&quot;">Tove</to>` },
251+
{ get: (doc) => {
252+
doc.note.to.$attr$aaa = 'foo';
253+
doc.note.to.$attr$bbb = '<bar\"';
254+
return xml.serializeToString(doc.note.to);
255+
},
256+
expected: `<to a="foo" aaa="foo" b="bar" bbb="&lt;bar&quot;">Tove</to>` },
257+
{ get: (doc) => {
258+
doc.note.to.$attr$aaa = 'foo';
259+
return doc.note.to.$attr$aaa;
260+
},
261+
expected: `foo` },
262+
{ get: (doc) => {
263+
doc.note.to.setAttribute('aaa', 'foo');
264+
doc.note.to.setAttribute('aaa', 'foo2');
265+
return xml.serializeToString(doc.note.to);
266+
},
267+
expected: `<to a="foo" aaa="foo2" b="bar">Tove</to>` },
268+
{ get: (doc) => {
269+
doc.note.to.removeAttribute('a');
270+
return xml.serializeToString(doc.note.to);
271+
},
272+
expected: `<to b="bar">Tove</to>` },
273+
{ get: (doc) => {
274+
doc.note.to.removeAllAttributes();
275+
return xml.serializeToString(doc.note.to);
276+
},
277+
expected: `<to>Tove</to>` },
278+
{ get: (doc) => {
279+
delete doc.note.to.$attr$a;
280+
return xml.serializeToString(doc.note.to);
281+
},
282+
expected: `<to b="bar">Tove</to>` },
283+
{ get: (doc) => {
284+
doc.note.to.setAttribute('a', null);
285+
return xml.serializeToString(doc.note.to);
286+
},
287+
expected: `<to b="bar">Tove</to>` },
288+
{ get: (doc) => {
289+
doc.note.to.setAttribute('<', 'foo');
290+
return xml.serializeToString(doc.note.to);
291+
},
292+
exception: 'TypeError: attribute name "<" is not valid' },
293+
{ get: (doc) => {
294+
let doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);
295+
doc.note.addChild(doc2);
296+
return xml.serializeToString(doc);
297+
},
298+
expected: `<note xmlns:n0="http://a"><to a="foo" b="bar">Tove</to><from>Jani</from><n0:pdu></n0:pdu></note>` },
299+
{ get: (doc) => {
300+
let doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);
301+
doc.note.addChild(doc2);
302+
doc.note.addChild(doc2);
303+
return xml.serializeToString(doc);
304+
},
305+
expected: `<note xmlns:n0="http://a"><to a="foo" b="bar">Tove</to><from>Jani</from><n0:pdu></n0:pdu><n0:pdu></n0:pdu></note>` },
306+
{ get: (doc) => {
307+
doc.note.removeChildren('to');
308+
return xml.serializeToString(doc);
309+
},
310+
expected: `<note><from>Jani</from></note>` },
311+
{ get: (doc) => {
312+
delete doc.note.$tag$to;
313+
return xml.serializeToString(doc);
314+
},
315+
expected: `<note><from>Jani</from></note>` },
316+
{ get: (doc) => {
317+
delete doc.note.to;
318+
return xml.serializeToString(doc);
319+
},
320+
expected: `<note><from>Jani</from></note>` },
321+
{ get: (doc) => {
322+
doc.note.removeChildren('xxx');
323+
return xml.serializeToString(doc);
324+
},
325+
expected: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>` },
326+
{ get: (doc) => {
327+
delete doc.note.$tag$xxx;
328+
return xml.serializeToString(doc);
329+
},
330+
expected: `<note><to a="foo" b="bar">Tove</to><from>Jani</from></note>` },
331+
{ doc: `<root><a>A</a><b>B</b><a>C</a></root>`,
332+
get: (doc) => {
333+
doc.$root.removeChildren('a');
334+
return xml.serializeToString(doc);
335+
},
336+
expected: `<root><b>B</b></root>` },
337+
{ doc: `<root><a>A</a><b>B</b><a>C</a></root>`,
338+
get: (doc) => {
339+
doc.$root.removeChildren();
340+
return xml.serializeToString(doc);
341+
},
342+
expected: `<root></root>` },
343+
{ doc: `<root><a>A</a><b>B</b><a>C</a></root>`,
344+
get: (doc) => {
345+
doc.$root.$tags = [];
346+
return xml.serializeToString(doc);
347+
},
348+
expected: `<root></root>` },
349+
{ doc: `<root><a>A</a><b>B</b><a>C</a></root>`,
350+
get: (doc) => {
351+
doc.$root.$tags$ = [];
352+
return xml.serializeToString(doc);
353+
},
354+
expected: `<root></root>` },
355+
{ doc: `<root><a>A</a><b>B</b><a>C</a></root>`,
356+
get: (doc) => {
357+
delete doc.$root.$tag$a;
358+
return xml.serializeToString(doc);
359+
},
360+
expected: `<root><b>B</b></root>` },
361+
{ get: (doc) => {
362+
doc.note.$tags = [doc.note.to];
363+
return xml.serializeToString(doc);
364+
},
365+
expected: `<note><to a="foo" b="bar">Tove</to></note>` },
366+
{ get: (doc) => {
367+
let doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);
368+
doc.note.$tags = [doc.note.to, doc2];
369+
return xml.serializeToString(doc);
370+
},
371+
expected: `<note xmlns:n0="http://a"><to a="foo" b="bar">Tove</to><n0:pdu></n0:pdu></note>` },
372+
{ get: (doc) => {
373+
let doc2 = xml.parse(`<n0:pdu xmlns:n0=\"http://a\"></n0:pdu>`);
374+
doc.note.$tags = [doc2, doc.note.to];
375+
return xml.serializeToString(doc);
376+
},
377+
expected: `<note xmlns:n0="http://a"><n0:pdu></n0:pdu><to a="foo" b="bar">Tove</to></note>` },
378+
]};
379+
380+
run([
381+
parse_tsuite,
382+
c14n_tsuite,
383+
modify_tsuite,
384+
])
385+
.then($DONE, $DONE);

0 commit comments

Comments
 (0)
Please sign in to comment.