Skip to content

Commit

Permalink
rule no-invalid-xpath: add other tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Manu1400 committed Apr 10, 2019
1 parent 156b67b commit 24969bf
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 5 deletions.
64 changes: 60 additions & 4 deletions lib/rules/no-invalid-xpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
// or ts-xpath: https://www.npmjs.com/package/ts-xpath

const jsXpath = require("js-xpath")
const XPathAnalyzer = require("xpath-analyzer").default;
const isEqual = require('lodash.isequal');
const bcp47Validate = require("bcp47-validate").validate

// https://developer.mozilla.org/fr/docs/Web/API/XPathResult#Constants
const XPathResult = {
Expand All @@ -20,31 +23,84 @@ const XPathResult = {
}

const create = context => {
const parseXpath = xpathExpression => {
new XPathAnalyzer(xpathExpression);
// other librairy
jsXpath.parse(xpathExpression)
}

const isValidXPath = xpathExpression => {
var bool
try {
parseXpath(xpathExpression)
bool = true
} catch {
bool = false
}
return bool
}

return {
'CallExpression[callee.object.name=\'document\'][callee.property.name=\'evaluate\'][arguments.length > 0]': node => {
const xpathExpression = node.arguments[0].value

if (xpathExpression.length > 100) {
context.report({node, message: "large XPath expression detected"})
}

try {
// select([], xpathExpression)

jsXpath.parse(xpathExpression)
} catch (error) {
// //TODO: add xpath value
// console.log(error)
//console.log(error)
context.report({node, message: "invalid XPath: " + "Unrecognized text"})
return;
}

const parsed = jsXpath.parse(xpathExpression)
var regenerated = parsed.toXPath();
const regenerated = parsed.toXPath();
if (xpathExpression !== regenerated) {
context.report({node, message: "XPath expression lisibility: " + regenerated})
}

// an other rule:
try {
var analyzer = new XPathAnalyzer(xpathExpression);
var obj = analyzer.parse();
} catch (error) {
context.report({node, message: "invalid XPath"})
return;
}
var analyzer = new XPathAnalyzer(xpathExpression);
var obj = analyzer.parse();
if (obj.steps && obj.steps.length >= 2 && obj.steps[1].predicates) {
const predicates = obj.steps[1].predicates
if (predicates.length >= 2 && isEqual(predicates[1], predicates[0])) {
context.report({node, message: "XPath expression: duplicate predicate"})
}
}

// other rule:
for (step in obj.steps) {
var step = obj.steps[step]
try {
const name = step.predicates[0].lhs.steps[0].test.name
if (name == "lang") {
const lang = step.predicates[0].rhs.string
if (bcp47Validate(lang) == false) {
context.report({node, message: "invalid @lang: " + lang})
}
}
} catch (error) {
if (error.name !== 'TypeError') {
console.error("Message: ", error.message);
}
}
}


// TODO: create an other rule no-magic-number-about-xpath (by moving code)

if (node.arguments.length >= 4) {
if (node.arguments[3].type == 'Literal') {
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
"dependencies": {
"@types/eslint": "^4.16.6",
"@types/listr": "^0.13.0",
"bcp47-validate": "^1.0.0",
"execa": "^1.0.0",
"import-modules": "^1.1.0",
"is-url": "^1.2.4",
"js-xpath": "0.0.4",
"listr": "^0.14.3",
"lodash.isequal": "^4.5.0",
"reflect-type-3": "^1.0.0",
"requireindex": "~1.1.0",
"tempy": "^0.2.1",
"xpath-analyzer": "^3.0.0",
"xpath.js": "^1.1.0"
},
"devDependencies": {
Expand Down
61 changes: 60 additions & 1 deletion tests/lib/rules/no-invalid-xpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,66 @@ ruleTester.run("no-invalid-xpath", rule, {
},
{
code: "document.evaluate(\"count(//div)\", document)",
}
},
{
code: "document.evaluate(\"//some/xpath/statement[@coolness = very]\", document)",
},
{
code: "document.evaluate(\"//title[@lang = \'en\']\", document)",
},
{
code: "document.evaluate(\"//title[@lang = \'en-GB\']\", document)",
},
{
code: "document.evaluate(\"//*[@lang = \'en-GB\']\", document)",
},
// XPath utility function, function isn't part of JavaScript itself, it is just a utility that's available in the console
{
code: "$x('//html', document)",
},
{
code: "window.document.evaluate(\"//html\", document)",
},
],

invalid: [
{
code: "document.evaluate(\"//title[@lang = 'invalid-lang:']\", document)",
errors: [{
message: "invalid @lang: invalid-lang:",
}]
},
{
code: "document.evaluate(\"//title/book[@lang = 'invalid-lang:']\", document)",
errors: [{
message: "invalid @lang: invalid-lang:",
}]
},
{
code: "document.evaluate(\"//\", document)",
errors: [{
message: "invalid XPath: Unrecognized text",
}]
},
{
code: "document.evaluate(\"\", document)",
errors: [{
message: "invalid XPath: Unrecognized text",
}]
},
{
code: "document.evaluate(\"//book[]\", document)",
errors: [{
message: "invalid XPath: Unrecognized text",
}]
},
// no error in Chrome but not a valid XPath expression:
{
code: "document.evaluate(\"/html[callee.object.name]\", document)",
errors: [{
message: "invalid XPath",
}]
},
{
code: "document.evaluate(\"/html/body//h2:llL ms%dl\", document)",
errors: [{
Expand All @@ -59,6 +115,9 @@ ruleTester.run("no-invalid-xpath", rule, {
code: "document.evaluate(\"//article[count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1][count(author) = 1]/title\", document)",
errors: [{
message: 'large XPath expression detected',
},
{
message: 'XPath expression: duplicate predicate',
}]
},
]
Expand Down

0 comments on commit 24969bf

Please sign in to comment.