Skip to content
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

style(tests): refactor tests #1501

Merged
merged 4 commits into from
Mar 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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