This repository has been archived by the owner on Mar 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(test): add shorthand property conformance tests (#112)
* add conformance tests for shorthand properties * add shorthand tests for Button * reset default icon position (needs to be addressed later) * changelog * remove unnecessary functionality * remove unnecessary functionality * reuse Icon's shorthand functionality * renaming * introduce checks in a separate module
- Loading branch information
Showing
6 changed files
with
169 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import * as _ from 'lodash' | ||
import { shallow } from 'enzyme' | ||
import * as React from 'react' | ||
|
||
import { createShorthand } from 'src/lib' | ||
import { consoleUtil } from 'test/utils' | ||
import { noDefaultClassNameFromProp } from './classNameHelpers' | ||
import helpers from './commonHelpers' | ||
|
||
const shorthandComponentName = ShorthandComponent => { | ||
if (typeof ShorthandComponent === 'string') return ShorthandComponent | ||
return ( | ||
_.get(ShorthandComponent, '_meta.name') || | ||
ShorthandComponent.displayName || | ||
ShorthandComponent.name | ||
) | ||
} | ||
|
||
/** | ||
* Assert that a Component correctly implements a shorthand prop. | ||
* | ||
* @param {function} Component The component to test. | ||
* @param {object} options | ||
* @param {string} options.propKey The name of the shorthand prop. | ||
* @param {string|function} options.ShorthandComponent The component that should be rendered from the shorthand value. | ||
* @param {boolean} [options.alwaysPresent] Whether or not the shorthand exists by default. | ||
* @param {boolean} [options.assertExactMatch] Selects an assertion method, `contain` will be used if true. | ||
* @param {boolean} [options.generateKey=false] Whether or not automatic key generation is allowed for the shorthand component. | ||
* @param {function} options.mapValueToProps A function that maps a primitive value to the Component props. | ||
* @param {Object} [options.requiredProps={}] Props required to render the component. | ||
* @param {Object} [options.shorthandDefaultProps] Default props for the shorthand component. | ||
* @param {Object} [options.shorthandOverrideProps] Override props for the shorthand component. | ||
*/ | ||
export default (Component, options: any = {}) => { | ||
const { | ||
alwaysPresent, | ||
assertExactMatch = true, | ||
generateKey = false, | ||
mapValueToProps, | ||
propKey, | ||
ShorthandComponent, | ||
shorthandDefaultProps = {}, | ||
shorthandOverrideProps = {}, | ||
requiredProps = {}, | ||
} = options | ||
const { assertRequired } = helpers('implementsShorthandProp', Component) | ||
const assertMethod = assertExactMatch ? 'contain' : 'containMatchingElement' | ||
|
||
describe(`${propKey} shorthand prop (common)`, () => { | ||
assertRequired(Component, 'a `Component`') | ||
assertRequired(_.isPlainObject(options), 'an `options` object') | ||
assertRequired(propKey, 'a `propKey`') | ||
assertRequired(ShorthandComponent, 'a `ShorthandComponent`') | ||
|
||
const name = shorthandComponentName(ShorthandComponent) | ||
const assertValidShorthand = value => { | ||
const expectedShorthandElement = createShorthand(ShorthandComponent, mapValueToProps, value, { | ||
defaultProps: shorthandDefaultProps, | ||
overrideProps: shorthandOverrideProps, | ||
generateKey, | ||
}) | ||
const element = React.createElement(Component, { ...requiredProps, [propKey]: value }) | ||
const wrapper = shallow(element) | ||
|
||
wrapper.should[assertMethod](expectedShorthandElement) | ||
|
||
// Enzyme's .key() method is not consistent with React for elements with | ||
// no key (`undefined` vs `null`), so use the underlying element instead | ||
// Will fail if more than one element of this type is found | ||
const shorthandElement = wrapper.find(ShorthandComponent).getElement() | ||
expect(shorthandElement.key).to.equal(expectedShorthandElement.key, "key doesn't match") | ||
} | ||
|
||
if (alwaysPresent || (Component.defaultProps && Component.defaultProps[propKey])) { | ||
it(`has default ${name} when not defined`, () => { | ||
shallow(<Component {...requiredProps} />).should.have.descendants(name) | ||
}) | ||
} else { | ||
noDefaultClassNameFromProp(Component, propKey, [], options) | ||
|
||
it(`has no ${name} when not defined`, () => { | ||
shallow(<Component {...requiredProps} />).should.not.have.descendants(name) | ||
}) | ||
} | ||
|
||
if (!alwaysPresent) { | ||
it(`has no ${name} when null`, () => { | ||
shallow( | ||
React.createElement(Component, { ...requiredProps, [propKey]: null }), | ||
).should.not.have.descendants(ShorthandComponent) | ||
}) | ||
} | ||
|
||
it(`renders a ${name} from strings`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand('string') | ||
}) | ||
|
||
it(`renders a ${name} from numbers`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(123) | ||
}) | ||
|
||
// the Input maps shorthand to `type` | ||
// React uses the default prop ('text') in place of type={0} | ||
if (propKey !== 'input') { | ||
it(`renders a ${name} from number 0`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(0) | ||
}) | ||
} | ||
|
||
it(`renders a ${name} from a props object`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(mapValueToProps('foo')) | ||
}) | ||
|
||
it(`renders a ${name} from elements`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(<ShorthandComponent />) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,123 +1,53 @@ | ||
import * as _ from 'lodash' | ||
import { shallow } from 'enzyme' | ||
import * as React from 'react' | ||
import { mount } from './isConformant' | ||
|
||
import { createShorthand } from 'src/lib' | ||
import { consoleUtil } from 'test/utils' | ||
import { noDefaultClassNameFromProp } from './classNameHelpers' | ||
import helpers from './commonHelpers' | ||
|
||
const shorthandComponentName = ShorthandComponent => { | ||
if (typeof ShorthandComponent === 'string') return ShorthandComponent | ||
return ( | ||
_.get(ShorthandComponent, '_meta.name') || | ||
ShorthandComponent.displayName || | ||
ShorthandComponent.name | ||
) | ||
type ShorthandTestOptions = { | ||
mapsValueToProp?: string | ||
} | ||
|
||
/** | ||
* Assert that a Component correctly implements a shorthand prop. | ||
* | ||
* @param {function} Component The component to test. | ||
* @param {object} options | ||
* @param {string} options.propKey The name of the shorthand prop. | ||
* @param {string|function} options.ShorthandComponent The component that should be rendered from the shorthand value. | ||
* @param {boolean} [options.alwaysPresent] Whether or not the shorthand exists by default. | ||
* @param {boolean} [options.assertExactMatch] Selects an assertion method, `contain` will be used if true. | ||
* @param {boolean} [options.generateKey=false] Whether or not automatic key generation is allowed for the shorthand component. | ||
* @param {function} options.mapValueToProps A function that maps a primitive value to the Component props. | ||
* @param {Object} [options.requiredProps={}] Props required to render the component. | ||
* @param {Object} [options.shorthandDefaultProps] Default props for the shorthand component. | ||
* @param {Object} [options.shorthandOverrideProps] Override props for the shorthand component. | ||
*/ | ||
export default (Component, options: any = {}) => { | ||
const { | ||
alwaysPresent, | ||
assertExactMatch = true, | ||
generateKey = false, | ||
mapValueToProps, | ||
propKey, | ||
ShorthandComponent, | ||
shorthandDefaultProps = {}, | ||
shorthandOverrideProps = {}, | ||
requiredProps = {}, | ||
} = options | ||
const { assertRequired } = helpers('implementsShorthandProp', Component) | ||
const assertMethod = assertExactMatch ? 'contain' : 'containMatchingElement' | ||
|
||
describe(`${propKey} shorthand prop (common)`, () => { | ||
assertRequired(Component, 'a `Component`') | ||
assertRequired(_.isPlainObject(options), 'an `options` object') | ||
assertRequired(propKey, 'a `propKey`') | ||
assertRequired(ShorthandComponent, 'a `ShorthandComponent`') | ||
const DefaultShorthandTestOptions: ShorthandTestOptions = { | ||
mapsValueToProp: 'content', | ||
} | ||
|
||
const name = shorthandComponentName(ShorthandComponent) | ||
const assertValidShorthand = value => { | ||
const expectedShorthandElement = createShorthand(ShorthandComponent, mapValueToProps, value, { | ||
defaultProps: shorthandDefaultProps, | ||
overrideProps: shorthandOverrideProps, | ||
generateKey, | ||
export default Component => { | ||
return function implementsShorthandProp( | ||
shorthandPropertyName: string, | ||
ShorthandComponent: React.ComponentType, | ||
options: ShorthandTestOptions = DefaultShorthandTestOptions, | ||
) { | ||
const { mapsValueToProp } = options | ||
|
||
describe(`shorthand property for '${ShorthandComponent.displayName}'`, () => { | ||
test(`is defined`, () => { | ||
expect(Component.propTypes[shorthandPropertyName]).toBeTruthy() | ||
}) | ||
const element = React.createElement(Component, { ...requiredProps, [propKey]: value }) | ||
const wrapper = shallow(element) | ||
|
||
wrapper.should[assertMethod](expectedShorthandElement) | ||
test(`string value is handled as ${ | ||
ShorthandComponent.displayName | ||
}'s ${mapsValueToProp}`, () => { | ||
const props = { [shorthandPropertyName]: 'some value' } | ||
const wrapper = mount(<Component {...props} />) | ||
|
||
// Enzyme's .key() method is not consistent with React for elements with | ||
// no key (`undefined` vs `null`), so use the underlying element instead | ||
// Will fail if more than one element of this type is found | ||
const shorthandElement = wrapper.find(ShorthandComponent).getElement() | ||
expect(shorthandElement.key).to.equal(expectedShorthandElement.key, "key doesn't match") | ||
} | ||
|
||
if (alwaysPresent || (Component.defaultProps && Component.defaultProps[propKey])) { | ||
it(`has default ${name} when not defined`, () => { | ||
shallow(<Component {...requiredProps} />).should.have.descendants(name) | ||
const shorthandComponentProps = wrapper.find(ShorthandComponent.displayName).props() | ||
expect(shorthandComponentProps[mapsValueToProp]).toEqual('some value') | ||
}) | ||
} else { | ||
noDefaultClassNameFromProp(Component, propKey, [], options) | ||
|
||
it(`has no ${name} when not defined`, () => { | ||
shallow(<Component {...requiredProps} />).should.not.have.descendants(name) | ||
}) | ||
} | ||
test(`object value is spread as ${ShorthandComponent.displayName}'s props`, () => { | ||
const ShorthandValue = { foo: 'foo value', bar: 'bar value' } | ||
|
||
if (!alwaysPresent) { | ||
it(`has no ${name} when null`, () => { | ||
shallow( | ||
React.createElement(Component, { ...requiredProps, [propKey]: null }), | ||
).should.not.have.descendants(ShorthandComponent) | ||
}) | ||
} | ||
const props = { [shorthandPropertyName]: ShorthandValue } | ||
const wrapper = mount(<Component {...props} />) | ||
|
||
it(`renders a ${name} from strings`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand('string') | ||
}) | ||
const shorthandComponentProps = wrapper.find(ShorthandComponent.displayName).props() | ||
|
||
it(`renders a ${name} from numbers`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(123) | ||
}) | ||
const allShorthandPropertiesArePassedToShorthandComponent = Object.keys( | ||
ShorthandValue, | ||
).every( | ||
propertyName => ShorthandValue[propertyName] === shorthandComponentProps[propertyName], | ||
) | ||
|
||
// the Input maps shorthand to `type` | ||
// React uses the default prop ('text') in place of type={0} | ||
if (propKey !== 'input') { | ||
it(`renders a ${name} from number 0`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(0) | ||
expect(allShorthandPropertiesArePassedToShorthandComponent).toBe(true) | ||
}) | ||
} | ||
|
||
it(`renders a ${name} from a props object`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(mapValueToProps('foo')) | ||
}) | ||
|
||
it(`renders a ${name} from elements`, () => { | ||
consoleUtil.disableOnce() | ||
assertValidShorthand(<ShorthandComponent />) | ||
}) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters