const {XMLParser, XMLBuilder} = require("../src/fxp");
describe("XMLParser Entities", function() {
it("should parse attributes with valid names, default entities", function() {
const xmlData = `
1
2
2
test&\r\nтест<\r\ntest
<<b>2]]]]>\r\n&]]>a
\
страхования\
» от суммы \
его активов\
`;
const expected = {
"a:root": {
"@_xmlns:a": "urn:none",
"@_xmlns:a-b": "urn:none",
"a:a": {
"#text": 1,
"@_attr": "2foo&bar'"
},
"a:b": 2,
"a-b:b-a": 2,
"a:c": "test&\nтест<\ntest",
"a:el": "<2]]]]>&a",
"c:string": {
"#text": "страхования » от суммы его активов",
"@_lang": "ru"
}
}
};
const options = {
allowBooleanAttributes: true,
ignoreAttributes: false,
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData, true);
//console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should parse XML with DOCTYPE without internal DTD", function() {
const xmlData = "";
const expected = {
"?xml": {
"@_version": "1.0",
"@_standalone": "no"
},
"svg" : {
"metadata": "test"
}
};
const options = {
allowBooleanAttributes: true,
ignoreAttributes: false,
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData, true);
expect(result).toEqual(expected);
});
it("should parse XML with DOCTYPE without internal DTD", function() {
const xmlData = `
`;
const expected = {
"?xml": {
"@_version": "1.0",
"@_standalone": "no"
},
"svg" : {
"metadata": "[test]"
}
};
const options = {
allowBooleanAttributes: true,
ignoreAttributes: false,
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData, true);
// console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should error for when any tag is left to close", function(){
const xmlData = `{
const parser = new XMLParser();
parser.parse(xmlData);
}).toThrowError("Unclosed DOCTYPE")
})
it("should parse XML with DOCTYPE", function() {
const xmlData = "" +
"" +
"" +
"" +
"" +
"]>" +
"Hello World.";
const expected = {
"?xml": "",
foo: "Hello World."
};
const options = {
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
//console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should parse attributes having '>' in value", function() {
const xmlData = `
]>
Tove
Jani
Reminder
Don't forget me this weekend!
`;
const expected = {
"?xml": {
"version": "1.0",
"encoding": "UTF-8"
},
"note": {
"to": "Tove",
"from": "Jani",
"heading": "Reminder",
"body": {
"#text": "Don't forget me this weekend!",
"attr": "Writer: Donald Duck."
},
"footer": "Writer: Donald Duck. Copyright: W3Schools."
}
};
const options = {
attributeNamePrefix: "",
ignoreAttributes: false,
processEntities: true
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should parse dynamic entity", function() {
const xmlData = `
]>
Reminder
Don't forget me this weekend!
`;
const expected = {
"?xml": {
"version": "1.0",
"encoding": "UTF-8"
},
"note": {
"heading": "Reminder",
"body": {
"#text": "Don't forget me this weekend!",
"attr": "Writer: Donald Duck."
},
"footer": "Writer: Donald Duck.Writer: Donald Duck.Copyright: W3Schools."
}
};
const options = {
attributeNamePrefix: "",
ignoreAttributes: false,
processEntities: true
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should allow !ATTLIST & !NOTATION", function() {
const xmlData = `
]>
Some VRML instructions`;
const expected = {
"?xml": {
"version": "1.0"
},
"code": {
"lang": 'vrml',
"#text": 'Some VRML instructions'
}
};
const options = {
attributeNamePrefix: "",
ignoreAttributes: false,
processEntities: true
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
it("should build by decoding defaul entities", function() {
const jsObj = {
"note": {
"@heading": "Reminder > \"Alert",
"body": {
"#text": " 3 < 4",
"attr": "Writer: Donald Duck."
},
}
};
const expected = `
3 < 4
Writer: Donald Duck.
`;
const options = {
attributeNamePrefix: "@",
ignoreAttributes: false,
// processEntities: true
};
const builder = new XMLBuilder(options);
const result = builder.build(jsObj);
expect(result.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});
it("should build by decoding defaul entities in prserve mode", function() {
const jsObj = [
{
"note": [
{
"body": [
{
"#text": "3 < 4"
},
{
"attr": [
{
"#text": "Writer: Donald Duck."
}
]
}
]
}
],
":@": {
"@heading": "Reminder > \"Alert"
}
}
];
const expected = `
3 < 4
Writer: Donald Duck.
`;
const options = {
attributeNamePrefix: "@",
ignoreAttributes: false,
preserveOrder: true,
// processEntities: false
};
const parser = new XMLParser(options);
let result = parser.parse(expected);
// console.log(JSON.stringify(result,null,4));
const builder = new XMLBuilder(options);
result = builder.build(jsObj);
// console.log(result);
expect(result.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});
it("should parse HTML entities when htmlEntities:true", function() {
const xmlData = `
]>
Reminder
Don't forget me this weekend!®
`;
const expected = {
"?xml": {
"version": "1.0",
"encoding": "UTF-8"
},
"note": {
"heading": "Reminder",
"body": {
"#text": "Don't forget me this weekend!®",
"attr": "Writer: Donald Duck."
},
"footer": "Writer: Donald Duck.Writer: Donald Duck.Copyright: W3Schools.₹"
}
};
const options = {
attributeNamePrefix: "",
ignoreAttributes: false,
processEntities: true,
htmlEntities: true
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
});
describe("XMLParser External Entites", function() {
it("should throw error when an entity value has '&'", function() {
const parser = new XMLParser();
expect( () => {
parser.addEntity("#xD", "&\r");
}).toThrowError("Entity value can't have '&'");
});
it("should throw error when an entity identifier has '&'", function() {
const parser = new XMLParser();
expect( () => {
parser.addEntity("
", "\r");
}).toThrowError("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'");
});
it("should throw error when an entity identifier has ';'", function() {
const parser = new XMLParser();
expect( () => {
parser.addEntity("#xD;", "\r");
}).toThrowError("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'");
});
it("should set and parse for valid entity set externally", function() {
const xmlData = `&unknown;
last `;
const parser = new XMLParser();
parser.addEntity("#xD", "\r\n");
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result.note).toEqual(`&unknown;\r\nlast`);
});
it("External Entity can change the behaviour of default entites", function() {
const xmlData = `>last `;
const parser = new XMLParser();
parser.addEntity("gt", "<>");
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result.note).toEqual(`<>last`);
});
it("Same external Entity can be set by multiple times", function() {
const xmlData = `>last `;
const parser = new XMLParser();
parser.addEntity("gt", "<>");
parser.addEntity("gt", "><");
let result = parser.parse(xmlData);
// console.log(JSON.stringify(result,null,4));
expect(result.note).toEqual(`> \"Alert"
}
}
];
const expected = `
(3 & 4) < 5
Writer: Donald Duck.
`;
const options = {
attributeNamePrefix: "@",
ignoreAttributes: false,
preserveOrder: true,
// processEntities: false
};
const parser = new XMLParser(options);
let result = parser.parse(expected);
// console.log(JSON.stringify(result,null,4));
const builder = new XMLBuilder(options);
result = builder.build(jsObj);
// console.log(result);
expect(result.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});
it("should build by decoding '&'", function() {
const jsObj = {
"note": {
"body": {
"attr": "Writer: Donald Duck.",
"#text": "(3 & 4) < 5"
},
"@heading": "Reminder > \"Alert"
}
};
const expected = `
Writer: Donald Duck.
(3 & 4) < 5
`;
const options = {
attributeNamePrefix: "@",
ignoreAttributes: false,
};
const parser = new XMLParser(options);
let result = parser.parse(expected);
// console.log(JSON.stringify(result,null,4));
// expect(expected).toEqual(jsObj);
const builder = new XMLBuilder(options);
const output = builder.build(jsObj);
// console.log(output);
expect(output.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});
it("should replace '<' with '<'", function() {
const xmlData = `
<
`;
const expected = {
"SimpleScalarPropertiesInputOutput": {
"stringValue": "<"
}
};
const options = {
attributeNamePrefix: "",
ignoreAttributes: false,
processEntities: true,
// preserveOrder: true
};
const parser = new XMLParser(options);
let result = parser.parse(xmlData);
//console.log(JSON.stringify(result,null,4));
expect(result).toEqual(expected);
});
});