diff --git a/HACKING.md b/HACKING.md index d0821e9..3e6f812 100644 --- a/HACKING.md +++ b/HACKING.md @@ -1,3 +1,7 @@ # How to run tests # In the source directory install the npm deps by typing `npm install`. Then load `tests/index.html` in a browser. + +# How to compile # + +See [http://www.typescriptlang.org/](http://www.typescriptlang.org/) \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..b4bf82c --- /dev/null +++ b/bower.json @@ -0,0 +1,31 @@ +{ + "name": "brucek-mutation-summary", + "description": "Makes observing the DOM fast and easy", + "main": [ + "src/mutation-summary.js" + ], + "moduleType": [ + "globals" + ], + "keywords": [ + "dom", + "mutation", + "observer" + ], + "authors": [ + "" + ], + "license": "Apache 2.0", + "ignore": [ + "docs/", + "examples/", + "node_modules/", + "tests/", + "util/" + ], + "repository": { + "type": "git", + "url": "git://github.com/brucek/mutation-summary.git" + }, + "private": true +} \ No newline at end of file diff --git a/src/mutation-summary.js b/src/mutation-summary.js index c32565e..bba8f10 100644 --- a/src/mutation-summary.js +++ b/src/mutation-summary.js @@ -271,7 +271,7 @@ var TreeChanges = (function (_super) { return TreeChanges; })(NodeMap); var MutationProjection = (function () { - // TOOD(any) + // TODO(any) function MutationProjection(rootNode, mutations, selectors, calcReordered, calcOldPreviousSibling) { this.rootNode = rootNode; this.mutations = mutations; @@ -730,16 +730,34 @@ var validNameNonInitialChar = /[a-zA-Z0-9_\-]+/; function escapeQuotes(value) { return '"' + value.replace(/"/, '\\\"') + '"'; } +var Modifier; +(function (Modifier) { + Modifier[Modifier["none"] = 0] = "none"; + Modifier[Modifier["containsToken"] = 1] = "containsToken"; + Modifier[Modifier["startsWith"] = 2] = "startsWith"; + Modifier[Modifier["endsWith"] = 3] = "endsWith"; + Modifier[Modifier["containsAny"] = 4] = "containsAny"; + Modifier[Modifier["startsHyphen"] = 5] = "startsHyphen"; +})(Modifier || (Modifier = {})); var Qualifier = (function () { function Qualifier() { + this.modifier = Modifier.none; } Qualifier.prototype.matches = function (oldValue) { if (oldValue === null) return false; if (this.attrValue === undefined) return true; - if (!this.contains) + if (this.modifier == Modifier.none) return this.attrValue == oldValue; + if (this.modifier == Modifier.startsWith) + return this.attrValue == oldValue.slice(0, this.attrValue.length); + if (this.modifier == Modifier.endsWith) + return this.attrValue == oldValue.slice(-this.attrValue.length); + if (this.modifier == Modifier.containsAny) + return oldValue.indexOf(this.attrValue) != -1; + if (this.modifier == Modifier.startsHyphen) + return this.attrValue == oldValue || (this.attrValue + '-') == oldValue.slice(0, this.attrValue.length + 1); var tokens = oldValue.split(' '); for (var i = 0; i < tokens.length; i++) { if (this.attrValue === tokens[i]) @@ -748,12 +766,20 @@ var Qualifier = (function () { return false; }; Qualifier.prototype.toString = function () { - if (this.attrName === 'class' && this.contains) + if (this.attrName === 'class' && this.modifier == Modifier.containsToken) return '.' + this.attrValue; - if (this.attrName === 'id' && !this.contains) + if (this.attrName === 'id' && this.modifier == Modifier.none) return '#' + this.attrValue; - if (this.contains) + if (this.modifier == Modifier.containsToken) return '[' + this.attrName + '~=' + escapeQuotes(this.attrValue) + ']'; + if (this.modifier == Modifier.startsWith) + return '[' + this.attrName + '^=' + escapeQuotes(this.attrValue) + ']'; + if (this.modifier == Modifier.endsWith) + return '[' + this.attrName + '$=' + escapeQuotes(this.attrValue) + ']'; + if (this.modifier == Modifier.containsAny) + return '[' + this.attrName + '*=' + escapeQuotes(this.attrValue) + ']'; + if (this.modifier == Modifier.startsHyphen) + return '[' + this.attrName + '|=' + escapeQuotes(this.attrValue) + ']'; if ('attrValue' in this) return '[' + this.attrName + '=' + escapeQuotes(this.attrValue) + ']'; return '[' + this.attrName + ']'; @@ -874,7 +900,7 @@ var Selector = (function () { newQualifier(); currentSelector.tagName = '*'; currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -905,7 +931,7 @@ var Selector = (function () { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -934,7 +960,7 @@ var Selector = (function () { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -974,7 +1000,7 @@ var Selector = (function () { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -1017,7 +1043,27 @@ var Selector = (function () { break; } if (c == '~') { - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; + state = EQUAL; + break; + } + if (c == '^') { + currentQualifier.modifier = Modifier.startsWith; + state = EQUAL; + break; + } + if (c == '$') { + currentQualifier.modifier = Modifier.endsWith; + state = EQUAL; + break; + } + if (c == '*') { + currentQualifier.modifier = Modifier.containsAny; + state = EQUAL; + break; + } + if (c == '|') { + currentQualifier.modifier = Modifier.startsHyphen; state = EQUAL; break; } @@ -1033,7 +1079,27 @@ var Selector = (function () { throw Error(SYNTAX_ERROR); case EQUIV_OR_ATTR_QUAL_END: if (c == '~') { - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; + state = EQUAL; + break; + } + if (c == '^') { + currentQualifier.modifier = Modifier.startsWith; + state = EQUAL; + break; + } + if (c == '$') { + currentQualifier.modifier = Modifier.endsWith; + state = EQUAL; + break; + } + if (c == '*') { + currentQualifier.modifier = Modifier.containsAny; + state = EQUAL; + break; + } + if (c == '|') { + currentQualifier.modifier = Modifier.startsHyphen; state = EQUAL; break; } diff --git a/src/mutation-summary.ts b/src/mutation-summary.ts index ee3a268..9452895 100644 --- a/src/mutation-summary.ts +++ b/src/mutation-summary.ts @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +declare var WebKitMutationObserver: any; + var MutationObserverCtor; if (typeof WebKitMutationObserver !== 'undefined') MutationObserverCtor = WebKitMutationObserver; @@ -335,7 +337,7 @@ class MutationProjection { private characterDataOnly:boolean; private matchCache:NumberMap>; - // TOOD(any) + // TODO(any) constructor(public rootNode:Node, public mutations:MutationRecord[], public selectors:Selector[], @@ -590,7 +592,7 @@ class MutationProjection { var change = this.treeChanges.get(target); if (!change.characterData || target.textContent == change.characterDataOldValue) - continue + continue; result.push(target); } @@ -860,7 +862,7 @@ class Summary { } if (query.all || query.characterData) { - var characterDataChanged = projection.getCharacterDataChanged() + var characterDataChanged = projection.getCharacterDataChanged(); if (query.characterData) this.valueChanged = characterDataChanged; @@ -901,10 +903,12 @@ function escapeQuotes(value:string):string { return '"' + value.replace(/"/, '\\\"') + '"'; } +enum Modifier { none, containsToken, startsWith, endsWith, containsAny, startsHyphen } + class Qualifier { public attrName:string; public attrValue:string; - public contains:boolean; + public modifier:Modifier = Modifier.none; constructor() {} @@ -915,9 +919,21 @@ class Qualifier { if (this.attrValue === undefined) return true; - if (!this.contains) + if (this.modifier == Modifier.none) return this.attrValue == oldValue; + if (this.modifier == Modifier.startsWith) + return this.attrValue == oldValue.slice(0, this.attrValue.length); + + if (this.modifier == Modifier.endsWith) + return this.attrValue == oldValue.slice(-this.attrValue.length); + + if (this.modifier == Modifier.containsAny) + return oldValue.indexOf(this.attrValue) != -1; + + if (this.modifier == Modifier.startsHyphen) + return this.attrValue == oldValue || (this.attrValue + '-') == oldValue.slice(0, this.attrValue.length + 1); + var tokens = oldValue.split(' '); for (var i = 0; i < tokens.length; i++) { if (this.attrValue === tokens[i]) @@ -928,15 +944,27 @@ class Qualifier { } public toString():string { - if (this.attrName === 'class' && this.contains) + if (this.attrName === 'class' && this.modifier == Modifier.containsToken) return '.' + this.attrValue; - if (this.attrName === 'id' && !this.contains) + if (this.attrName === 'id' && this.modifier == Modifier.none) return '#' + this.attrValue; - if (this.contains) + if (this.modifier == Modifier.containsToken) return '[' + this.attrName + '~=' + escapeQuotes(this.attrValue) + ']'; + if (this.modifier == Modifier.startsWith) + return '[' + this.attrName + '^=' + escapeQuotes(this.attrValue) + ']'; + + if (this.modifier == Modifier.endsWith) + return '[' + this.attrName + '$=' + escapeQuotes(this.attrValue) + ']'; + + if (this.modifier == Modifier.containsAny) + return '[' + this.attrName + '*=' + escapeQuotes(this.attrValue) + ']'; + + if (this.modifier == Modifier.startsHyphen) + return '[' + this.attrName + '|=' + escapeQuotes(this.attrValue) + ']'; + if ('attrValue' in this) return '[' + this.attrName + '=' + escapeQuotes(this.attrValue) + ']'; @@ -1088,7 +1116,7 @@ class Selector { newQualifier(); currentSelector.tagName = '*'; currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -1123,7 +1151,7 @@ class Selector { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -1156,7 +1184,7 @@ class Selector { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -1203,7 +1231,7 @@ class Selector { if (c == '.') { newQualifier(); currentQualifier.attrName = 'class'; - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; state = QUALIFIER_NAME_FIRST_CHAR; break; } @@ -1254,7 +1282,31 @@ class Selector { } if (c == '~') { - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; + state = EQUAL; + break; + } + + if (c == '^') { + currentQualifier.modifier = Modifier.startsWith; + state = EQUAL; + break; + } + + if (c == '$') { + currentQualifier.modifier = Modifier.endsWith; + state = EQUAL; + break; + } + + if (c == '*') { + currentQualifier.modifier = Modifier.containsAny; + state = EQUAL; + break; + } + + if (c == '|') { + currentQualifier.modifier = Modifier.startsHyphen; state = EQUAL; break; } @@ -1274,7 +1326,31 @@ class Selector { case EQUIV_OR_ATTR_QUAL_END: if (c == '~') { - currentQualifier.contains = true; + currentQualifier.modifier = Modifier.containsToken; + state = EQUAL; + break; + } + + if (c == '^') { + currentQualifier.modifier = Modifier.startsWith; + state = EQUAL; + break; + } + + if (c == '$') { + currentQualifier.modifier = Modifier.endsWith; + state = EQUAL; + break; + } + + if (c == '*') { + currentQualifier.modifier = Modifier.containsAny; + state = EQUAL; + break; + } + + if (c == '|') { + currentQualifier.modifier = Modifier.startsHyphen; state = EQUAL; break; } @@ -1298,7 +1374,7 @@ class Selector { case EQUAL: if (c == '=') { currentQualifier.attrValue = ''; - state = VALUE_FIRST_CHAR + state = VALUE_FIRST_CHAR; break; } diff --git a/tests/setup.js b/tests/setup.js index ea4c4c5..4953428 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -13,9 +13,9 @@ // limitations under the License. function assertSelectorNames(selectors, expectSelectorStrings) { - assert.strictEqual(expectSelectorStrings.length, selectors.length); + assert.strictEqual(selectors.length, expectSelectorStrings.length); expectSelectorStrings.forEach(function(expectSelectorString, i) { - assert.strictEqual(expectSelectorString, selectors[i].selectorString); + assert.strictEqual(selectors[i].selectorString, expectSelectorString); }); } @@ -256,10 +256,6 @@ suite('Setup', function() { MutationSummary.parseElementFilter('div[foo=bar baz]'); }); - assert.throws(function() { - MutationSummary.parseElementFilter('div[foo|=bar]'); - }); - assertSelectorNames( MutationSummary.parseElementFilter('div[foo~=bar]'), ['div[foo~="bar"]'] @@ -278,6 +274,78 @@ suite('Setup', function() { MutationSummary.parseElementFilter('div[foo~ =bar]'); }); + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo^=bar]'), + ['div[foo^="bar"]'] + ); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo^="bar "]'), + ['div[foo^="bar "]'] + ); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo^=]'); + }); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo^ =bar]'); + }); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo$=bar]'), + ['div[foo$="bar"]'] + ); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo$="bar "]'), + ['div[foo$="bar "]'] + ); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo$=]'); + }); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo$ =bar]'); + }); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo*=bar]'), + ['div[foo*="bar"]'] + ); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo*="bar "]'), + ['div[foo*="bar "]'] + ); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo*=]'); + }); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo* =bar]'); + }); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo|=bar]'), + ['div[foo|="bar"]'] + ); + + assertSelectorNames( + MutationSummary.parseElementFilter('div[foo|="bar "]'), + ['div[foo|="bar "]'] + ); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo|=]'); + }); + + assert.throws(function() { + MutationSummary.parseElementFilter('div[foo| =bar]'); + }); + assertSelectorNames( MutationSummary.parseElementFilter('div[foo][bar]'), ['div[foo][bar]'] @@ -317,7 +385,7 @@ suite('Setup', function() { // queries is required. assert.throws(function() { new MutationSummary({ - callback: function() {}, + callback: function() {} }); }); @@ -434,7 +502,7 @@ suite('Setup', function() { assert.throws(function() { new MutationSummary({ callback: function() {}, - queries: [{ element: 'div[noTrailingBracket', }] + queries: [{ element: 'div[noTrailingBracket' }] }); }); diff --git a/tests/test-validator.js b/tests/test-validator.js index 9ade2b7..95743ad 100644 --- a/tests/test-validator.js +++ b/tests/test-validator.js @@ -37,7 +37,7 @@ MutationSummary.createQueryValidator = function(root, query) { function allValidator(summary, stayed, old, current) { summary.reordered.forEach(function(node) { var oldPreviousSiblingMap = old.get(summary.getOldParentNode(node)); - assert.strictEqual(oldPreviousSiblingMap.get(node), summary.getOldPreviousSibling(node)); + assert.strictEqual(summary.getOldPreviousSibling(node), oldPreviousSiblingMap.get(node)); }); } @@ -61,7 +61,7 @@ MutationSummary.createQueryValidator = function(root, query) { compareNodeArrayIgnoreOrder(changed, summary.valueChanged); changed.forEach(function(node) { - assert.strictEqual(old.get(node), summary.getOldCharacterData(node)); + assert.strictEqual(summary.getOldCharacterData(node), old.get(node)); }); } @@ -85,7 +85,7 @@ MutationSummary.createQueryValidator = function(root, query) { compareNodeArrayIgnoreOrder(changed, summary.valueChanged); changed.forEach(function(node) { - assert.strictEqual(old.get(node), summary.getOldAttribute(node, query.attribute)); + assert.strictEqual(summary.getOldAttribute(node, query.attribute), old.get(node)); }); } @@ -160,7 +160,7 @@ MutationSummary.createQueryValidator = function(root, query) { }); function checkOldParentNode(node) { - assert.strictEqual(old.get(node).parentNode, summary.getOldParentNode(node)); + assert.strictEqual(summary.getOldParentNode(node), old.get(node).parentNode); } summary.removed.forEach(checkOldParentNode); diff --git a/tests/test.js b/tests/test.js index ee57ab3..dded788 100644 --- a/tests/test.js +++ b/tests/test.js @@ -2,7 +2,7 @@ /// /// function compareNodeArrayIgnoreOrder(expected, actual) { - assert.strictEqual(expected.length, actual.length); + assert.strictEqual(actual.length, expected.length); var map = new MutationSummary.NodeMap(); expected.forEach(function (node) { map.set(node, true); @@ -61,7 +61,7 @@ suite('Mutation Summary', function () { compareNodeArrayIgnoreOrder(expect.removed, changed.removed); if (options.oldPreviousSibling) { expect.removed.forEach(function (node, index) { - assert.strictEqual(expect.removedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.removedOldPreviousSibling[index]); }); } // reparented @@ -70,11 +70,10 @@ suite('Mutation Summary', function () { compareNodeArrayIgnoreOrder(expect.reparented, changed.reparented); if (options.oldPreviousSibling) { expect.reparented.forEach(function (node, index) { - assert.strictEqual(expect.reparentedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.reparentedOldPreviousSibling[index]); }); } - } - else { + } else { assert.isUndefined(changed.reparented); } // reordered @@ -82,10 +81,9 @@ suite('Mutation Summary', function () { assert(typeof expect.reordered == typeof changed.reordered); compareNodeArrayIgnoreOrder(expect.reordered, changed.reordered); expect.reordered.forEach(function (node, index) { - assert.strictEqual(expect.reorderedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.reorderedOldPreviousSibling[index]); }); - } - else { + } else { assert.isUndefined(changed.reordered); } // valueChanged @@ -94,24 +92,22 @@ suite('Mutation Summary', function () { compareNodeArrayIgnoreOrder(expect.valueChanged, changed.valueChanged); var getOldFunction = query.attribute ? 'getOldAttribute' : 'getOldCharacterData'; expect.valueChanged.forEach(function (node, index) { - assert.strictEqual(expect.oldValues[index], changed[getOldFunction](node, query.attribute)); + assert.strictEqual(changed[getOldFunction](node, query.attribute), expect.oldValues[index]); }); - } - else { + } else { assert.isUndefined(changed.valueChanged); } // attributeChanged if (query.all || query.elementAttributes) { assert(typeof expect.attributeChanged == typeof changed.attributeChanged); - assert.strictEqual(Object.keys(expect.attributeChanged).length, Object.keys(changed.attributeChanged).length); + assert.strictEqual(Object.keys(changed.attributeChanged).length, Object.keys(expect.attributeChanged).length); Object.keys(expect.attributeChanged).forEach(function (attrName) { compareNodeArrayIgnoreOrder(expect.attributeChanged[attrName], changed.attributeChanged[attrName]); expect.attributeOldValue[attrName].forEach(function (attrOldValue, index) { - assert.strictEqual(expect.attributeOldValue[attrName][index], changed.getOldAttribute(expect.attributeChanged[attrName][index], attrName)); + assert.strictEqual(changed.getOldAttribute(expect.attributeChanged[attrName][index], attrName), expect.attributeOldValue[attrName][index]); }); }); - } - else { + } else { assert.isUndefined(changed.attributeChanged); } } @@ -303,7 +299,7 @@ suite('Mutation Summary', function () { }); test('Element Attribute Specified', function () { startObserving({ - element: 'div[foo], A, *[bar], div[ baz = "bat"], span#foo[blow~=blarg]' + element: 'div[foo], A, *[bar], div[ baz = "bat"], span#foo[blow~=blarg], span[some^=thing], span[another$=.html], ' + 'span[splat*=atat], div[lang|=en]' }); var div = document.createElement('div'); testDiv.appendChild(div); @@ -319,6 +315,18 @@ suite('Mutation Summary', function () { var p = document.createElement('P'); testDiv.appendChild(p); p.setAttribute('baz', 'baz'); + var span2 = document.createElement('span'); + span2.setAttribute('some', 'not a thing'); + div2.appendChild(span2); + var span3 = document.createElement('span'); + span3.setAttribute('another', '.html at the front'); + div3.appendChild(span3); + var span4 = document.createElement('span'); + span4.setAttribute('splat', 'no at at'); + p.appendChild(span4); + var div4 = document.createElement('div'); + testDiv.appendChild(div4); + div4.setAttribute('lang', 'not-en-matching'); assertSummary({ added: [div] }); @@ -328,12 +336,17 @@ suite('Mutation Summary', function () { div3.setAttribute('baz', 'bat'); span.id = 'foo'; span.setAttribute('blow', 'blarg bloog'); + span2.setAttribute('some', 'thinggood'); + span3.setAttribute('another', 'this one ends in.html'); + span4.setAttribute('splat', 'some-atat-thing'); + div4.setAttribute('lang', 'en-yes'); assertSummary({ - added: [p, div3, span], + added: [p, div3, span, span2, span3, span4, div4], removed: [div] }); div3.removeAttribute('baz'); div3.setAttribute('baz', 'bat'); + div4.setAttribute('lang', 'en'); assertNothingReported(); }); test('Case Insensitive Element Attributes', function () { @@ -445,7 +458,7 @@ suite('Mutation Summary', function () { testDiv.appendChild(div2); startObserving({ element: ' div[ baz ]', - elementAttributes: 'foo boo', + elementAttributes: 'foo boo' }); div.setAttribute('foo', 'bar2'); div.setAttribute('baz', 'bat2'); @@ -534,7 +547,7 @@ suite('Mutation Summary', function () { }); div.removeChild(span); assertSummary({ - removed: [span], + removed: [span] }); }); test('Sequential Removals', function () { @@ -838,16 +851,14 @@ suite('Mutation Summary', function () { var summary = summaries[0]; count++; if (count == 1) { - assert.strictEqual(1, summary.added.length); + assert.strictEqual(summary.added.length, 1); div = testDiv.appendChild(document.createElement('div')); - } - else if (count == 2) { - assert.strictEqual(2, summary.added.length); + } else if (count == 2) { + assert.strictEqual(summary.added.length, 2); div = testDiv.appendChild(document.createElement('div')); summary1.disconnect(); - } - else if (count == 3) { - assert.strictEqual(1, summary.added.length); + } else if (count == 3) { + assert.strictEqual(summary.added.length, 1); summary1.disconnect(); async(); } @@ -860,16 +871,14 @@ suite('Mutation Summary', function () { var summary = summaries[0]; count++; if (count == 1) { - assert.strictEqual(1, summary.added.length); + assert.strictEqual(summary.added.length, 1); div = testDiv.appendChild(document.createElement('div')); - } - else if (count == 2) { - assert.strictEqual(2, summary.added.length); + } else if (count == 2) { + assert.strictEqual(summary.added.length, 2); div = testDiv.appendChild(document.createElement('div')); summary2.disconnect(); - } - else if (count == 3) { - assert.strictEqual(1, summary.added.length); + } else if (count == 3) { + assert.strictEqual(summary.added.length, 1); summary2.disconnect(); async(); } @@ -891,7 +900,7 @@ suite('Mutation Summary', function () { setTimeout(function () { div.setAttribute('bar', 'baz'); setTimeout(function () { - assert.strictEqual(1, callbackCount); + assert.strictEqual(callbackCount, 1); async(); }); }, 0); @@ -950,11 +959,9 @@ suite('TreeMirror Fuzzer', function () { if (pass >= PASSES) { mirrorClient.disconnect(); async(); - } - else + } else doNextPass(); } - ; doNextPass(); }); function testRandomCloneAndTestCopy() { @@ -963,20 +970,19 @@ suite('TreeMirror Fuzzer', function () { assertTreesEqual(testDiv, copy); } function assertTreesEqual(node, copy) { - assert.strictEqual(node.tagName, copy.tagName); - assert.strictEqual(node.id, copy.id); - assert.strictEqual(node.nodeType, copy.nodeType); + assert.strictEqual(copy.tagName, node.tagName); + assert.strictEqual(copy.id, node.id); + assert.strictEqual(copy.nodeType, node.nodeType); if (node.nodeType == Node.ELEMENT_NODE) { - assert.strictEqual(node.attributes.length, copy.attributes.length); + assert.strictEqual(copy.attributes.length, node.attributes.length); for (var i = 0; i < node.attributes.length; i++) { var attr = node.attributes[i]; - assert.strictEqual(attr.value, copy.getAttribute(attr.name)); + assert.strictEqual(copy.getAttribute(attr.name), attr.value); } + } else { + assert.strictEqual(copy.textContent, node.textContent); } - else { - assert.strictEqual(node.textContent, copy.textContent); - } - assert.strictEqual(node.childNodes.length, copy.childNodes.length); + assert.strictEqual(copy.childNodes.length, node.childNodes.length); var copyChild = copy.firstChild; for (var child = node.firstChild; child; child = child.nextSibling) { assertTreesEqual(child, copyChild); @@ -1054,8 +1060,7 @@ suite('TreeMirror Fuzzer', function () { node = document.createTextNode(text); else node = document.createComment(text); - } - else { + } else { node = document.createElement(randomTagname()); } return node; diff --git a/tests/test.ts b/tests/test.ts index 89ee95c..4becea9 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -8,7 +8,7 @@ declare var setup:(a:any)=>any; declare var teardown:(a:any)=>any; function compareNodeArrayIgnoreOrder(expected:Node[], actual:Node[]) { - assert.strictEqual(expected.length, actual.length); + assert.strictEqual(actual.length, expected.length); var map = new MutationSummary.NodeMap(); expected.forEach(function(node) { @@ -47,7 +47,7 @@ suite('Mutation Summary', function() { throw 'Mutation Delivered at end of microtask' }, queries: [query] - } + }; if (extraOptions) { Object.keys(extraOptions).forEach(function(key) { @@ -83,7 +83,7 @@ suite('Mutation Summary', function() { if (options.oldPreviousSibling) { expect.removed.forEach(function(node:Node, index:number) { - assert.strictEqual(expect.removedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.removedOldPreviousSibling[index]); }); } @@ -94,7 +94,7 @@ suite('Mutation Summary', function() { if (options.oldPreviousSibling) { expect.reparented.forEach(function(node:Node, index:number) { - assert.strictEqual(expect.reparentedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.reparentedOldPreviousSibling[index]); }); } } else { @@ -107,7 +107,7 @@ suite('Mutation Summary', function() { compareNodeArrayIgnoreOrder(expect.reordered, changed.reordered); expect.reordered.forEach(function(node:Node, index:number) { - assert.strictEqual(expect.reorderedOldPreviousSibling[index], changed.getOldPreviousSibling(node)); + assert.strictEqual(changed.getOldPreviousSibling(node), expect.reorderedOldPreviousSibling[index]); }); } else { assert.isUndefined(changed.reordered); @@ -120,7 +120,7 @@ suite('Mutation Summary', function() { var getOldFunction = query.attribute ? 'getOldAttribute' : 'getOldCharacterData'; expect.valueChanged.forEach(function(node:Node, index:number) { - assert.strictEqual(expect.oldValues[index], changed[getOldFunction](node, query.attribute)); + assert.strictEqual(changed[getOldFunction](node, query.attribute), expect.oldValues[index]); }); } else { assert.isUndefined(changed.valueChanged); @@ -129,12 +129,12 @@ suite('Mutation Summary', function() { // attributeChanged if (query.all || query.elementAttributes) { assert(typeof expect.attributeChanged == typeof changed.attributeChanged); - assert.strictEqual(Object.keys(expect.attributeChanged).length, Object.keys(changed.attributeChanged).length); + assert.strictEqual(Object.keys(changed.attributeChanged).length, Object.keys(expect.attributeChanged).length); Object.keys(expect.attributeChanged).forEach(function(attrName) { compareNodeArrayIgnoreOrder(expect.attributeChanged[attrName], changed.attributeChanged[attrName]); expect.attributeOldValue[attrName].forEach(function(attrOldValue:string, index:number) { - assert.strictEqual(expect.attributeOldValue[attrName][index], changed.getOldAttribute(expect.attributeChanged[attrName][index], attrName)); + assert.strictEqual(changed.getOldAttribute(expect.attributeChanged[attrName][index], attrName), expect.attributeOldValue[attrName][index]); }); }); } else { @@ -368,7 +368,8 @@ suite('Mutation Summary', function() { test('Element Attribute Specified', function() { startObserving({ - element: 'div[foo], A, *[bar], div[ baz = "bat"], span#foo[blow~=blarg]' + element: 'div[foo], A, *[bar], div[ baz = "bat"], span#foo[blow~=blarg], span[some^=thing], span[another$=.html], ' + + 'span[splat*=atat], div[lang|=en]' }); var div = document.createElement('div'); @@ -386,6 +387,18 @@ suite('Mutation Summary', function() { var p = document.createElement('P'); testDiv.appendChild(p); p.setAttribute('baz', 'baz'); + var span2 = document.createElement('span'); + span2.setAttribute('some', 'not a thing'); + div2.appendChild(span2); + var span3 = document.createElement('span'); + span3.setAttribute('another', '.html at the front'); + div3.appendChild(span3); + var span4 = document.createElement('span'); + span4.setAttribute('splat', 'no at at'); + p.appendChild(span4); + var div4 = document.createElement('div'); + testDiv.appendChild(div4); + div4.setAttribute('lang', 'not-en-matching'); assertSummary({ added: [div] }); @@ -396,13 +409,18 @@ suite('Mutation Summary', function() { div3.setAttribute('baz', 'bat'); span.id = 'foo'; span.setAttribute('blow', 'blarg bloog'); + span2.setAttribute('some', 'thinggood'); + span3.setAttribute('another', 'this one ends in.html'); + span4.setAttribute('splat', 'some-atat-thing'); + div4.setAttribute('lang', 'en-yes'); assertSummary({ - added: [p, div3, span], + added: [p, div3, span, span2, span3, span4, div4], removed: [div] }); div3.removeAttribute('baz'); div3.setAttribute('baz', 'bat'); + div4.setAttribute('lang', 'en'); assertNothingReported(); }); @@ -898,7 +916,7 @@ suite('Mutation Summary', function() { var divC = document.createElement('div'); testDiv.appendChild(divC); divC.id = 'c'; - var divD = document.createElement('div') + var divD = document.createElement('div'); testDiv.appendChild(divD); divD.id = 'd'; @@ -922,7 +940,7 @@ suite('Mutation Summary', function() { var divB = document.createElement('div'); testDiv.appendChild(divB); divB.id = 'b'; - var divC = document.createElement('div') + var divC = document.createElement('div'); testDiv.appendChild(divC); divC.id = 'c'; @@ -1014,14 +1032,14 @@ suite('Mutation Summary', function() { count++; if (count == 1) { - assert.strictEqual(1, summary.added.length) + assert.strictEqual(summary.added.length, 1); div = testDiv.appendChild(document.createElement('div')); } else if (count == 2) { - assert.strictEqual(2, summary.added.length); + assert.strictEqual(summary.added.length, 2); div = testDiv.appendChild(document.createElement('div')); summary1.disconnect(); } else if (count == 3) { - assert.strictEqual(1, summary.added.length); + assert.strictEqual(summary.added.length, 1); summary1.disconnect(); async(); } @@ -1036,14 +1054,14 @@ suite('Mutation Summary', function() { count++; if (count == 1) { - assert.strictEqual(1, summary.added.length) + assert.strictEqual(summary.added.length, 1); div = testDiv.appendChild(document.createElement('div')); } else if (count == 2) { - assert.strictEqual(2, summary.added.length); + assert.strictEqual(summary.added.length, 2); div = testDiv.appendChild(document.createElement('div')); summary2.disconnect(); } else if (count == 3) { - assert.strictEqual(1, summary.added.length); + assert.strictEqual(summary.added.length, 1); summary2.disconnect(); async(); } @@ -1070,7 +1088,7 @@ suite('Mutation Summary', function() { setTimeout(function() { div.setAttribute('bar', 'baz'); setTimeout(function() { - assert.strictEqual(1, callbackCount); + assert.strictEqual(callbackCount, 1); async(); }); }, 0); @@ -1100,7 +1118,7 @@ suite('TreeMirror Fuzzer', function() { var NON_DOC_ROOTS_MAX = 4; - var allNodes:Node[] = [] + var allNodes:Node[] = []; var nonRootNodes:Node[] = []; // Generate random document. @@ -1152,7 +1170,7 @@ suite('TreeMirror Fuzzer', function() { async(); } else doNextPass(); - }; + } doNextPass(); }); @@ -1163,22 +1181,22 @@ suite('TreeMirror Fuzzer', function() { assertTreesEqual(testDiv, copy); } - function assertTreesEqual(node:HTMLElement, copy:HTMLElement) { - assert.strictEqual(node.tagName, copy.tagName); - assert.strictEqual(node.id, copy.id); + function assertTreesEqual(node:Node, copy:Node) { + assert.strictEqual(copy.tagName, node.tagName); + assert.strictEqual(copy.id, node.id); - assert.strictEqual(node.nodeType, copy.nodeType); + assert.strictEqual(copy.nodeType, node.nodeType); if (node.nodeType == Node.ELEMENT_NODE) { - assert.strictEqual(node.attributes.length, copy.attributes.length); + assert.strictEqual(copy.attributes.length, node.attributes.length); for (var i = 0; i < node.attributes.length; i++) { var attr = node.attributes[i]; - assert.strictEqual(attr.value, (copy).getAttribute(attr.name)); + assert.strictEqual((copy).getAttribute(attr.name), attr.value); } } else { - assert.strictEqual(node.textContent, copy.textContent); + assert.strictEqual(copy.textContent, node.textContent); } - assert.strictEqual(node.childNodes.length, copy.childNodes.length); + assert.strictEqual(copy.childNodes.length, node.childNodes.length); var copyChild = copy.firstChild; for (var child = node.firstChild; child; child = child.nextSibling) { diff --git a/util/tree-mirror.ts b/util/tree-mirror.ts index ff75412..ba71b9a 100644 --- a/util/tree-mirror.ts +++ b/util/tree-mirror.ts @@ -278,7 +278,7 @@ class TreeMirrorClient { all.forEach((node) => { var parent = node.parentNode; - var children = parentMap.get(parent) + var children = parentMap.get(parent); if (!children) { children = new MutationSummary.NodeMap(); parentMap.set(parent, children); @@ -336,7 +336,7 @@ class TreeMirrorClient { } applyChanged(summaries:Summary[]) { - var summary:Summary = summaries[0] + var summary:Summary = summaries[0]; var removed:NodeData[] = summary.removed.map((node:Node) => { return this.serializeNode(node);