Code Monkey home page Code Monkey logo

dom-compare's Introduction

dom-compare

Build Status Coverage Status NPM version

NodeJS module to compare two DOM-trees

Works with Node.JS v0.10+

DOM Comparison

Consider two documents. Expected:

<document>
    <!-- comment -->
    <element attribute="10" attributeX="100">
        <text>  text content </text>
        <inner>
            <node />
        </inner>
    </element>
    <![CDATA[  cdata node]]>
</document>

and actual one:

<document>
    <element attribute="100">
        <text>text content</text>
        <inner />
        <inner2 />
    </element>
    <![CDATA[cdata node  ]]>
</document>

One can compare them, get the result (is them equals, or not), and get extended report (why them are different).

var compare = require('dom-compare').compare,
    reporter = require('dom-compare').GroupingReporter,
    expected = ..., // expected DOM tree
    actual = ..., // actual one
    result, diff, groupedDiff;

// compare to DOM trees, get a result object
result = compare(expected, actual);

// get comparison result
console.log(result.getResult()); // false cause' trees are different

// get all differences
diff = result.getDifferences(); // array of diff-objects

// differences, grouped by node XPath
groupedDiff = reporter.getDifferences(result); // object, key - node XPATH, value - array of differences (strings)

// string representation
console.log(reporter.report(result));

Diff-object has a following form:

{
    node: "/document/element",
    message: "Attribute 'attribute': expected value '10' instead of '100'";
}

By using GroupingReporter one can get a result of a following type

{
    '/document/element': [
        "Attribute 'attribute': expected value '10' instead of '100'",
        "Extra attribute 'attributeX'"
    ]    
}

Comparison options

Comparison function can take a third argument with options like this:

var options = {
    stripSpaces: true,
    compareComments: true,
    collapseSpaces: true,
    normalizeNewlines: true
};

result = compare(expected, actual, options);

Comments comparison

By default, all comments are ignored. Set compareComments options to true to compare them too.

Whitespace comparison

By default, all text nodes (text, CDATA, comments if enabled as mentioned above) compared with respect to leading, trailing, and internal 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. Set collapseSpaces option to true to automatically collapse all 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. Set normalizeNewlines option to true to automatically normalize new line characters in text, comment, and CDATA nodes.

Cli utility

When installed globally with npm install -g dom-compare cli utility is available. See usage information and command-line options with domcompare --help

You can try it on bundled samples:

  $ cd samples
  $ domcompare -s ./expected.xml ./actual.xml
  Documents are not equal
  /document/element
      Attribute 'attribute': expected value '10' instead of '100'
      Attribute 'attributeX' is missed
      Extra element 'inner2'
  /document/element/inner
      Element 'node' is missed
  /document
      Expected CDATA value '  cdata node' instead of 'cdata node  '

DOM Canonic Form

Implemented as XMLSerializer interface

Simple rules

  1. Every node (text, node, attribute) on a new line
  2. Empty tags - in a short form
  3. Node indent - 4 spaces, attribute indent - 2 spaces
  4. Attributes are sorted alphabetically
  5. Attribute values are serialized in double quotes

Consider the following XML-document...

<document>
  <element>DOM Compare</element>
  <emptyNode></emptyNode>
  <element attribute1="value" attribute2="value">
    <element>Text node</element>
  </element>
</document>

...and code snippet...

var canonizingSerializer = new (require('dom-compare').XMLSerializer)();
var doc = ...; // parse above document somehow 
console.log(canonizingSerializer.serializeToString(doc));

You'll receive the following output

<document>
    <element>
        DOM Compare
    </element>
    <emptyNode />
    <element
      attribute1="value"
      attribute2="value">
        <element>
            Text node
        </element>
    </element>
</document>

dom-compare's People

Contributors

dependabot[bot] avatar olegas avatar paolo-cargnin avatar rondevera avatar sbichenko avatar svenheden avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dom-compare's Issues

Whitespaces are not correctly compared

I would expect the module to identify some xml only as equal, if their contents with text-contents and their whitespaces are really the same.
It seems that the module does ignore it, if the one XML has got some spaces somewhere and the other has got a differenz amount of spaces or none at all.

const dom = require('xmldom').DOMParser;
const xmlSerializer = require('xmldom').XMLSerializer;
const compare = require('dom-compare').compare;


const xmlA = new dom().parseFromString("<foo> </foo>");
const xmlB = new dom().parseFromString("<foo></foo>");
const xmlC = new dom().parseFromString("<foo> a</foo>");

console.log(new xmlSerializer().serializeToString(xmlA));
console.log(new xmlSerializer().serializeToString(xmlB));

const resultAB = compare(xmlA, xmlB); //empty diff, but should contain " '    ' is not ' ' "
const resultBC = compare(xmlB, xmlC); //non-empty diff, because " " is not " a" what is correct

console.log(resultAB);
console.log(resultBC);

"stripSpaces" should be false as default so I don't know what to do to achieve this.

Node type undefined comparison is not implemented

Hi,
I'm getting the following error:
Node type undefined comparison is not implemented

Error: Node type undefined comparison is not implemented
    at Error (native)
    at Comparator.compareNode (node_modules/dom-compare/lib/compare.js:130:22)

This is the file I'm trying to compare

<?xml version="1.0"?>

<conf>
  <domain>
      <name>name</name>
      <custom>value</custom>
      <custom>value</custom>
      <custom>value</custom>
  </domain>
</conf>

And using the defaults as in the home page

var options = {
       stripSpaces: true,
       compareComments: true
};

Thanks in advanced

Use UMD for module definitions

I would like to use dom-compare together with RequireJS (AMD) module loader.

Since dom-compare currently uses Node module definitions (CommonJS-like),

I suggest to use UMD as an universal module loader pattern,
particularly, we could leverage this adapter:
https://github.com/umdjs/umd/blob/master/commonjsAdapter.js

I have already re-factored dom-compare to use just AMD (https://github.com/lfryc/dom-compare/commits/requirejs), but I can open new pull request with refactoring to UMD.

License issue

Hi,

Awesome work. Is it open to use? or will be licensed in near future?

Please let us know. Thanks in advance!!!

Not all differences is returned

Initially reported in #34 by @tiagorvmartins, moved here as a separate issue.

For the following examples:

actual.xml

<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>I got changed</body>
</note>

expected.xml

<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
  <age>seventeen</age>
  <address>street of freedom</address>
  <anotherelement>
    <message>Hiyo</message>
  </anotherelement>
</note>

It returns only the following differences:

Differences:  [ { node: '/note/body',
    message:
     'Expected text \'Don\'t forget me this weekend!\' instead of \'I got changed\'' },
  { node: '/note', message: 'Element \'age\' is missed' } ]

I was expecting address and anotherelement as well to be included on the differences :/

Error: Node type 11 comparison is not implemented

A fatal runtime error is thrown when running dom-compare against a document with a DocumentFragment in it: Error: Node type 11 comparison is not implemented.

To duplicate, add a <template> tag to documents and run dom-compare against them.

It seems to be caused by the comparator function compareNode not containing a case for for DocumentFragment (such as the <template> tag).

I encountered this problem using the bs-html-injector npm library for my project (which contains template tags in .hbs docs and compiles them). Though it uses a fork of this dom-compare, both this repo and the fork have this bug, and I'm hoping reporting it at the root here gets it fixed everywhere :)

Reportedly, all that is needed is to add a case for type.DOCUMENT_FRAGMENT_NODE to the switch statement in the compareNode function - I would write the PR myself, but I'm not confident that I understand how to make sure that the correct behaviour happens.

It's mentioned in #18 in html-injector but it seems that nothing was done about it.

Comparing attributes gives 'nodeType is unexpected' error

When comparing attributes, I get the error

Error: nodeValue is not equal, but nodeType is unexpected
    at Collector.collectFailure (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/collector.js:113:34)
    at Comparator._compareAttributes (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:74:36)
    at Comparator.compareNode (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:108:28)
    at Comparator._compareNodeList (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:36:23)
    at Comparator.compareNode (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:109:27)
    at Comparator._compareNodeList (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:36:23)
    at Comparator.compareNode (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:109:27)
    at Comparator.compareNode (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:106:28)
    at module.exports (/home/arnold/Projects/legalthings/legalform-js/node_modules/dom-compare/lib/compare.js:137:18)
    at Object.<anonymous> (/home/arnold/Projects/legalthings/legalform-js/dom-compare-tests.js:10:14)
var compare = require('dom-compare').compare;
var jsdom = require('jsdom');

// Those are the HTML fragments that we want to compare:
var expectedHTML = '<div class="foo"></div>';
var actualHTML = '<div class="bar"></div>';

var expectedDOM = jsdom.jsdom(expectedHTML);
var actualDOM = jsdom.jsdom(actualHTML);
var result = compare(expectedDOM, actualDOM);

console.log('diff array:', result.getDifferences());

I've dumped the actual and expected variables in collector.js and noticed they don't seem to have any properties.

console.log(expected, actual, vExpected, vActual);

yields

Attr {} Attr {} 'foo' 'bar'

xPath generation is incorrect when you have the same node name at multiple levels like in HTML

In the code here: https://github.com/Olegas/dom-compare/blob/master/lib/revxpath.js#L10
When parent.getElementsByTagName() is called it gets all tags with the given name regardless of the level, so the result is not just siblings with the same name.

It would actually be better to loop over all the children of the parent and only collect elements with the same name in an array in iterate over that.

Also, I'm using xmldom package to create parse an HTML file into a dom tree and comparing that.

Example of this:

var xmldom = require("xmldom");
var compare = require('dom-compare').compare;

var parser = new xmldom.DOMParser();

const xmlA = `
<html>
    <body>
        <div>
            <div>
                <div>X</div>
            </div>
            <div>
                <div>Y</div>
            </div>
            <div>
                <div>Z</div>
            </div>
        </div>
        <div>
            <section>
                <div>
                    Some Text
                </div>
            </section>
            <section>
                <div>
                    Some Text Part 2
                </div>
            </section>
        </div>
    </body>
</html>
`;

const xmlB = `
<html>
    <body>
        <div>
            <div>
                <div>X</div>
            </div>
            <div>
                <div>Y</div>
            </div>
            <div>
                <div>Z</div>
            </div>
        </div>
        <div>
            <section>
                <div>
                    Some Text
                </div>
            </section>
            <section>
                <div>
                    Some Text Modified
                </div>
            </section>
        </div>
    </body>
</html>
`;

console.log(compare(parser.parseFromString(xmlA), parser.parseFromString(xmlB)).getDifferences());

Output:

[
  {
    node: '/html/body/div[8]/section[2]/div',
    message: 'Expected text \'\n                    Some Text Part 2\n                \' instead of \'\n                    Some Text Modified\n                \'' 
  }
]

But the XPath is generated incorrectly the correct XPath for the element that changed is: /html/body/div[2]/section[2]/div

Add & Delete elements in the middle of DOM tree gives incorrect results

This is part of my Original DOM tree,

<div id="buttonContainer">
    <button id="addNodeBtn">Add node</button>
    <button id="setAttrBtn">Add attribute</button>
    <button id="setContentBtn">Change content</button>
    <button id="removeNodBtn">Remove node</button>
</div>

When I remove button(Id=setAttrBtn) it recognize as follows,

  • A deletion of button(id="removeNodBtn")
  • All the attributes of button( id="setContentBtn") has replaced the button(Id=setAttrBtn)

Note: Same behavior occur when i add a new element in the middle of the button sets.

Please look into this issue.

Is the user supposed to prepare the DOM/HTML string somehow?

Your node example, leaves the actual values of expected and actual ambiguous? Are these supposed to be html strings? File paths? Or some preprocessed DOM tree? I really want to try/use this package but am stuck trying to figure this out. Any help would be appreciated.

    var compare = require('dom-compare').compare,
    reporter = require('dom-compare').GroupingReporter,
    expected = ..., // expected DOM tree
    actual = ..., // actual one
    result, diff, groupedDiff;

Template tags causing error in compare.js

Hi! ๐Ÿ‘‹

Firstly, thanks for your work on this project! ๐Ÿ™‚

Today I used patch-package to patch [email protected] for the project I'm working on.

I am using Petite Vue in a Lauravel Blade file. Like any Vue project, it required me to use to Template tags to handle rendering components. However, the compare.js file in Dom-Compare was emitting an error because it does not account for Document Fragments such as Template tags. I added a line to Switch block to account for document fragments.

See the difference below:

diff --git a/node_modules/dom-compare-temp/lib/compare.js b/node_modules/dom-compare-temp/lib/compare.js
index 73f6361..108850a 100644
--- a/node_modules/dom-compare-temp/lib/compare.js
+++ b/node_modules/dom-compare-temp/lib/compare.js
@@ -111,6 +111,8 @@
                // fallthrough
             case type.CDATA_SECTION_NODE:
                // fallthrough
+            case type.DOCUMENT_FRAGMENT_NODE:
+               // fallthrough
             case type.COMMENT_NODE:
                if (left.nodeType == type.COMMENT_NODE && !this._options.compareComments)
                   return true;

This issue body was partially generated by patch-package.

Use literal require paths

I would like to use dom-compare in the browser by bundling it with browserify. The only required change is to use String literals for require paths in index.js (because browserify analyses dependencies statically).

Would you accept a PR?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.