From 4663aaa61ee59fba9ce76cccb2d0bdf528374317 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Sun, 11 Sep 2016 21:47:35 -0500 Subject: [PATCH 1/2] Warn if selector contains a psuedo-class --- src/Utils.js | 30 +++++++++++++++++++---- test/ReactWrapper-spec.jsx | 4 +-- test/Utils-spec.jsx | 50 ++++++++++++++++++++------------------ 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index 8b31148d1..c011c9ad7 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -165,14 +165,31 @@ export function splitSelector(selector) { return selector.split(/(?=\.|\[.*\])|(?=#|\[#.*\])/); } -export function isSimpleSelector(selector) { - // any of these characters pretty much guarantee it's a complex selector - return !/[~\s:>]/.test(selector); + +const containsQuotes = /'|"/; +const containsColon = /:/; + + +export function isPsuedoClassSelector(selector) { + if (containsColon.test(selector)) { + if (!containsQuotes.test(selector)) { + return true; + } + const tokens = selector.split(containsQuotes); + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (containsColon.test(token) && i % 2 === 0) { + return true; + } + } + return false; + } + return false; } -export function selectorError(selector) { +export function selectorError(selector, type = '') { return new TypeError( - `Enzyme received a complex CSS selector ('${selector}') that it does not currently support` + `Enzyme received a ${type} CSS selector ('${selector}') that it does not currently support` ); } @@ -187,6 +204,9 @@ export const SELECTOR = { }; export function selectorType(selector) { + if (isPsuedoClassSelector(selector)) { + throw selectorError(selector, 'psudo-class'); + } if (selector[0] === '.') { return SELECTOR.CLASS_TYPE; } else if (selector[0] === '#') { diff --git a/test/ReactWrapper-spec.jsx b/test/ReactWrapper-spec.jsx index 36d3911be..2adb8465e 100644 --- a/test/ReactWrapper-spec.jsx +++ b/test/ReactWrapper-spec.jsx @@ -390,12 +390,12 @@ describeWithDOM('mount', () => { React.createElement('div', null, React.createElement('span', { '123-foo': 'bar', '-foo': 'bar', - ':foo': 'bar', + '+foo': 'bar', })) ); expect(wrapper.find('[-foo]')).to.have.length(0, '-foo'); - expect(wrapper.find('[:foo]')).to.have.length(0, ':foo'); + expect(wrapper.find('[+foo]')).to.have.length(0, '+foo'); expect(wrapper.find('[123-foo]')).to.have.length(0, '123-foo'); }); diff --git a/test/Utils-spec.jsx b/test/Utils-spec.jsx index 358eed824..ca6215a05 100644 --- a/test/Utils-spec.jsx +++ b/test/Utils-spec.jsx @@ -9,7 +9,7 @@ import { coercePropValue, getNode, nodeEqual, - isSimpleSelector, + isPsuedoClassSelector, propFromEvent, SELECTOR, selectorType, @@ -246,39 +246,43 @@ describe('Utils', () => { }); - describe('isSimpleSelector', () => { + describe('isPsuedoClassSelector', () => { + describe('prohibited selectors', () => { - function isComplex(selector) { + function isNotPsuedo(selector) { it(selector, () => { - expect(isSimpleSelector(selector)).to.equal(false); + expect(isPsuedoClassSelector(selector)).to.equal(false); }); } - - isComplex('.foo .bar'); - isComplex(':visible'); - isComplex('.foo>.bar'); - isComplex('.foo > .bar'); - isComplex('.foo~.bar'); - + isNotPsuedo('.foo'); + isNotPsuedo('div'); + isNotPsuedo('.foo .bar'); + isNotPsuedo('[hover]'); + isNotPsuedo('[checked=""]'); + isNotPsuedo('[checked=":checked"]'); + isNotPsuedo('[checked=\':checked\']'); + isNotPsuedo('.foo>.bar'); + isNotPsuedo('.foo > .bar'); + isNotPsuedo('.foo~.bar'); + isNotPsuedo('#foo'); }); describe('allowed selectors', () => { - function isSimple(selector) { + function isPsuedo(selector) { it(selector, () => { - expect(isSimpleSelector(selector)).to.equal(true); + expect(isPsuedoClassSelector(selector)).to.equal(true); }); } - - isSimple('.foo'); - isSimple('.foo-and-foo'); - isSimple('input[foo="bar"]'); - isSimple('input[foo="bar"][bar="baz"][baz="foo"]'); - isSimple('.FoOaNdFoO'); - isSimple('tag'); - isSimple('.foo.bar'); - isSimple('input.foo'); - + isPsuedo(':checked'); + isPsuedo(':focus'); + isPsuedo(':hover'); + isPsuedo(':disabled'); + isPsuedo(':any'); + isPsuedo(':last-child'); + isPsuedo(':nth-child(1)'); + isPsuedo('div:checked'); + isPsuedo('[data-foo=":hover"]:hover'); }); }); From 2d94e5388ec295d8bb28bb3976ffc2890061cbfd Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Sun, 11 Sep 2016 23:54:41 -0500 Subject: [PATCH 2/2] use .some() instead of for loop Also pseudo-spelling errors --- src/Utils.js | 16 ++++++-------- test/Utils-spec.jsx | 52 ++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index c011c9ad7..15bc375bb 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -170,19 +170,15 @@ const containsQuotes = /'|"/; const containsColon = /:/; -export function isPsuedoClassSelector(selector) { +export function isPseudoClassSelector(selector) { if (containsColon.test(selector)) { if (!containsQuotes.test(selector)) { return true; } const tokens = selector.split(containsQuotes); - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - if (containsColon.test(token) && i % 2 === 0) { - return true; - } - } - return false; + return tokens.some((token, i) => + containsColon.test(token) && i % 2 === 0 + ); } return false; } @@ -204,8 +200,8 @@ export const SELECTOR = { }; export function selectorType(selector) { - if (isPsuedoClassSelector(selector)) { - throw selectorError(selector, 'psudo-class'); + if (isPseudoClassSelector(selector)) { + throw selectorError(selector, 'pseudo-class'); } if (selector[0] === '.') { return SELECTOR.CLASS_TYPE; diff --git a/test/Utils-spec.jsx b/test/Utils-spec.jsx index ca6215a05..808ad355b 100644 --- a/test/Utils-spec.jsx +++ b/test/Utils-spec.jsx @@ -9,7 +9,7 @@ import { coercePropValue, getNode, nodeEqual, - isPsuedoClassSelector, + isPseudoClassSelector, propFromEvent, SELECTOR, selectorType, @@ -246,43 +246,43 @@ describe('Utils', () => { }); - describe('isPsuedoClassSelector', () => { + describe('isPseudoClassSelector', () => { describe('prohibited selectors', () => { - function isNotPsuedo(selector) { + function isNotPseudo(selector) { it(selector, () => { - expect(isPsuedoClassSelector(selector)).to.equal(false); + expect(isPseudoClassSelector(selector)).to.equal(false); }); } - isNotPsuedo('.foo'); - isNotPsuedo('div'); - isNotPsuedo('.foo .bar'); - isNotPsuedo('[hover]'); - isNotPsuedo('[checked=""]'); - isNotPsuedo('[checked=":checked"]'); - isNotPsuedo('[checked=\':checked\']'); - isNotPsuedo('.foo>.bar'); - isNotPsuedo('.foo > .bar'); - isNotPsuedo('.foo~.bar'); - isNotPsuedo('#foo'); + isNotPseudo('.foo'); + isNotPseudo('div'); + isNotPseudo('.foo .bar'); + isNotPseudo('[hover]'); + isNotPseudo('[checked=""]'); + isNotPseudo('[checked=":checked"]'); + isNotPseudo('[checked=\':checked\']'); + isNotPseudo('.foo>.bar'); + isNotPseudo('.foo > .bar'); + isNotPseudo('.foo~.bar'); + isNotPseudo('#foo'); }); describe('allowed selectors', () => { - function isPsuedo(selector) { + function isPseudo(selector) { it(selector, () => { - expect(isPsuedoClassSelector(selector)).to.equal(true); + expect(isPseudoClassSelector(selector)).to.equal(true); }); } - isPsuedo(':checked'); - isPsuedo(':focus'); - isPsuedo(':hover'); - isPsuedo(':disabled'); - isPsuedo(':any'); - isPsuedo(':last-child'); - isPsuedo(':nth-child(1)'); - isPsuedo('div:checked'); - isPsuedo('[data-foo=":hover"]:hover'); + isPseudo(':checked'); + isPseudo(':focus'); + isPseudo(':hover'); + isPseudo(':disabled'); + isPseudo(':any'); + isPseudo(':last-child'); + isPseudo(':nth-child(1)'); + isPseudo('div:checked'); + isPseudo('[data-foo=":hover"]:hover'); }); });