From eeaab59fbfd0de78426e145628ad6d08e5d464ee Mon Sep 17 00:00:00 2001 From: "sa.lichak" Date: Tue, 11 Nov 2014 13:47:36 +0300 Subject: [PATCH 01/11] initial requirejs support --- lib/collector.js | 246 +++++++++++++++++++++++---------------------- lib/compare.js | 249 +++++++++++++++++++++++++--------------------- lib/node_types.js | 11 +- lib/revxpath.js | 84 +++++++++------- 4 files changed, 316 insertions(+), 274 deletions(-) diff --git a/lib/collector.js b/lib/collector.js index d2c89fc..859eb59 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -2,142 +2,150 @@ "use strict"; - var type = require('./node_types'); - var revxpath = require('./revxpath.js'); - - var typeMap = {}, - comparatorTypeMap = {}; - - typeMap[type.ATTRIBUTE_NODE] = "attribute"; - typeMap[type.ELEMENT_NODE] = "element"; - typeMap[type.TEXT_NODE] = "text node"; - typeMap[type.COMMENT_NODE] = "comment node"; - typeMap[type.CDATA_SECTION_NODE] = "CDATA node"; - typeMap[type.DOCUMENT_NODE] = "document"; - - Object.keys(type).forEach(function(k){ - comparatorTypeMap[type[k]] = k; - }); - - function Collector(options) { - this._diff = []; - this._options = options || {}; - } + function factory(type, revxpath) { + var typeMap = {}, + comparatorTypeMap = {}; + + typeMap[type.ATTRIBUTE_NODE] = "attribute"; + typeMap[type.ELEMENT_NODE] = "element"; + typeMap[type.TEXT_NODE] = "text node"; + typeMap[type.COMMENT_NODE] = "comment node"; + typeMap[type.CDATA_SECTION_NODE] = "CDATA node"; + typeMap[type.DOCUMENT_NODE] = "document"; + + Object.keys(type).forEach(function(k){ + comparatorTypeMap[type[k]] = k; + }); - Collector.prototype._describeNode = function(node) { - if(node.nodeType == type.TEXT_NODE || - node.nodeType == type.CDATA_SECTION_NODE || - node.nodeType == type.COMMENT_NODE) { - return "'" + (this._options.stripSpaces ? node.nodeValue.trim() : node.nodeValue) + "'"; + function Collector(options) { + this._diff = []; + this._options = options || {}; } - else - return "'" + node.nodeName + "'"; - }; - - Collector.prototype.getDifferences = function() { - return this._diff; - }; - - Collector.prototype.getResult = function() { - return this._diff.length == 0 - }; - - Collector.prototype.collectFailure = function(expected, actual) { - - var msg, canContinue = true, vExpected, vActual, ref = expected || actual, cmprtr, r; - - if(this._options.comparators && (cmprtr = this._options.comparators[comparatorTypeMap[ref.nodeType]])) { - if(!(cmprtr instanceof Array)) - cmprtr = [ cmprtr ]; - for(var i = 0, l = cmprtr.length; i < l; i++) { - r = cmprtr[i](expected, actual); - if(r) { - // true -> ignore differences. Stop immediately, continue; - if(r === true) { - return true; - } - // string - treat as error message, continue; - else if(typeof r == 'string') { - msg = r; - canContinue = true; - } - // object - .message = error message, .stop - stop flag - else if(typeof r == 'object') { - msg = r.message; - canContinue = !(!!r.stop); + + Collector.prototype._describeNode = function(node) { + if(node.nodeType == type.TEXT_NODE || + node.nodeType == type.CDATA_SECTION_NODE || + node.nodeType == type.COMMENT_NODE) { + return "'" + (this._options.stripSpaces ? node.nodeValue.trim() : node.nodeValue) + "'"; + } + else + return "'" + node.nodeName + "'"; + }; + + Collector.prototype.getDifferences = function() { + return this._diff; + }; + + Collector.prototype.getResult = function() { + return this._diff.length == 0 + }; + + Collector.prototype.collectFailure = function(expected, actual) { + + var msg, canContinue = true, vExpected, vActual, ref = expected || actual, cmprtr, r; + + if(this._options.comparators && (cmprtr = this._options.comparators[comparatorTypeMap[ref.nodeType]])) { + if(!(cmprtr instanceof Array)) + cmprtr = [ cmprtr ]; + for(var i = 0, l = cmprtr.length; i < l; i++) { + r = cmprtr[i](expected, actual); + if(r) { + // true -> ignore differences. Stop immediately, continue; + if(r === true) { + return true; + } + // string - treat as error message, continue; + else if(typeof r == 'string') { + msg = r; + canContinue = true; + } + // object - .message = error message, .stop - stop flag + else if(typeof r == 'object') { + msg = r.message; + canContinue = !(!!r.stop); + } + break; } - break; - } + } } - } - if(!msg) { + if(!msg) { - if(expected && !actual) { - msg = typeMap[expected.nodeType].charAt(0).toUpperCase() + typeMap[expected.nodeType].substr(1) + - " " + this._describeNode(expected) + " is missed"; - canContinue = true; - } - else if(!expected && actual) { - msg = "Extra " + typeMap[actual.nodeType] + " " + this._describeNode(actual); - canContinue = true; - } - else { - if(expected.nodeType == actual.nodeType) { - if(expected.nodeName == actual.nodeName) { - vExpected = expected.nodeValue; - vActual = actual.nodeValue; - if(this._options.stripSpaces && expected.nodeType != type.CDATA_SECTION_NODE) { - vExpected = vExpected.trim(); - vActual = vActual.trim(); + if(expected && !actual) { + msg = typeMap[expected.nodeType].charAt(0).toUpperCase() + typeMap[expected.nodeType].substr(1) + + " " + this._describeNode(expected) + " is missed"; + canContinue = true; + } + else if(!expected && actual) { + msg = "Extra " + typeMap[actual.nodeType] + " " + this._describeNode(actual); + canContinue = true; + } + else { + if(expected.nodeType == actual.nodeType) { + if(expected.nodeName == actual.nodeName) { + vExpected = expected.nodeValue; + vActual = actual.nodeValue; + if(this._options.stripSpaces && expected.nodeType != type.CDATA_SECTION_NODE) { + vExpected = vExpected.trim(); + vActual = vActual.trim(); + } + if(vExpected == vActual) + throw new Error("Nodes are considered equal but shouldn't"); + else { + switch(expected.nodeType) { + case type.ATTRIBUTE_NODE: + msg = "Attribute '" + expected.nodeName + "': expected value '" + vExpected + "' instead of '" + vActual + "'"; + break; + case type.COMMENT_NODE: + msg = "Expected comment value '" + vExpected + "' instead of '" + vActual + "'"; + break; + case type.CDATA_SECTION_NODE: + msg = "Expected CDATA value '" + vExpected + "' instead of '" + vActual + "'"; + break; + case type.TEXT_NODE: + msg = "Expected text '" + vExpected + "' instead of '" + vActual + "'"; + break; + default: + throw new Error("nodeValue is not equal, but nodeType is unexpected"); + } + canContinue = true; + } } - if(vExpected == vActual) - throw new Error("Nodes are considered equal but shouldn't"); else { - switch(expected.nodeType) { - case type.ATTRIBUTE_NODE: - msg = "Attribute '" + expected.nodeName + "': expected value '" + vExpected + "' instead of '" + vActual + "'"; - break; - case type.COMMENT_NODE: - msg = "Expected comment value '" + vExpected + "' instead of '" + vActual + "'"; - break; - case type.CDATA_SECTION_NODE: - msg = "Expected CDATA value '" + vExpected + "' instead of '" + vActual + "'"; - break; - case type.TEXT_NODE: - msg = "Expected text '" + vExpected + "' instead of '" + vActual + "'"; - break; - default: - throw new Error("nodeValue is not equal, but nodeType is unexpected"); - } - canContinue = true; + msg = "Expected " + typeMap[expected.nodeType] + + " '" + expected.nodeName + "' instead of '" + actual.nodeName + "'"; + canContinue = false; } } else { - msg = "Expected " + typeMap[expected.nodeType] + - " '" + expected.nodeName + "' instead of '" + actual.nodeName + "'"; + msg = "Expected node of type " + expected.nodeType + + " (" + typeMap[expected.nodeType] + ") instead of " + + actual.nodeType + " (" + typeMap[actual.nodeType] + ")"; canContinue = false; } } - else { - msg = "Expected node of type " + expected.nodeType + - " (" + typeMap[expected.nodeType] + ") instead of " + - actual.nodeType + " (" + typeMap[actual.nodeType] + ")"; - canContinue = false; - } } - } - - this._diff.push({ - node: revxpath(ref.ownerElement || ref.parentNode), - message: msg - }); - return canContinue; - }; + this._diff.push({ + node: revxpath(ref.ownerElement || ref.parentNode), + message: msg + }); - module.exports = Collector; + return canContinue; + }; + return Collector; + } + if((this || (0,eval)('this')).document) { + define(['./node_types', './revxpath'], function(node_types, revxpath) { + return factory(node_types, revxpath) + }); + } + else { + var type = require('./node_types'); + var revxpath = require('./revxpath'); + module.exports = factory(type, revxpath); + } -})(); \ No newline at end of file +})(); diff --git a/lib/compare.js b/lib/compare.js index 73f6361..d35af2f 100644 --- a/lib/compare.js +++ b/lib/compare.js @@ -1,143 +1,160 @@ (function(){ "use strict"; + function factory(type, Collector) { - var type = require('./node_types'); - var Collector = require('./collector'); - - function Comparator(options, collector) { - this._options = options || {}; - if(!collector) - throw new Error("Collector instance must be specified"); - this._collector = collector; - } - - Comparator.prototype._filterNodes = function(list) { - var ret = [], - i, l, item; - for (i = 0, l = list.length; i < l; i++) { - item = list.item(i); - if (item.nodeType == type.COMMENT_NODE && !this._options.compareComments) - continue; - if (item.nodeType == type.TEXT_NODE && ("" + item.nodeValue).trim() == "") - continue; - ret.push(item); + function Comparator(options, collector) { + this._options = options || {}; + if(!collector) + throw new Error("Collector instance must be specified"); + this._collector = collector; } - return ret; - }; - - Comparator.prototype._compareNodeList = function(left, right) { - var lLeft = this._filterNodes(left), - lRight = this._filterNodes(right), - i, l; - - for (i = 0, l = Math.max(lLeft.length, lRight.length); i < l; i++) { - if(lLeft[i] && lRight[i]) { - if (!this.compareNode(lLeft[i], lRight[i])) - return false; - } - else { - return this._collector.collectFailure(lLeft[i], lRight[i]); + + Comparator.prototype._filterNodes = function(list) { + var ret = [], + i, l, item; + for (i = 0, l = list.length; i < l; i++) { + item = list.item(i); + if (item.nodeType == type.COMMENT_NODE && !this._options.compareComments) + continue; + if (item.nodeType == type.TEXT_NODE && ("" + item.nodeValue).trim() == "") + continue; + ret.push(item); } - } - return true; - }; + return ret; + }; - Comparator.prototype._compareAttributes = function(expected, actual) { - var aExpected = {}, aActual = {}, - i, l; + Comparator.prototype._compareNodeList = function(left, right) { + var lLeft = this._filterNodes(left), + lRight = this._filterNodes(right), + i, l; - if (!expected && !actual) + for (i = 0, l = Math.max(lLeft.length, lRight.length); i < l; i++) { + if(lLeft[i] && lRight[i]) { + if (!this.compareNode(lLeft[i], lRight[i])) + return false; + } + else { + return this._collector.collectFailure(lLeft[i], lRight[i]); + } + } return true; + }; + Comparator.prototype._compareAttributes = function(expected, actual) { + var aExpected = {}, aActual = {}, + i, l; + if (!expected && !actual) + return true; - for(i = 0, l = expected.length; i < l; i++) { - aExpected[expected[i].nodeName] = expected[i]; - } - for(i = 0, l = actual.length; i < l; i++) { - aActual[actual[i].nodeName] = actual[i]; - } - for(i in aExpected) { - // both nodes has an attribute - if(aExpected.hasOwnProperty(i) && aActual.hasOwnProperty(i)) { - // but values is differ - var vExpected = aExpected[i].nodeValue; - var vActual = aActual[i].nodeValue; - if(this._options.stripSpaces && aExpected[i].nodeType != type.CDATA_SECTION_NODE) { - vExpected = vExpected.trim(); - vActual = vActual.trim(); + for(i = 0, l = expected.length; i < l; i++) { + aExpected[expected[i].nodeName] = expected[i]; + } + + for(i = 0, l = actual.length; i < l; i++) { + aActual[actual[i].nodeName] = actual[i]; + } + + for(i in aExpected) { + // both nodes has an attribute + if(aExpected.hasOwnProperty(i) && aActual.hasOwnProperty(i)) { + // but values is differ + var vExpected = aExpected[i].nodeValue; + var vActual = aActual[i].nodeValue; + if(this._options.stripSpaces && aExpected[i].nodeType != type.CDATA_SECTION_NODE) { + vExpected = vExpected.trim(); + vActual = vActual.trim(); + } + if(vExpected !== vActual) { + if(!this._collector.collectFailure(aExpected[i], aActual[i])) + return false; + } + // remove to check for extra/missed attributes; + delete aActual[i]; + delete aExpected[i]; } - if(vExpected !== vActual) { - if(!this._collector.collectFailure(aExpected[i], aActual[i])) + } + + // report all missed attributes + for(i in aExpected) { + if(aExpected.hasOwnProperty(i)) + if(!this._collector.collectFailure(aExpected[i], null)) return false; - } - // remove to check for extra/missed attributes; - delete aActual[i]; - delete aExpected[i]; } - } - // report all missed attributes - for(i in aExpected) { - if(aExpected.hasOwnProperty(i)) - if(!this._collector.collectFailure(aExpected[i], null)) - return false; - } + // report all extra attributes + for(i in aActual) { + if(aActual.hasOwnProperty(i)) + if(!this._collector.collectFailure(null, aActual[i])) + return false; + } - // report all extra attributes - for(i in aActual) { - if(aActual.hasOwnProperty(i)) - if(!this._collector.collectFailure(null, aActual[i])) - return false; - } + return true; + }; + + Comparator.prototype.compareNode = function(left, right) { + var vLeft, vRight, r; + + if (right.nodeName.toLowerCase() == left.nodeName.toLowerCase() && left.nodeType == right.nodeType) { + switch (left.nodeType) { + case type.DOCUMENT_NODE: + return this.compareNode(left.documentElement, right.documentElement); + case type.ELEMENT_NODE: + return this._compareAttributes(left.attributes, right.attributes) && + this._compareNodeList(left.childNodes, right.childNodes); + case type.TEXT_NODE: + // fallthrough + case type.CDATA_SECTION_NODE: + // fallthrough + case type.COMMENT_NODE: + if (left.nodeType == type.COMMENT_NODE && !this._options.compareComments) + return true; + vLeft = "" + left.nodeValue; + vRight = "" + right.nodeValue; + if (this._options.stripSpaces && left.nodeType !== type.CDATA_SECTION_NODE) { + vLeft = vLeft.trim(); + vRight = vRight.trim(); + } + r = vLeft === vRight; + return !r ? this._collector.collectFailure(left, right) : r; + default: + throw Error("Node type " + left.nodeType + " comparison is not implemented"); + } + } else + return this._collector.collectFailure(left, right); + }; - return true; - }; - - Comparator.prototype.compareNode = function(left, right) { - var vLeft, vRight, r; - - if (left.nodeName === right.nodeName && left.nodeType == right.nodeType) { - switch (left.nodeType) { - case type.DOCUMENT_NODE: - return this.compareNode(left.documentElement, right.documentElement); - case type.ELEMENT_NODE: - return this._compareAttributes(left.attributes, right.attributes) && - this._compareNodeList(left.childNodes, right.childNodes); - case type.TEXT_NODE: - // fallthrough - case type.CDATA_SECTION_NODE: - // fallthrough - case type.COMMENT_NODE: - if (left.nodeType == type.COMMENT_NODE && !this._options.compareComments) - return true; - vLeft = "" + left.nodeValue; - vRight = "" + right.nodeValue; - if (this._options.stripSpaces && left.nodeType !== type.CDATA_SECTION_NODE) { - vLeft = vLeft.trim(); - vRight = vRight.trim(); - } - r = vLeft === vRight; - return !r ? this._collector.collectFailure(left, right) : r; - default: - throw Error("Node type " + left.nodeType + " comparison is not implemented"); - } - } else - return this._collector.collectFailure(left, right); - }; + return Comparator; + } + if((this || (0,eval)('this')).document) { + define(['./node_types', './collector'], function(node_types, Collector) { + return function(a, b, options) { + var Comparator = factory(node_types, collector); - module.exports = function(a, b, options) { + var collector = new Collector(options); + var comparator = new Comparator(options, collector); + comparator.compareNode(a, b); + return collector; + } + }); + } + else { + var type = require('./node_types'); + var Collector = require('./collector'); - var collector = new Collector(options); - var comparator = new Comparator(options, collector); - comparator.compareNode(a, b); + module.exports = function(a, b, options) { + var Comparator = factory(type, Collector); - return collector; + var collector = new Collector(options); + var comparator = new Comparator(options, collector); + comparator.compareNode(a, b); - }; + return collector; + }; + } })(); \ No newline at end of file diff --git a/lib/node_types.js b/lib/node_types.js index 4f7cad2..9486b82 100644 --- a/lib/node_types.js +++ b/lib/node_types.js @@ -1,8 +1,7 @@ (function(){ "use strict"; - - module.exports = { + var node_types = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, @@ -17,4 +16,12 @@ NOTATION_NODE: 12 }; + if((this || (0,eval)('this')).document) { + define([], function() { + return node_types; + }); + } + else { + module.exports = node_types; + } })(); \ No newline at end of file diff --git a/lib/revxpath.js b/lib/revxpath.js index 3e4aeb9..348e014 100644 --- a/lib/revxpath.js +++ b/lib/revxpath.js @@ -1,52 +1,62 @@ (function(){ "use strict"; + function factory(type) { + function _describeNode(node) { + var parent = node.parentNode, + myName = node.nodeName, + sameSiblings = parent && parent.getElementsByTagName && parent.getElementsByTagName(myName), + i, l; - var type = require('./node_types'); - - function _describeNode(node) { - var parent = node.parentNode, - myName = node.nodeName, - sameSiblings = parent && parent.getElementsByTagName(myName), - i, l; - - if(node.nodeType == type.DOCUMENT_NODE) - return ""; - - if(sameSiblings && sameSiblings.length > 1) { - if(node.hasAttribute('id')) - return myName + "[@id='" + node.getAttribute('id') + "']"; - for(i = 0, l = sameSiblings.length; i < l; i++) { - if(sameSiblings.item(i) == node) - return myName + "[" + (i + 1) + "]"; - } - throw new Error("Node is not found, but should be!"); - } else - return myName; - } + if(node.nodeType == type.DOCUMENT_NODE) + return ""; - function _processNode(node, res) { + if(sameSiblings && sameSiblings.length > 1) { + if(node.hasAttribute('id')) + return myName + "[@id='" + node.getAttribute('id') + "']"; + for(i = 0, l = sameSiblings.length; i < l; i++) { + if(sameSiblings.item(i) == node) + return myName + "[" + (i + 1) + "]"; + } + throw new Error("Node is not found, but should be!"); + } else + return myName; + } - res.unshift(_describeNode(node)); - if(node.parentNode) - _processNode(node.parentNode, res); + function _processNode(node, res) { - } + res.unshift(_describeNode(node)); + if(node.parentNode) + _processNode(node.parentNode, res); + + } + + return function (node) { - module.exports = function revXPath(node) { + var res; - var res; + if(!node) + return ""; - if(!node) - return ""; + if(node.nodeType == type.DOCUMENT_NODE) + return "/"; - if(node.nodeType == type.DOCUMENT_NODE) - return "/"; + _processNode(node, res = []); + return res.join('/'); - _processNode(node, res = []); - return res.join('/'); + }; + } + + if((this || (0,eval)('this')).document) { + define(['./node_types'], function(node_types) { - }; + return factory(node_types); + }); + } + else { + var node_types = require('./node_types'); + module.exports = factory(node_types); + } -})(); \ No newline at end of file +})(); From 431c68c0be0a1a8bd51c84536712cb9797d4ada9 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Sun, 17 Nov 2013 13:46:20 +0400 Subject: [PATCH 02/11] Starting 0.2.1. Fixing deps, moving to tensor-xmldom, adding UMD support --- bin/domcompare | 2 +- package.json | 2 +- test/test-canonize.js | 2 +- test/test-collector.js | 2 +- test/test-compare.js | 2 +- test/test-reporter.js | 2 +- test/test-revxpath.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/domcompare b/bin/domcompare index 0b53e24..d54cbb7 100755 --- a/bin/domcompare +++ b/bin/domcompare @@ -9,7 +9,7 @@ var domcompare = require('../'), fs = require('fs'), ArgumentParser = require('argparse').ArgumentParser, version = require('../package.json').version, - xmldom = require('xmldom'), + xmldom = require('tensor-xmldom'), path = require('path'), domparser = new (xmldom.DOMParser)(), args, dom1, dom2, f1ext, f2ext, mimeMap, result, argparser; diff --git a/package.json b/package.json index fafc3aa..8b63af3 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "argparse": "1.0.2", - "xmldom": "0.1.16", + "tensor-xmldom": "0.1.18", "colors": "0.6.2" }, "devDependencies": { diff --git a/test/test-canonize.js b/test/test-canonize.js index aad8ae4..0bb0b28 100644 --- a/test/test-canonize.js +++ b/test/test-canonize.js @@ -1,5 +1,5 @@ var assert = require("assert"); -var xmldom = require("xmldom"); +var xmldom = require("tensor-xmldom"); var parser = new xmldom.DOMParser(); var domCompare = require('../'); var canonize = (new (domCompare.XMLSerializer)()).serializeToString; diff --git a/test/test-collector.js b/test/test-collector.js index e7585b1..329c968 100644 --- a/test/test-collector.js +++ b/test/test-collector.js @@ -1,5 +1,5 @@ var assert = require("assert"); -var xmldom = require("xmldom"); +var xmldom = require("tensor-xmldom"); var parser = new xmldom.DOMParser(); var domCompare = require("../"); var compare = domCompare.compare; diff --git a/test/test-compare.js b/test/test-compare.js index 73bb971..0448003 100644 --- a/test/test-compare.js +++ b/test/test-compare.js @@ -1,5 +1,5 @@ var assert = require("assert"); -var xmldom = require("xmldom"); +var xmldom = require("tensor-xmldom"); var parser = new xmldom.DOMParser(); var domCompare = require("../"); var compare = domCompare.compare; diff --git a/test/test-reporter.js b/test/test-reporter.js index ebbb270..38b0983 100644 --- a/test/test-reporter.js +++ b/test/test-reporter.js @@ -1,5 +1,5 @@ var assert = require("assert"); -var xmldom = require("xmldom"); +var xmldom = require("tensor-xmldom"); var parser = new xmldom.DOMParser(); var domCompare = require("../"); var compare = domCompare.compare; diff --git a/test/test-revxpath.js b/test/test-revxpath.js index b599bee..032c278 100644 --- a/test/test-revxpath.js +++ b/test/test-revxpath.js @@ -1,5 +1,5 @@ var assert = require("assert"); -var xmldom = require("xmldom"); +var xmldom = require("tensor-xmldom"); var parser = new xmldom.DOMParser(); var domCompare = require("../"); var revxpath = domCompare.revXPath; From 5bfb7394abdf19ec066ec79cfbee26b0273896f6 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Tue, 6 Jan 2015 18:00:24 +0300 Subject: [PATCH 03/11] Improved grouping reporter logging --- lib/reporters/groupingReporter.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/reporters/groupingReporter.js b/lib/reporters/groupingReporter.js index dd6b2bd..e86464e 100644 --- a/lib/reporters/groupingReporter.js +++ b/lib/reporters/groupingReporter.js @@ -6,14 +6,17 @@ report: function(res) { var _res = this.getDefferences(res); return Object.keys(_res).map(function(path){ - return [path, "\n\t", _res[path].join('\n\t')].join(''); - }.bind(this)).join('\n'); + var message = _res[path].map(function(message){ + return message.replace(/\n/g, '\n\t\t'); + }).join('\n\t'); + return [path, "\n\t", message].join(''); + }).join('\n'); }, getDefferences: function(res) { var _res = {}; res.getDifferences().forEach(function(f){ (_res[f.node] = (_res[f.node] || [])).push(f.message); - }.bind(this)); + }); return _res; } }; From 5c11413c391ea2c76fe0a52043db61a01334ea7b Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Sun, 11 Jan 2015 23:17:55 +0300 Subject: [PATCH 04/11] JSON comparator added --- index.js | 5 +++- lib/comparators/jsonComparator.js | 43 +++++++++++++++++++++++++++++++ package.json | 3 ++- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 lib/comparators/jsonComparator.js diff --git a/index.js b/index.js index 8dea9ed..d41fa7e 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,10 @@ compare: require(libPrefix + '/compare'), XMLSerializer: require(libPrefix + '/canonizer'), revXPath: require(libPrefix + '/revxpath'), - GroupingReporter: require(libPrefix + '/reporters/groupingReporter.js') + GroupingReporter: require(libPrefix + '/reporters/groupingReporter.js'), + comparators: { + jsonComparator: require(libPrefix + '/comparators/jsonComparator.js') + } }; })(); \ No newline at end of file diff --git a/lib/comparators/jsonComparator.js b/lib/comparators/jsonComparator.js new file mode 100644 index 0000000..2a891b3 --- /dev/null +++ b/lib/comparators/jsonComparator.js @@ -0,0 +1,43 @@ +var jsonDiff = require('rfc6902-json-diff'); + +module.exports = function nodeValueJSONComparator(nodeNamesToCompare) { + + function mustCompareNode(node) { + if (nodeNamesToCompare && nodeNamesToCompare.length) { + return nodeNamesToCompare.indexOf(node.nodeName) !== -1; + } else { + return true; + } + } + + return function nodeValueJSONComparator(expected, actual) { + + var + node = expected || actual, + eVal = expected && expected.nodeValue, + aVal = actual && actual.nodeValue, + result, diff; + + + if (mustCompareNode(node) && eVal && aVal) { + try { + eVal = eVal && JSON.parse(eVal); + aVal = aVal && JSON.parse(aVal); + + diff = jsonDiff(aVal, eVal); + + if (diff.length > 0) { + result = 'Attribute "config" differs. Expected:\n' + JSON.stringify(eVal, null, 2); + result += '\nActual:\n' + JSON.stringify(aVal, null, 2); + result += '\nDiff:\n' + JSON.stringify(diff, null, 2); + + return result; + } else { + return true; + } + } catch (e) { + // ignore + } + } + }; +}; \ No newline at end of file diff --git a/package.json b/package.json index 8b63af3..059cb1d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "dependencies": { "argparse": "1.0.2", "tensor-xmldom": "0.1.18", - "colors": "0.6.2" + "colors": "0.6.2", + "rfc6902-json-diff": "0.1.x" }, "devDependencies": { "istanbul": "0.3.8", From 3d7a9a8fca4e904b51bf4918833f776ae61478c9 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Sun, 15 Mar 2015 23:46:06 +0300 Subject: [PATCH 05/11] Baking 0.3.0 --- README.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e664b44..d9cbd53 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ NodeJS module to compare two DOM-trees * [Comparison options](#comparison-options) * [Comments comparison](#comments-comparison) * [Whitespace comparison](#whitespace-comparison) + * [Custom comparators] * [Cli utility](#cli-utility) * [DOM Canonic Form](#dom-canonic-form) @@ -128,6 +129,8 @@ You can try it on bundled samples: /document Expected CDATA value ' cdata node' instead of 'cdata node ' ``` + +### Custom comparators (since 0.3) DOM Canonic Form diff --git a/package.json b/package.json index 059cb1d..63bc054 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dom-compare", "description": "Library to compare two DOM trees", - "version": "0.2.1", + "version": "0.3.0", "author": "Oleg Elifantiev ", "contributors": [], "keywords": [ From 184902b8c8903acb4a200afba9c74bbf8ae990e9 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Mon, 16 Mar 2015 00:43:55 +0300 Subject: [PATCH 06/11] Docs for custom comparators --- README.md | 83 +++++++++++++++++++++++++++++-- lib/comparators/jsonComparator.js | 4 +- samples/json-compare/actual.xml | 3 ++ samples/json-compare/demo.js | 24 +++++++++ samples/json-compare/expected.xml | 3 ++ 5 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 samples/json-compare/actual.xml create mode 100644 samples/json-compare/demo.js create mode 100644 samples/json-compare/expected.xml diff --git a/README.md b/README.md index d9cbd53..ff4796c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Diff-object has a following form: } ``` -By using `GroupongReporter` one can get a result of a following type +By using `GroupingReporter` one can get a result of a following type ```javascript { @@ -110,6 +110,84 @@ to leading and trailing whitespaces. Set `stripSpaces` option to `true` to automatically strip spaces in text and comment nodes. This option doesn't change the way CDATA sections is compared, they are always compared with respect to whitespaces. +### Custom comparators (since 0.3) + +**This is experimental feature and may change in future releases** + +Sometimes one needs a special rules to compare some DOM elements. + +Imagine you have some nodes with `config` attr which contains JSON data (see `samples/json-compare`). +So, if you'd like to compare such documents, you will see something like this: + +``` +/document/div + Attribute 'config': expected value '{"attr1":10,"attr2":30,"attr3":-1}' instead of '{"attr1":10,"attr2":20}' +``` + +This makes not much sense... You can use custom comparators option! + +```javascript +var domcompare = require('dom-compare'); + +// create comparator for specified node name +// you can specify multiple node names here - pass multiple arguments +var configComparator = domcompare.comparators.jsonComparator('config') + +var res = domcompare.compare(treeA, treeB, { + comparators: { + // for every attrbute difference, run custom comparison routine + // you can pass multiple comparators here using array + ATTRIBUTE_NODE: configComparator + } +}); +``` + +Bundled JSON comparator can parse node's value like JSON, and if it's parsed, +compare using [rfc6902-json-diff](https://www.npmjs.com/package/rfc6902-json-diff) library. +Using comparators, you can get result like this: + +``` +/document/div + Attribute "config" differs. Expected: + { + "attr1": 10, + "attr2": 20 + } + Actual: + { + "attr1": 10, + "attr2": 30, + "attr3": -1 + } + Diff: + [ + { + "op": "replace", + "path": "/attr2", + "value": 20 + }, + { + "op": "remove", + "path": "/attr3" + } + ] +``` + +#### Writing custom comparators + +The comparator is a function. It receives two arguments: expected node and actual node. + +Comparators can return following values: + + * any falsy value - skip to next comparator or (if no any) proceed as regular + * `true` - ignore any differences, continue comparison + * non empty string - difference found, string is treated as error message + * object with fields: + * `message` - error message as above, + * `stop` - boolean flag, set to `true` to stop further comparison + +See `lib/comparators/jsonComparator.js` for a working example. + ### Cli utility When installed globally with `npm install -g dom-compare` cli utility is available. @@ -130,9 +208,6 @@ You can try it on bundled samples: Expected CDATA value ' cdata node' instead of 'cdata node ' ``` -### Custom comparators (since 0.3) - - DOM Canonic Form ---------------- diff --git a/lib/comparators/jsonComparator.js b/lib/comparators/jsonComparator.js index 2a891b3..794f9ed 100644 --- a/lib/comparators/jsonComparator.js +++ b/lib/comparators/jsonComparator.js @@ -1,6 +1,8 @@ var jsonDiff = require('rfc6902-json-diff'); -module.exports = function nodeValueJSONComparator(nodeNamesToCompare) { +module.exports = function nodeValueJSONComparator() { + + var nodeNamesToCompare = Array.prototype.slice.call(arguments); function mustCompareNode(node) { if (nodeNamesToCompare && nodeNamesToCompare.length) { diff --git a/samples/json-compare/actual.xml b/samples/json-compare/actual.xml new file mode 100644 index 0000000..96c5a71 --- /dev/null +++ b/samples/json-compare/actual.xml @@ -0,0 +1,3 @@ + +
+
\ No newline at end of file diff --git a/samples/json-compare/demo.js b/samples/json-compare/demo.js new file mode 100644 index 0000000..7bc3bd2 --- /dev/null +++ b/samples/json-compare/demo.js @@ -0,0 +1,24 @@ +var xmldom = require('tensor-xmldom'); +var parser = new xmldom.DOMParser(); +var fs = require('fs'); +var lib = require('../../index'); + +var domExpected = parser.parseFromString(fs.readFileSync('./expected-attr.xml', 'utf8'), 'text/xml'); +var domActual = parser.parseFromString(fs.readFileSync('./actual-attr.xml', 'utf8'), 'text/xml'); + + +var jsonComparator = lib.comparators.jsonComparator('config'); +var reporter = lib.GroupingReporter; +var compare = lib.compare; + +var result = compare(domActual, domExpected, { + comparators: { + ATTRIBUTE_NODE: jsonComparator + } +}); + +if (!result.getResult()) { + console.log(reporter.report(result)); +} + + diff --git a/samples/json-compare/expected.xml b/samples/json-compare/expected.xml new file mode 100644 index 0000000..e6b2b24 --- /dev/null +++ b/samples/json-compare/expected.xml @@ -0,0 +1,3 @@ + +
+
\ No newline at end of file From c5d9b78e843415e8539499a24b0f79042e26610c Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Mon, 16 Mar 2015 00:46:36 +0300 Subject: [PATCH 07/11] TOC fixed --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff4796c..6ef4d30 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ NodeJS module to compare two DOM-trees * [Comparison options](#comparison-options) * [Comments comparison](#comments-comparison) * [Whitespace comparison](#whitespace-comparison) - * [Custom comparators] + * [Custom comparators](#custom-comparators-since-03) + * [Writing custom comparators](#writing-custom-comparators) * [Cli utility](#cli-utility) * [DOM Canonic Form](#dom-canonic-form) @@ -110,7 +111,7 @@ to leading and trailing whitespaces. Set `stripSpaces` option to `true` to automatically strip spaces in text and comment nodes. This option doesn't change the way CDATA sections is compared, they are always compared with respect to whitespaces. -### Custom comparators (since 0.3) +#### Custom comparators (since 0.3) **This is experimental feature and may change in future releases** @@ -173,7 +174,7 @@ Using comparators, you can get result like this: ] ``` -#### Writing custom comparators +##### Writing custom comparators The comparator is a function. It receives two arguments: expected node and actual node. From bde2d0dbd47e226e1afd0323a0950f66ad15b468 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Mon, 16 Mar 2015 00:50:41 +0300 Subject: [PATCH 08/11] Code style fixed --- lib/canonizer.js | 2 +- lib/collector.js | 2 +- lib/compare.js | 3 ++- lib/node_types.js | 3 ++- lib/reporters/groupingReporter.js | 2 +- lib/revxpath.js | 3 ++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/canonizer.js b/lib/canonizer.js index 2318368..4e509e7 100644 --- a/lib/canonizer.js +++ b/lib/canonizer.js @@ -1,6 +1,6 @@ (function(){ - "use strict"; + 'use strict'; var c = require('./node_types'), spaces = ' '; diff --git a/lib/collector.js b/lib/collector.js index 859eb59..03b8437 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -1,6 +1,6 @@ (function(){ - "use strict"; + 'use strict'; function factory(type, revxpath) { var typeMap = {}, diff --git a/lib/compare.js b/lib/compare.js index d35af2f..3ce30dc 100644 --- a/lib/compare.js +++ b/lib/compare.js @@ -1,6 +1,7 @@ (function(){ - "use strict"; + 'use strict'; + function factory(type, Collector) { function Comparator(options, collector) { diff --git a/lib/node_types.js b/lib/node_types.js index 9486b82..d4950cf 100644 --- a/lib/node_types.js +++ b/lib/node_types.js @@ -1,6 +1,7 @@ (function(){ - "use strict"; + 'use strict'; + var node_types = { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, diff --git a/lib/reporters/groupingReporter.js b/lib/reporters/groupingReporter.js index e86464e..1ae3355 100644 --- a/lib/reporters/groupingReporter.js +++ b/lib/reporters/groupingReporter.js @@ -1,6 +1,6 @@ (function() { - "use strict"; + 'use strict'; module.exports = { report: function(res) { diff --git a/lib/revxpath.js b/lib/revxpath.js index 348e014..35c1183 100644 --- a/lib/revxpath.js +++ b/lib/revxpath.js @@ -1,6 +1,7 @@ (function(){ - "use strict"; + 'use strict'; + function factory(type) { function _describeNode(node) { var parent = node.parentNode, From be2091cfb9d79e7f8b8dbfa7fac1ab01feea02d6 Mon Sep 17 00:00:00 2001 From: Shane Osbourne Date: Sat, 9 Aug 2014 21:18:31 +0100 Subject: [PATCH 09/11] Added formatFailure method --- lib/collector.js | 14 +++++++++++--- test/test-collector.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/collector.js b/lib/collector.js index 03b8437..73cd480 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -127,10 +127,18 @@ } } - this._diff.push({ - node: revxpath(ref.ownerElement || ref.parentNode), + var nodeElem = ref.ownerElement || ref.parentNode; + + var failure = { + node: revxpath(nodeElem), message: msg - }); + }; + + if ("function" === typeof this._options.formatFailure) { + failure = this._options.formatFailure(failure, nodeElem); + } + + this._diff.push(failure); return canContinue; }; diff --git a/test/test-collector.js b/test/test-collector.js index 329c968..52010c4 100644 --- a/test/test-collector.js +++ b/test/test-collector.js @@ -252,4 +252,32 @@ describe("Error collection", function(){ }); + describe("Calling user-provided callback for formatting failure", function () { + + it("User can provide a custom callback function for modifying the format of the failure", function () { + + var doc1 = parser.parseFromString(""); + var doc2 = parser.parseFromString(""); + + var result = compare(doc1, doc2, { + formatFailure: function (failure, nodeElem) { + failure.nodeName = nodeElem.nodeName; + failure.randomKey = "random-value"; + return failure; + } + }); + + var failures = result.getDifferences(); + + assert.equal(1, failures.length); + assert.equal("Expected element 'root1' instead of 'root2'", failures[0].message); + + assert.equal(failures[0].nodeName, "#document"); + assert.equal(failures[0].randomKey, "random-value"); + + }); + + }); + + }); \ No newline at end of file From 3d35cb0ed77c7961109579fa4bd1fc6b3bfbc141 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Sat, 25 Apr 2015 23:33:52 +0300 Subject: [PATCH 10/11] Ported shakyShane/dom-compare@cf14cdf6043ac16a6c10b6562c589c6e4dc275d4 --- lib/collector.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/collector.js b/lib/collector.js index 73cd480..87f5026 100644 --- a/lib/collector.js +++ b/lib/collector.js @@ -42,7 +42,7 @@ Collector.prototype.collectFailure = function(expected, actual) { - var msg, canContinue = true, vExpected, vActual, ref = expected || actual, cmprtr, r; + var msg, canContinue = true, vExpected, vActual, ref = expected || actual, cmprtr, r, diff = {}; if(this._options.comparators && (cmprtr = this._options.comparators[comparatorTypeMap[ref.nodeType]])) { if(!(cmprtr instanceof Array)) @@ -73,17 +73,22 @@ if(!msg) { if(expected && !actual) { + diff.type = typeMap[expected.nodeType]; + diff.desc = "missing"; msg = typeMap[expected.nodeType].charAt(0).toUpperCase() + typeMap[expected.nodeType].substr(1) + " " + this._describeNode(expected) + " is missed"; canContinue = true; } else if(!expected && actual) { + diff.type = typeMap[actual.nodeType]; + diff.desc = "extra"; msg = "Extra " + typeMap[actual.nodeType] + " " + this._describeNode(actual); canContinue = true; } else { if(expected.nodeType == actual.nodeType) { if(expected.nodeName == actual.nodeName) { + diff.desc = "modified"; vExpected = expected.nodeValue; vActual = actual.nodeValue; if(this._options.stripSpaces && expected.nodeType != type.CDATA_SECTION_NODE) { @@ -93,6 +98,7 @@ if(vExpected == vActual) throw new Error("Nodes are considered equal but shouldn't"); else { + diff.type = typeMap[expected.nodeType]; switch(expected.nodeType) { case type.ATTRIBUTE_NODE: msg = "Attribute '" + expected.nodeName + "': expected value '" + vExpected + "' instead of '" + vActual + "'"; @@ -104,6 +110,7 @@ msg = "Expected CDATA value '" + vExpected + "' instead of '" + vActual + "'"; break; case type.TEXT_NODE: + diff.type = "text"; msg = "Expected text '" + vExpected + "' instead of '" + vActual + "'"; break; default: @@ -113,12 +120,16 @@ } } else { + diff.desc = "tag:nameChanged"; + diff.type = typeMap[expected.nodeType]; msg = "Expected " + typeMap[expected.nodeType] + " '" + expected.nodeName + "' instead of '" + actual.nodeName + "'"; canContinue = false; } } else { + diff.desc = "tag:typeChanged"; + diff.type = typeMap[expected.nodeType]; msg = "Expected node of type " + expected.nodeType + " (" + typeMap[expected.nodeType] + ") instead of " + actual.nodeType + " (" + typeMap[actual.nodeType] + ")"; @@ -131,7 +142,8 @@ var failure = { node: revxpath(nodeElem), - message: msg + message: msg, + diff: diff }; if ("function" === typeof this._options.formatFailure) { From 4672762830b5c16628f1ffce8acf912e4cdcb256 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Mon, 16 Mar 2015 00:57:48 +0300 Subject: [PATCH 11/11] Note on breaking changes --- .travis.yml | 2 -- README.md | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1fca0b..4b88df2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: node_js node_js: - - 0.6 - - 0.8 - 0.10 - 0.12 - "iojs" diff --git a/README.md b/README.md index 6ef4d30..77845b3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ dom-compare [![NPM version](https://badge.fury.io/js/dom-compare.png)](http://badge.fury.io/js/dom-compare) [![Dependency Status](https://gemnasium.com/Olegas/dom-compare.png)](https://gemnasium.com/Olegas/dom-compare) +**Breaking changes. v0.3.0 requires node version >=0.10** + NodeJS module to compare two DOM-trees * [DOM Comparison](#dom-comparison)