Skip to content

Commit

Permalink
style(tests): refactor tests (#1501)
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter authored and levithomason committed Mar 24, 2017
1 parent d0a54fb commit 7562368
Show file tree
Hide file tree
Showing 13 changed files with 1,143 additions and 1,086 deletions.
1,086 changes: 0 additions & 1,086 deletions test/specs/commonTests.js

This file was deleted.

50 changes: 50 additions & 0 deletions test/specs/commonTests/classNameHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import _ from 'lodash'
import React, { createElement } from 'react'

import { consoleUtil } from 'test/utils'

export const classNamePropValueBeforePropName = (Component, propKey, propValues, options = {}) => {
const { className = propKey, requiredProps = {} } = options

propValues.forEach(propVal => {
it(`adds "${propVal} ${className}" to className`, () => {
shallow(createElement(Component, { ...requiredProps, [propKey]: propVal }))
.should.have.className(`${propVal} ${className}`)
})
})
}

export const noClassNameFromBoolProps = (Component, propKey, propValues, options = {}) => {
const { className = propKey, requiredProps = {} } = options

_.each([true, false], bool => it(`does not add any className when ${bool}`, () => {
consoleUtil.disableOnce()

const wrapper = shallow(createElement(Component, { ...requiredProps, [propKey]: bool }))

wrapper.should.not.have.className(className)
wrapper.should.not.have.className('true')
wrapper.should.not.have.className('false')

propValues.forEach(propVal => wrapper.should.not.have.className(propVal.toString()))
}))
}

export const noDefaultClassNameFromProp = (Component, propKey, propValues, options = {}) => {
const { defaultProps = {} } = Component
const { className = propKey, requiredProps = {} } = options
// required props may include a prop that creates a className
// if so, we cannot assert that it doesn't exist by default because it is required to exist
// skip assertions for required props
if (propKey in defaultProps) return
if (propKey in requiredProps) return

it('is not included in className when not defined', () => {
const wrapper = shallow(<Component {...requiredProps} />)
wrapper.should.not.have.className(className)

// ensure that none of the prop option values are in className
// SUI classes ought to be built up using a declarative component API
propValues.forEach(propValue => wrapper.should.not.have.className(propValue.toString()))
})
}
14 changes: 14 additions & 0 deletions test/specs/commonTests/commonHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default (testName, Component) => {
const throwError = msg => {
throw new Error(`${testName}: ${msg} \n Component: ${Component && Component.name}`)
}

const assertRequired = (required, description) => {
return required || throwError(`Required ${description}, got: ${required} (${typeof required})`)
}

return {
assertRequired,
throwError,
}
}
63 changes: 63 additions & 0 deletions test/specs/commonTests/componentInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import _ from 'lodash'
import path from 'path'
import React from 'react'
import ReactDOMServer from 'react-dom/server'

import { META } from 'src/lib'
import helpers from './commonHelpers'

const componentCtx = require.context(
'../../../src/',
true,
/(addons|collections|elements|modules|views).\w+.(?!index)\w+.js/
)

const componentInfo = componentCtx.keys().map(key => {
const Component = componentCtx(key).default
const componentType = typeof Component
const { throwError } = helpers('componentInfo', Component)

if (componentType !== 'function') {
throwError([
`${key} is not properly exported.`,
`Components should export a class or function, got: ${componentType}.`,
].join(' '))
}

const { _meta, prototype } = Component

if (!_meta) {
throwError([
'Component is missing a static _meta object property. This should help identify it:',
`Rendered:\n${ReactDOMServer.renderToStaticMarkup(<Component />)}`,
].join('\n'))
}

const constructorName = prototype.constructor.name
const filePath = key
const filename = path.basename(key)
const filenameWithoutExt = path.basename(key, '.js')
const subComponentName = _.has(_meta, 'parent') && _.has(_meta, 'name')
? _meta.name.replace(_meta.parent, '')
: null

// name of the component, sub component, or plural parent for sub component groups
const componentClassName = (
META.isChild(Component)
? subComponentName.replace(/Group$/, `${_meta.parent}s`)
: _meta.name
).toLowerCase()

return {
_meta,
Component,
constructorName,
componentClassName,
subComponentName,
filePath,
filename,
filenameWithoutExt,
}
})

export default componentInfo
16 changes: 16 additions & 0 deletions test/specs/commonTests/hasSubComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import _ from 'lodash'

/**
* Assert a component exposes other components as (static properties).
* @param {React.Component|Function} Component The Component.
* @param {React.Component[]} subComponents Array of components that should exist on Component.
*/
export default (Component, subComponents) => {
const staticValues = _.values(Component)

_.each(subComponents, subComponent => {
it(`has sub component ${subComponent._meta.name}`, () => {
staticValues.should.contain(subComponent)
})
})
}
20 changes: 20 additions & 0 deletions test/specs/commonTests/hasUIClassName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'
import helpers from './commonHelpers'

/**
* Assert a component adds the Semantic UI "ui" className.
* @param {React.Component|Function} Component The Component.
* @param {Object} [options={}]
* @param {Object} [options.requiredProps={}] Props required to render the component.
*/
export default (Component, options = {}) => {
const { requiredProps = {} } = options
const { assertRequired } = helpers('hasUIClassName', Component)

it('has the "ui" className', () => {
assertRequired(Component, 'a `Component`')

shallow(<Component {...requiredProps} />)
.should.have.className('ui')
})
}
146 changes: 146 additions & 0 deletions test/specs/commonTests/implementsClassNameProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { createElement } from 'react'
import _ from 'lodash'

import { consoleUtil } from 'test/utils'
import {
classNamePropValueBeforePropName,
noClassNameFromBoolProps,
noDefaultClassNameFromProp,
} from './classNameHelpers'
import helpers from './commonHelpers'

/**
* Assert that a Component prop's name and value are required to create a className.
* @param {React.Component|Function} Component The component to test.
* @param {String} propKey A props key.
* @param {array} propValues Array of possible values of prop.
* @param {Object} [options={}]
* @param {Object} [options.requiredProps={}] Props required to render the component.
* @param {Object} [options.className=propKey] The className to assert exists.
*/
export const propKeyAndValueToClassName = (Component, propKey, propValues, options = {}) => {
const { assertRequired } = helpers('propKeyAndValueToClassName', Component)

describe(`${propKey} (common)`, () => {
assertRequired(Component, 'a `Component`')
assertRequired(propKey, 'a `propKey`')

classNamePropValueBeforePropName(Component, propKey, propValues, options)
noDefaultClassNameFromProp(Component, propKey, propValues, options)
noClassNameFromBoolProps(Component, propKey, propValues, options)
})
}

/**
* Assert that only a Component prop's name is converted to className.
* @param {React.Component|Function} Component The component to test.
* @param {String} propKey A props key.
* @param {Object} [options={}]
* @param {Object} [options.requiredProps={}] Props required to render the component.
* @param {Object} [options.className=propKey] The className to assert exists.
*/
export const propKeyOnlyToClassName = (Component, propKey, options = {}) => {
const { className = propKey, requiredProps = {} } = options
const { assertRequired } = helpers('propKeyOnlyToClassName', Component)

describe(`${propKey} (common)`, () => {
assertRequired(Component, 'a `Component`')
assertRequired(propKey, 'a `propKey`')

noDefaultClassNameFromProp(Component, propKey, [], options)

it('adds prop name to className', () => {
shallow(createElement(Component, { ...requiredProps, [propKey]: true }))
.should.have.className(className)
})

it('does not add prop value to className', () => {
consoleUtil.disableOnce()

const value = 'foo-bar-baz'
shallow(createElement(Component, { ...requiredProps, [propKey]: value }))
.should.not.have.className(value)
})
})
}

/**
* Assert that a Component prop name or value convert to a className.
* @param {React.Component|Function} Component The component to test.
* @param {String} propKey A props key.
* @param {array} propValues Array of possible values of prop.
* @param {Object} [options={}]
* @param {Object} [options.requiredProps={}] Props required to render the component.
* @param {Object} [options.className=propKey] The className to assert exists.
*/
export const propKeyOrValueAndKeyToClassName = (Component, propKey, propValues, options = {}) => {
const { className = propKey, requiredProps = {} } = options
const { assertRequired } = helpers('propKeyOrValueAndKeyToClassName', Component)

describe(`${propKey} (common)`, () => {
assertRequired(Component, 'a `Component`')
assertRequired(propKey, 'a `propKey`')

noDefaultClassNameFromProp(Component, propKey, propValues, options)
classNamePropValueBeforePropName(Component, propKey, propValues, options)

beforeEach(() => {
consoleUtil.disableOnce()
})

it('adds only the name to className when true', () => {
shallow(createElement(Component, { ...requiredProps, [propKey]: true }))
.should.have.className(className)
})

it('adds no className when false', () => {
const wrapper = shallow(createElement(Component, { ...requiredProps, [propKey]: false }))

wrapper.should.not.have.className(className)
wrapper.should.not.have.className('true')
wrapper.should.not.have.className('false')

_.each(propValues, propVal => {
wrapper.should.not.have.className(propVal)
})
})
})
}

/**
* Assert that only a Component prop's value is converted to className.
* @param {React.Component|Function} Component The component to test.
* @param {String} propKey A props key.
* @param {array} propValues Array of possible props values.
* @param {Object} [options={}]
* @param {Object} [options.requiredProps={}] Props required to render the component.
* @param {Object} [options.className=propKey] The className to assert exists.
*/
export const propValueOnlyToClassName = (Component, propKey, propValues, options = {}) => {
const { requiredProps = {} } = options
const { assertRequired } = helpers('propValueOnlyToClassName', Component)

describe(`${propKey} (common)`, () => {
assertRequired(Component, 'a `Component`')
assertRequired(propKey, 'a `propKey`')

noClassNameFromBoolProps(Component, propKey, propValues, options)
noDefaultClassNameFromProp(Component, propKey, propValues, options)

it('adds prop value to className', () => {
propValues.forEach(propValue => {
shallow(createElement(Component, { ...requiredProps, [propKey]: propValue }))
.should.have.className(propValue)
})
})

it('does not add prop name to className', () => {
consoleUtil.disableOnce()

propValues.forEach(propValue => {
shallow(createElement(Component, { ...requiredProps, [propKey]: propValue }))
.should.not.have.className(propKey)
})
})
})
}
Loading

0 comments on commit 7562368

Please sign in to comment.