-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Property Selector #70
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
import React from 'react'; | ||
import { | ||
coercePropValue, | ||
propsOfNode, | ||
isSimpleSelector, | ||
splitSelector, | ||
selectorError, | ||
isCompoundSelector, | ||
selectorType, | ||
AND, | ||
SELECTOR, | ||
} from './Utils'; | ||
|
||
|
||
export function childrenOfNode(node) { | ||
if (!node) return []; | ||
const maybeArray = propsOfNode(node).children; | ||
|
@@ -66,6 +70,24 @@ export function nodeHasId(node, id) { | |
return propsOfNode(node).id === id; | ||
} | ||
|
||
|
||
export function nodeHasProperty(node, propKey, stringifiedPropValue) { | ||
const nodeProps = propsOfNode(node); | ||
const propValue = coercePropValue(stringifiedPropValue); | ||
const nodePropValue = nodeProps[propKey]; | ||
|
||
if (nodePropValue === undefined) { | ||
return false; | ||
} | ||
|
||
if (propValue) { | ||
return nodePropValue === propValue; | ||
} | ||
|
||
return nodeProps.hasOwnProperty(propKey); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and, should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't ran into a situation where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In fact, I just tested this with the new changes and it doesn't work in this situation. wrapper = <div foo={false} />
wrapper.find('[foo=false]') In that situation that turns into !!wrapper['foo'] // false which then says it doesn't have the prop, though it does. So hasOwnProperty seems to be the way to go. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is basically the |
||
} | ||
|
||
|
||
export function nodeHasType(node, type) { | ||
if (!type || !node) return false; | ||
if (!node.type) return false; | ||
|
@@ -86,21 +108,32 @@ export function buildPredicate(selector) { | |
if (isCompoundSelector.test(selector)) { | ||
return AND(splitSelector(selector).map(buildPredicate)); | ||
} | ||
if (selector[0] === '.') { | ||
// selector is a class name | ||
return node => hasClassName(node, selector.substr(1)); | ||
} else if (selector[0] === '#') { | ||
// selector is an id name | ||
return node => nodeHasId(node, selector.substr(1)); | ||
|
||
switch (selectorType(selector)) { | ||
case SELECTOR.CLASS_TYPE: | ||
return node => hasClassName(node, selector.substr(1)); | ||
|
||
case SELECTOR.ID_TYPE: | ||
return node => nodeHasId(node, selector.substr(1)); | ||
|
||
case SELECTOR.PROP_TYPE: | ||
const propKey = selector.split(/\[([a-zA-Z\-]*?)(=|\])/)[1]; | ||
const propValue = selector.split(/=(.*?)\]/)[1]; | ||
|
||
return node => nodeHasProperty(node, propKey, propValue); | ||
default: | ||
// selector is a string. match to DOM tag or constructor displayName | ||
return node => nodeHasType(node, selector); | ||
} | ||
// selector is a string. match to DOM tag or constructor displayName | ||
return node => nodeHasType(node, selector); | ||
break; | ||
|
||
|
||
default: | ||
throw new TypeError('Expecting a string or Component Constructor'); | ||
} | ||
} | ||
|
||
|
||
export function getTextFromNode(node) { | ||
if (node === null || node === undefined) { | ||
return ''; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,12 +102,12 @@ export function withSetStateAllowed(fn) { | |
} | ||
|
||
export function splitSelector(selector) { | ||
return selector.split(/(?=\.)/); | ||
return selector.split(/(?=\.|\[.*\])/); | ||
} | ||
|
||
export function isSimpleSelector(selector) { | ||
// any of these characters pretty much guarantee it's a complex selector | ||
return !/[~\s\[\]:>]/.test(selector); | ||
return !/[~\s:>]/.test(selector); | ||
} | ||
|
||
export function selectorError(selector) { | ||
|
@@ -116,8 +116,25 @@ export function selectorError(selector) { | |
); | ||
} | ||
|
||
export const isCompoundSelector = /[a-z]\.[a-z]/i; | ||
export const isCompoundSelector = /([a-z]\.[a-z]|[a-z]\[.*\])/i; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will this always work? what about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does, I tested just tested that exact scenario, but also this test effectively proves it also, https://github.com/airbnb/enzyme/pull/70/files#diff-e08e795cdfa46e03ea97df132b57d75aR167 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My testing shows that it doesnt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe test again? I literally just tested both those ideas and they both matched. |
||
|
||
const isPropSelector = /^\[.*\]$/; | ||
|
||
export const SELECTOR = { | ||
CLASS_TYPE: 0, | ||
ID_TYPE: 1, | ||
PROP_TYPE: 2, | ||
}; | ||
|
||
export function selectorType(selector) { | ||
if (selector[0] === '.') { | ||
return SELECTOR.CLASS_TYPE; | ||
} else if (selector[0] === '#') { | ||
return SELECTOR.ID_TYPE; | ||
} else if (isPropSelector.test(selector)) { | ||
return SELECTOR.PROP_TYPE; | ||
} | ||
} | ||
|
||
export function AND(fns) { | ||
return x => { | ||
|
@@ -128,3 +145,26 @@ export function AND(fns) { | |
return true; | ||
}; | ||
} | ||
|
||
export function coercePropValue(propValue) { | ||
// can be undefined | ||
if (propValue === undefined) { | ||
return propValue; | ||
} | ||
|
||
// if propValue includes quotes, it should be | ||
// treated as a string | ||
if (propValue.search(/"/) !== -1) { | ||
return propValue.replace(/"/g, ''); | ||
} | ||
|
||
const numericPropValue = parseInt(propValue, 10); | ||
|
||
// if parseInt is not NaN, then we've wanted a number | ||
if (!isNaN(numericPropValue)) { | ||
return numericPropValue; | ||
} | ||
|
||
// coerce to boolean | ||
return propValue === 'true' ? true : false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there should be a
propsOfNode()
function that abstracts this away... it's also important to check for falsy nodes as well where this would probably throw.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍