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

test(typings): additional tests for typings and fixes #1624

Merged
merged 3 commits into from
May 6, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions src/addons/Confirm/Confirm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as React from 'react';
import { ModalProps } from '../../modules/Modal';

export interface ConfirmProps extends ModalProps {
[key: string]: any;

/** The cancel button text. */
cancelButton?: any;

Expand Down
6 changes: 3 additions & 3 deletions src/addons/Portal/Portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ class Portal extends Component {
/** The node where the portal should mount. */
mountNode: PropTypes.any,

/** Milliseconds to wait before closing on mouse leave */
mouseLeaveDelay: PropTypes.number,

/** Milliseconds to wait before opening on mouse over */
mouseEnterDelay: PropTypes.number,

/** Milliseconds to wait before closing on mouse leave */
mouseLeaveDelay: PropTypes.number,

/**
* Called when a close event happens
*
Expand Down
1 change: 1 addition & 0 deletions src/addons/Select/Select.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { default as DropdownItem } from '../../modules/Dropdown/DropdownItem';
import { default as DropdownMenu } from '../../modules/Dropdown/DropdownMenu';

export interface SelectProps extends DropdownProps {
[key: string]: any;
}

interface SelectComponent extends React.StatelessComponent<SelectProps> {
Expand Down
3 changes: 3 additions & 0 deletions src/collections/Form/FormCheckbox.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export interface FormCheckboxProps extends FormFieldProps, CheckboxProps {

/** A FormField control prop. */
control?: any;

/** HTML input type, either checkbox or radio. */
type?: 'checkbox' | 'radio';
}

declare const FormCheckbox: React.StatelessComponent<FormCheckboxProps>;
Expand Down
3 changes: 3 additions & 0 deletions src/collections/Form/FormRadio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export interface FormRadioProps extends FormFieldProps, RadioProps {

/** A FormField control prop. */
control?: any;

/** HTML input type, either checkbox or radio. */
type?: 'checkbox' | 'radio';
}

declare const FormRadio: React.StatelessComponent<FormRadioProps>;
Expand Down
1 change: 1 addition & 0 deletions src/collections/Table/TableFooter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { TableHeaderProps } from './TableHeader';

export interface TableFooterProps extends TableHeaderProps {
[key: string]: any;
}

declare const TableFooter: React.StatelessComponent<TableFooterProps>;
Expand Down
2 changes: 2 additions & 0 deletions src/elements/List/ListIcon.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { SemanticVERTICALALIGNMENTS } from '../..';
import { IconProps } from '../Icon';

export interface ListIconProps extends IconProps {
[key: string]: any;

/** Additional classes. */
className?: string;

Expand Down
35 changes: 35 additions & 0 deletions src/modules/Popup/Popup.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ export interface PopupProps extends PortalProps {
/** Event triggering the popup. */
on?: 'hover' | 'click' | 'focus';

/**
* Called when a close event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onClose?: (event: React.MouseEvent<HTMLElement>, data: PopupProps) => void;

/**
* Called when the portal is mounted on the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onMount?: (nothing: null, data: PopupProps) => void;

/**
* Called when an open event happens.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onOpen?: (event: React.MouseEvent<HTMLElement>, data: PopupProps) => void;

/**
* Called when the portal is unmounted from the DOM.
*
* @param {null}
* @param {object} data - All props.
*/
onUnmount?: (nothing: null, data: PopupProps) => void;

/** Position for the popover. */
position?: 'top left' | 'top right' |
'bottom right' | 'bottom left' |
Expand All @@ -52,6 +84,9 @@ export interface PopupProps extends PortalProps {
/** Custom Popup style. */
style?: Object;

/** Element to be rendered in-place where the popup is defined. */
trigger?: React.ReactNode;

/** Popup width. */
wide?: boolean | 'very';
}
Expand Down
2 changes: 1 addition & 1 deletion src/views/Statistic/StatisticGroup.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';

import { SemanticCOLORS, SemanticWIDTHS } from '../..';
import { StatisticSizeProp } from './Statictic';
import { StatisticSizeProp } from './Statistic';

export interface StatisticGroupProps {
[key: string]: any;
Expand Down
3 changes: 3 additions & 0 deletions test/specs/addons/Portal/Portal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import { unmountComponentAtNode } from 'react-dom'

import * as common from 'test/specs/commonTests'
import { domEvent, sandbox } from 'test/utils'
import Portal from 'src/addons/Portal/Portal'

Expand All @@ -25,6 +26,8 @@ describe('Portal', () => {
if (attachTo) document.body.removeChild(attachTo)
})

common.hasValidTypings(Portal)

it('propTypes.children should be required', () => {
Portal.propTypes.children.should.equal(PropTypes.node.isRequired)
})
Expand Down
4 changes: 3 additions & 1 deletion test/specs/collections/Form/FormFieldCheckbox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import Checkbox from 'src/modules/Checkbox/Checkbox'
import * as common from 'test/specs/commonTests'

describe('FormCheckbox', () => {
common.isConformant(FormCheckbox)
common.isConformant(FormCheckbox, {
ignoredProps: ['type'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: given that the conformance test covers many things, can we rename this option to specify that it only ignores the prop in typings?

Perhaps ignoreTypingsProps or similar?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a good suggestion. I will fix it in evening

})

it('renders a FormField with a Checkbox control', () => {
shallow(<FormCheckbox />)
Expand Down
4 changes: 3 additions & 1 deletion test/specs/collections/Form/FormFieldRadio-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import FormRadio from 'src/collections/Form/FormRadio'
import * as common from 'test/specs/commonTests'

describe('FormRadio', () => {
common.isConformant(FormRadio)
common.isConformant(FormRadio, {
ignoredProps: ['type'],
})

it('renders a FormField with a Radio control', () => {
shallow(<FormRadio />)
Expand Down
15 changes: 11 additions & 4 deletions test/specs/commonTests/hasValidTypings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import componentInfo from './componentInfo'
import {
getNodes,
getInterfaces,
hasAnySignature,
requireTs,
} from './tsHelpers'

Expand All @@ -14,6 +15,7 @@ import {
* @param {Object} [extractedInfo={}]
* @param {Object} [extractedInfo._meta={}] The meta information about Component
* @param {Object} [options={}]
* @param {array} [options.ignoredProps=[]] Props that will be ignored in tests.
* @param {Object} [options.requiredProps={}] Props required to render Component without errors or warnings.
*/
export default (Component, extractedInfo, options = {}) => {
Expand All @@ -22,7 +24,7 @@ export default (Component, extractedInfo, options = {}) => {
filenameWithoutExt,
filePath,
} = extractedInfo || _.find(componentInfo, i => i.constructorName === Component.prototype.constructor.name)
const { requiredProps } = options
const { ignoredProps = [], requiredProps } = options

const tsFile = filenameWithoutExt + '.d.ts'
const tsContent = requireTs(path.join(path.dirname(filePath), tsFile))
Expand Down Expand Up @@ -50,18 +52,23 @@ export default (Component, extractedInfo, options = {}) => {
})

describe('props', () => {
const { props: interfaceProps } = interfaceObject
const { props } = interfaceObject

it('has any signature', () => {
hasAnySignature(tsNodes).should.to.equal(true)
})

it('are correctly defined', () => {
const componentPropTypes = _.get(Component, 'propTypes')
const componentProps = _.keys(componentPropTypes)
const interfaceProps = _.without(_.map(props, 'name'), ...ignoredProps)

componentProps.should.to.deep.equal(_.map(interfaceProps, 'name'))
componentProps.should.to.deep.equal(interfaceProps)
})

it('only necessary are required', () => {
const componentRequired = _.keys(requiredProps)
const interfaceRequired = _.filter(interfaceProps, ['required', true])
const interfaceRequired = _.filter(props, ['required', true])

componentRequired.should.to.deep.equal(_.map(interfaceRequired, 'name'))
})
Expand Down
1 change: 1 addition & 0 deletions test/specs/commonTests/isConformant.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { consoleUtil, sandbox, syntheticEvent } from 'test/utils'
* Assert Component conforms to guidelines that are applicable to all components.
* @param {React.Component|Function} Component A component that should conform.
* @param {Object} [options={}]
* @param {array} [options.ignoredProps=[]] Props that will be ignored in typings tests.
* @param {Object} [options.eventTargets={}] Map of events and the child component to target.
* @param {Object} [options.requiredProps={}] Props required to render Component without errors or warnings.
*/
Expand Down
15 changes: 13 additions & 2 deletions test/specs/commonTests/tsHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import {
SyntaxKind,
} from 'typescript'

const isAnyKeyword = ({ kind }) => kind === SyntaxKind.AnyKeyword
const isIndexSignature = ({ kind }) => kind === SyntaxKind.IndexSignature
const isInterface = ({ kind }) => kind === SyntaxKind.InterfaceDeclaration

const isExportModifier = ({ kind }) => kind === SyntaxKind.ExportKeyword

const isPropertySignature = ({ kind }) => kind === SyntaxKind.PropertySignature
const isStringKeyword = ({ kind }) => kind === SyntaxKind.StringKeyword

const getProps = members => {
const props = _.filter(members, isPropertySignature)
Expand Down Expand Up @@ -47,6 +48,16 @@ export const getInterfaces = nodes => {
}))
}

export const hasAnySignature = nodes => {
const signatures = _.filter(nodes, isIndexSignature)

return _.some(signatures, ({ parameters, type: rightType }) => {
const { name: { text }, type } = _.head(parameters)

return isAnyKeyword(rightType) && isStringKeyword(type) && text === 'key'
})
}

export const requireTs = tsPath => {
try {
return require(`!raw-loader!../../../src/${tsPath}`)
Expand Down
1 change: 1 addition & 0 deletions test/specs/modules/Popup/Popup-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('Popup', () => {
})

common.hasSubComponents(Popup, [PopupHeader, PopupContent])
common.hasValidTypings(Popup)

// Heads up!
//
Expand Down