| Index: test/browser/elemHideEmulation.js |
| =================================================================== |
| --- a/test/browser/elemHideEmulation.js |
| +++ b/test/browser/elemHideEmulation.js |
| @@ -12,25 +12,33 @@ |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| "use strict"; |
| -/* globals ElemHideEmulation */ |
| +/* globals ElemHideEmulation, splitSelector, |
| + parseSelectorContent, |
| + parseSelector, positionInParent, makeSelector, |
| + PlainSelector, HasSelector, PropsSelector */ |
| let myUrl = document.currentScript.src; |
| exports.tearDown = function(callback) |
| { |
| let styleElements = document.head.getElementsByTagName("style"); |
| while (styleElements.length) |
| styleElements[0].parentNode.removeChild(styleElements[0]); |
| + |
| + let child; |
| + while (child = document.body.firstChild) |
| + document.body.removeChild(child); |
|
Wladimir Palant
2017/06/08 13:12:42
If you store the child in an intermediate variable
hub
2017/06/08 15:45:48
Done.
|
| + |
| callback(); |
| }; |
| function unexpectedError(error) |
| { |
| console.error(error); |
| this.ok(false, "Unexpected error: " + error); |
| } |
| @@ -64,72 +72,328 @@ |
| else |
| { |
| styleElement = document.createElement("style"); |
| document.head.appendChild(styleElement); |
| } |
| styleElement.sheet.insertRule(rule, styleElement.sheet.cssRules.length); |
| } |
| -function createElementWithStyle(styleBlock) |
| +// insert a <div> with a unique id and and empty CSS rule |
|
Wladimir Palant
2017/06/08 13:12:43
You have "and" twice here. Also, how is this an em
hub
2017/06/08 15:45:50
Done.
|
| +// for the the selector matching the id. |
| +function createElementWithStyle(styleBlock, parent) |
| { |
| let element = document.createElement("div"); |
| element.id = findUniqueId(); |
| - document.body.appendChild(element); |
| + if (!parent) |
| + document.body.appendChild(element); |
| + else |
| + parent.appendChild(element); |
| insertStyleRule("#" + element.id + " " + styleBlock); |
| return element; |
| } |
| -function applyElemHideEmulation(selectors) |
| +// Will ensure the class ElemHideEmulation is loaded |
| +// and then will call the callback. |
|
Wladimir Palant
2017/06/08 13:12:41
There are no callbacks here, this function returns
hub
2017/06/08 15:45:50
I forgot to update the comment when we switched th
|
| +// NOTE: if it never loads, this will probably hang in an infinite |
| +// loop |
|
Wladimir Palant
2017/06/08 13:12:42
There is no loop here. But - yes, loadScript() mig
hub
2017/06/08 15:45:52
Done.
|
| +function loadElemHideEmulation() |
| { |
| if (typeof ElemHideEmulation == "undefined") |
| { |
| return loadScript(myUrl + "/../../../lib/common.js").then(() => |
| { |
| return loadScript(myUrl + "/../../../chrome/content/elemHideEmulation.js"); |
| }).then(() => |
| { |
| - return applyElemHideEmulation(selectors); |
| + return loadElemHideEmulation(); |
| }); |
| } |
| - let elemHideEmulation = new ElemHideEmulation( |
| - window, |
| - callback => |
| - { |
| - let patterns = []; |
| - selectors.forEach(selector => |
| - { |
| - patterns.push({selector}); |
| - }); |
| - callback(patterns); |
| - }, newSelectors => |
| - { |
| - if (!newSelectors.length) |
| - return; |
| - let selector = newSelectors.join(", "); |
| - insertStyleRule(selector + "{display: none !important;}"); |
| - } |
| - ); |
| - |
| - elemHideEmulation.apply(); |
| return Promise.resolve(); |
| } |
| +// instantiate a ElemHideEmulation with @selectors. |
|
Wladimir Palant
2017/06/08 13:12:43
"an ElemHideEmulation instance"?
hub
2017/06/08 15:45:48
Done.
|
| +function applyElemHideEmulation(selectors) |
| +{ |
| + return loadElemHideEmulation().then(() => |
| + { |
| + let elemHideEmulation = new ElemHideEmulation( |
| + window, |
| + callback => |
| + { |
| + let patterns = []; |
| + selectors.forEach(selector => |
| + { |
| + patterns.push({selector}); |
| + }); |
| + callback(patterns); |
| + }, |
| + newSelectors => |
| + { |
| + if (!newSelectors.length) |
| + return; |
| + let selector = newSelectors.join(", "); |
| + insertStyleRule(selector + "{display: none !important;}"); |
| + }, |
| + elems => |
| + { |
| + if (!elems.length) |
| + return; |
|
Wladimir Palant
2017/06/08 13:12:42
This check is pointless, we can just let the loop
hub
2017/06/08 15:45:49
Done.
|
| + for (let elem of elems) |
| + elem.style.display = "none"; |
| + } |
| + ); |
| + |
| + elemHideEmulation.apply(); |
| + return Promise.resolve(); |
|
Wladimir Palant
2017/06/08 13:12:43
While it was like that before your changes already
hub
2017/06/08 15:45:50
Done.
|
| + }); |
| +} |
| + |
| +exports.testParseSelectorContent = function(test) |
| +{ |
| + loadElemHideEmulation().then(() => |
| + { |
| + let parsed = parseSelectorContent(":-abp-has(> div) > div", 10); |
| + test.equal(parsed.text, "> div"); |
| + test.equal(parsed.end, 15); |
| + |
| + parsed = parseSelectorContent("\"> div\") > div", 0); |
| + test.equal(parsed.text, "\"> div\""); |
| + test.equal(parsed.end, 7); |
| + parsed = parseSelectorContent(" \"> div\" ) > div", 0); |
| + test.equal(parsed.text, " \"> div\" "); |
| + test.equal(parsed.end, 9); |
| + |
| + // parens not closed. |
| + parsed = parseSelectorContent("> div > div", 0); |
| + test.equal(parsed, null); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
|
Wladimir Palant
2017/06/08 13:12:40
I'm not really a friend of testing implementation
hub
2017/06/08 15:45:49
I actually think that *also* testing internal func
Wladimir Palant
2017/06/13 13:16:25
As you wish but I certainly won't object to anybod
|
| +}; |
| + |
| +exports.testParseSelector = function(test) |
| +{ |
| + loadElemHideEmulation().then(() => |
| + { |
| + let selectors = parseSelector(""); |
| + test.equal(selectors.length, 0); |
| + |
| + let selector = "div > :-abp-properties('background-color: rgb(0, 0, 0)')"; |
| + selectors = parseSelector(selector); |
| + test.equal(selectors.length, 2); |
| + test.ok(selectors[0] instanceof PlainSelector); |
| + test.ok(selectors[1] instanceof PropsSelector); |
| + |
| + selector = "div > :-abp-has(> div.inside) > div"; |
| + selectors = parseSelector(selector); |
| + test.equal(selectors.length, 3); |
| + test.ok(selectors[0] instanceof PlainSelector); |
| + test.ok(selectors[1] instanceof HasSelector); |
| + test.ok(selectors[2] instanceof PlainSelector); |
| + |
| + selector = "div > div:-abp-has(> div.inside) > div"; |
| + selectors = parseSelector(selector); |
| + |
| + test.equal(selectors.length, 3); |
| + test.ok(selectors[0] instanceof PlainSelector); |
| + test.ok(selectors[1] instanceof HasSelector); |
| + test.ok(selectors[2] instanceof PlainSelector); |
| + |
| + selector = "div > :-abp-has(> div.inside) > :-abp-properties(background-color: rgb(0, 0, 0))"; |
| + selectors = parseSelector(selector); |
| + |
| + test.equal(selectors.length, 4); |
| + test.ok(selectors[0] instanceof PlainSelector); |
| + test.ok(selectors[1] instanceof HasSelector); |
| + test.ok(selectors[2] instanceof PlainSelector); |
| + test.ok(selectors[3] instanceof PropsSelector); |
| + |
| + selector = "div > :-abp-has(> div.inside > :-abp-properties(background-color: rgb(0, 0, 0))"; |
| + selectors = parseSelector(selector); |
| + test.equal(selectors, null); |
| + |
| + // -abp-has-unsupported() is unknown. Ensure we fail parsing. |
| + selector = 'div[arial-label="Story"]:-abp-has(> div > div > span > span:-abp-unsupported("Suggested Post"))'; |
| + selectors = parseSelector(selector); |
| + test.equal(selectors, null); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +function buildDom(doc) |
| +{ |
| + doc.body.innerHTML = `<div id="parent"> |
| + <div id="middle"> |
| + <div id="middle1"><div id="inside" class="inside"></div></div> |
| + </div> |
| + <div id="sibling"> |
| + <div id="tohide">to hide</div> |
| + </div> |
| + <div id="sibling2"> |
| + <div id="sibling21"><div id="sibling211" class="inside"></div></div> |
| + </div> |
| + </div>`; |
| + let parent = document.getElementById("parent"); |
| + let middle = document.getElementById("middle"); |
| + let middle1 = document.getElementById("middle1"); |
| + let inside = document.getElementById("inside"); |
| + let sibling = document.getElementById("sibling"); |
| + let sibling2 = document.getElementById("sibling2"); |
| + let toHide = document.getElementById("tohide"); |
| + return {parent, middle, middle1, inside, sibling, sibling2, toHide}; |
| +} |
| + |
| +exports.testPositionInParent = function(test) |
| +{ |
| + let nodes = buildDom(document); |
| + |
| + loadElemHideEmulation().then(() => |
| + { |
| + test.equal(positionInParent(nodes.middle1), 1); |
| + test.equal(positionInParent(nodes.inside), 1); |
| + test.equal(positionInParent(nodes.sibling2), 3); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testMakeSelector = function(test) |
| +{ |
| + let nodes = buildDom(document); |
| + |
| + loadElemHideEmulation().then(() => |
| + { |
| + test.equal(makeSelector(nodes.middle, ""), |
| + ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1)"); |
| + test.equal(makeSelector(nodes.toHide, ""), |
| + ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(2) > DIV:nth-child(1)"); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testPlainSelector = function(test) |
| +{ |
| + buildDom(document); |
| + |
| + loadElemHideEmulation().then(() => |
| + { |
| + let selector = new PlainSelector("div > div"); |
| + |
| + let iter = selector.getSelectors("foo > "); |
| + let value = iter.next(); |
| + test.equal(value.value[0], "foo > div > div"); |
| + test.ok(iter.next().done); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testHasSelector = function(test) |
| +{ |
| + buildDom(document); |
| + |
| + loadElemHideEmulation().then(() => |
| + { |
| + let selector = new HasSelector("> div.inside"); |
| + |
| + let iter = selector.getSelectors("", document, document.sheet); |
| + let value = iter.next(); |
| + test.ok(!value.done); |
| + test.equal(value.value[0], |
| + ":root > BODY:nth-child(2) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1)"); |
| + |
| + iter = selector.getElements("", document, document.sheet); |
| + value = iter.next(); |
| + test.ok(!value.done); |
| + test.equal(value.value.id, "middle1"); |
| + value = iter.next(); |
| + test.ok(!value.done); |
| + test.equal(value.value.id, "sibling21"); |
| + value = iter.next(); |
| + test.ok(value.done); |
| + |
| + selector = new HasSelector(":-abp-has(> div.inside)"); |
| + |
| + test.ok(selector._innerSelectors); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testSplitStyleRule = function(test) |
| +{ |
| + loadElemHideEmulation().then(() => |
| + { |
| + let selectors = splitSelector("div:-abp-has(div) > [-abp-properties='background-color: rgb(0, 0, 0)'] > span"); |
| + test.ok(selectors); |
| + test.equal(selectors.length, 1, "There is only one selector"); |
| + |
| + selectors = splitSelector("div:-abp-has(div), [-abp-properties='background-color: rgb(0, 0, 0)']"); |
| + test.ok(selectors); |
| + test.equal(selectors.length, 2, "There are two selectors"); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| exports.testVerbatimPropertySelector = function(test) |
| { |
| let toHide = createElementWithStyle("{background-color: #000}"); |
| applyElemHideEmulation( |
| [":-abp-properties(background-color: rgb(0, 0, 0))"] |
| ).then(() => |
| { |
| expectHidden(test, toHide); |
| }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| }; |
| +exports.testVerbatimPropertySelectorWithPrefix = function(test) |
| +{ |
| + let parent = createElementWithStyle("{background-color: #000}"); |
| + let toHide = createElementWithStyle("{background-color: #000}", parent); |
| + applyElemHideEmulation( |
| + ["div > :-abp-properties(background-color: rgb(0, 0, 0))"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testVerbatimPropertySelectorWithPrefixNoMatch = function(test) |
| +{ |
| + let parent = createElementWithStyle("{background-color: #000}"); |
| + let toHide = createElementWithStyle("{background-color: #fff}", parent); |
| + applyElemHideEmulation( |
| + ["div > :-abp-properties(background-color: rgb(0, 0, 0))"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectVisible(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testVerbatimPropertySelectorWithSuffix = function(test) |
| +{ |
| + let parent = createElementWithStyle("{background-color: #000}"); |
| + let toHide = createElementWithStyle("{background-color: #000}", parent); |
| + applyElemHideEmulation( |
| + [":-abp-properties(background-color: rgb(0, 0, 0)) > div"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testVerbatimPropertyPseudoSelectorWithPrefixAndSuffix = function(test) |
| +{ |
| + let parent = createElementWithStyle("{background-color: #000}"); |
| + let middle = createElementWithStyle("{background-color: #000}", parent); |
| + let toHide = createElementWithStyle("{background-color: #000}", middle); |
| + applyElemHideEmulation( |
| + ["div > :-abp-properties(background-color: rgb(0, 0, 0)) > div"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectVisible(test, middle); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| exports.testPropertySelectorWithWildcard = function(test) |
| { |
| let toHide = createElementWithStyle("{background-color: #000}"); |
| applyElemHideEmulation( |
| [":-abp-properties(*color: rgb(0, 0, 0))"] |
| ).then(() => |
| { |
| expectHidden(test, toHide); |
| @@ -183,8 +447,143 @@ |
| window.setTimeout(() => |
| { |
| expectHidden(test, toHide); |
| resolve(); |
| }, 0); |
| }); |
| }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| }; |
| + |
| +exports.testPseudoClassHasSelector = function(test) |
| +{ |
| + let toHide = createElementWithStyle("{}"); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(div)"] |
| + ).then(() => |
| + { |
| + expectVisible(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithPrefix = function(test) |
| +{ |
| + let parent = createElementWithStyle("{}"); |
| + let child = createElementWithStyle("{}", parent); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(div)"] |
| + ).then(() => |
| + { |
| + expectHidden(test, parent); |
| + expectVisible(test, child); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithSuffix = function(test) |
| +{ |
| + let parent = createElementWithStyle("{}"); |
| + let middle = createElementWithStyle("{}", parent); |
| + let child = createElementWithStyle("{}", middle); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(div) > div"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectHidden(test, middle); |
| + expectHidden(test, child); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithSuffixSibling = function(test) |
| +{ |
| + let parent = createElementWithStyle("{}"); |
| + let middle = createElementWithStyle("{}", parent); |
| + let toHide = createElementWithStyle("{}"); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(div) + div"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectVisible(test, middle); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithSuffixSiblingChild = function(test) |
| +{ |
| + // <div> |
| + // <div></div> |
| + // <div> |
| + // <div>to hide</div> |
| + // </div> |
| + // </div> |
| + let parent = createElementWithStyle("{}"); |
| + let middle = createElementWithStyle("{}", parent); |
| + let sibling = createElementWithStyle("{}"); |
| + let toHide = createElementWithStyle("{}", sibling); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(div) + div > div"] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectVisible(test, middle); |
| + expectVisible(test, sibling); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |
| + |
| +function runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, selector) |
| +{ |
| + document.body.innerHTML = `<div id="parent"> |
| + <div id="middle"> |
| + <div id="middle1"><div id="inside" class="inside"></div></div> |
| + </div> |
| + <div id="sibling"> |
| + <div id="tohide">to hide</div> |
| + </div> |
| + <div id="sibling2"> |
| + <div id="sibling21"><div id="sibling211" class="inside"></div></div> |
| + </div> |
| + </div>`; |
| + let parent = document.getElementById("parent"); |
| + let middle = document.getElementById("middle"); |
| + let inside = document.getElementById("inside"); |
| + let sibling = document.getElementById("sibling"); |
| + let sibling2 = document.getElementById("sibling2"); |
| + let toHide = document.getElementById("tohide"); |
| + |
| + insertStyleRule(".inside {}"); |
| + |
| + applyElemHideEmulation( |
| + [selector] |
| + ).then(() => |
| + { |
| + expectVisible(test, parent); |
| + expectVisible(test, middle); |
| + expectVisible(test, inside); |
| + expectVisible(test, sibling); |
| + expectVisible(test, sibling2); |
| + expectHidden(test, toHide); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +} |
| + |
| +exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling = function(test) |
| +{ |
| + runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(:-abp-has(div.inside)) + div > div"); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithHasAndWithSuffixSibling2 = function(test) |
| +{ |
| + runTestPseudoClassHasSelectorWithHasAndWithSuffixSibling(test, "div:-abp-has(:-abp-has(> div.inside)) + div > div"); |
| +}; |
| + |
| +exports.testPseudoClassHasSelectorWithPropSelector = function(test) |
| +{ |
| + let parent = createElementWithStyle("{}"); |
| + let child = createElementWithStyle("{background-color: #000}", parent); |
| + applyElemHideEmulation( |
| + ["div:-abp-has(:-abp-properties(background-color: rgb(0, 0, 0)))"] |
| + ).then(() => |
| + { |
| + expectVisible(test, child); |
| + expectHidden(test, parent); |
| + }).catch(unexpectedError.bind(test)).then(() => test.done()); |
| +}; |