diff --git a/index.d.ts b/index.d.ts index a309b17147..62e30b68aa 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,7 +2,10 @@ export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm'; export { default as MountNode, MountNodeProps } from './dist/commonjs/addons/MountNode'; export { default as Pagination, PaginationProps } from './dist/commonjs/addons/Pagination'; -export { default as PaginationItem, PaginationItemProps } from './dist/commonjs/addons/Pagination/PaginationItem'; +export { + default as PaginationItem, + PaginationItemProps +} from './dist/commonjs/addons/Pagination/PaginationItem'; export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal'; export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio'; export { default as Ref, RefProps } from './dist/commonjs/addons/Ref'; @@ -40,43 +43,91 @@ export { } from './dist/commonjs/collections/Breadcrumb/BreadcrumbSection'; export { default as Form, FormComponent, FormProps } from './dist/commonjs/collections/Form'; -export { default as FormButton, FormButtonProps } from './dist/commonjs/collections/Form/FormButton'; -export { default as FormCheckbox, FormCheckboxProps } from './dist/commonjs/collections/Form/FormCheckbox'; -export { default as FormDropdown, FormDropdownProps } from './dist/commonjs/collections/Form/FormDropdown'; +export { + default as FormButton, + FormButtonProps +} from './dist/commonjs/collections/Form/FormButton'; +export { + default as FormCheckbox, + FormCheckboxProps +} from './dist/commonjs/collections/Form/FormCheckbox'; +export { + default as FormDropdown, + FormDropdownProps +} from './dist/commonjs/collections/Form/FormDropdown'; export { default as FormField, FormFieldProps } from './dist/commonjs/collections/Form/FormField'; export { default as FormGroup, FormGroupProps } from './dist/commonjs/collections/Form/FormGroup'; export { default as FormInput, FormInputProps } from './dist/commonjs/collections/Form/FormInput'; export { default as FormRadio, FormRadioProps } from './dist/commonjs/collections/Form/FormRadio'; -export { default as FormSelect, FormSelectProps } from './dist/commonjs/collections/Form/FormSelect'; -export { default as FormTextArea, FormTextAreaProps } from './dist/commonjs/collections/Form/FormTextArea'; +export { + default as FormSelect, + FormSelectProps +} from './dist/commonjs/collections/Form/FormSelect'; +export { + default as FormTextArea, + FormTextAreaProps +} from './dist/commonjs/collections/Form/FormTextArea'; export { default as Grid, GridProps } from './dist/commonjs/collections/Grid'; -export { default as GridColumn, GridColumnProps } from './dist/commonjs/collections/Grid/GridColumn'; +export { + default as GridColumn, + GridColumnProps +} from './dist/commonjs/collections/Grid/GridColumn'; export { default as GridRow, GridRowProps } from './dist/commonjs/collections/Grid/GridRow'; export { default as Menu, MenuProps } from './dist/commonjs/collections/Menu'; -export { default as MenuHeader, MenuHeaderProps } from './dist/commonjs/collections/Menu/MenuHeader'; +export { + default as MenuHeader, + MenuHeaderProps +} from './dist/commonjs/collections/Menu/MenuHeader'; export { default as MenuItem, MenuItemProps } from './dist/commonjs/collections/Menu/MenuItem'; export { default as MenuMenu, MenuMenuProps } from './dist/commonjs/collections/Menu/MenuMenu'; export { default as Message, MessageProps } from './dist/commonjs/collections/Message'; -export { default as MessageContent, MessageContentProps } from './dist/commonjs/collections/Message/MessageContent'; -export { default as MessageHeader, MessageHeaderProps } from './dist/commonjs/collections/Message/MessageHeader'; -export { default as MessageItem, MessageItemProps } from './dist/commonjs/collections/Message/MessageItem'; -export { default as MessageList, MessageListProps } from './dist/commonjs/collections/Message/MessageList'; +export { + default as MessageContent, + MessageContentProps +} from './dist/commonjs/collections/Message/MessageContent'; +export { + default as MessageHeader, + MessageHeaderProps +} from './dist/commonjs/collections/Message/MessageHeader'; +export { + default as MessageItem, + MessageItemProps +} from './dist/commonjs/collections/Message/MessageItem'; +export { + default as MessageList, + MessageListProps +} from './dist/commonjs/collections/Message/MessageList'; export { default as Table, TableProps } from './dist/commonjs/collections/Table'; export { default as TableBody, TableBodyProps } from './dist/commonjs/collections/Table/TableBody'; export { default as TableCell, TableCellProps } from './dist/commonjs/collections/Table/TableCell'; -export { default as TableFooter, TableFooterProps } from './dist/commonjs/collections/Table/TableFooter'; -export { default as TableHeader, TableHeaderProps } from './dist/commonjs/collections/Table/TableHeader'; -export { default as TableHeaderCell, TableHeaderCellProps } from './dist/commonjs/collections/Table/TableHeaderCell'; +export { + default as TableFooter, + TableFooterProps +} from './dist/commonjs/collections/Table/TableFooter'; +export { + default as TableHeader, + TableHeaderProps +} from './dist/commonjs/collections/Table/TableHeader'; +export { + default as TableHeaderCell, + TableHeaderCellProps +} from './dist/commonjs/collections/Table/TableHeaderCell'; export { default as TableRow, TableRowProps } from './dist/commonjs/collections/Table/TableRow'; // Elements export { default as Button, ButtonProps } from './dist/commonjs/elements/Button/Button'; -export { default as ButtonContent, ButtonContentProps } from './dist/commonjs/elements/Button/ButtonContent'; -export { default as ButtonGroup, ButtonGroupProps } from './dist/commonjs/elements/Button/ButtonGroup'; +export { + default as ButtonContent, + ButtonContentProps +} from './dist/commonjs/elements/Button/ButtonContent'; +export { + default as ButtonGroup, + ButtonGroupProps +} from './dist/commonjs/elements/Button/ButtonGroup'; export { default as ButtonOr, ButtonOrProps } from './dist/commonjs/elements/Button/ButtonOr'; export { default as Container, ContainerProps } from './dist/commonjs/elements/Container'; @@ -86,8 +137,14 @@ export { default as Divider, DividerProps } from './dist/commonjs/elements/Divid export { default as Flag, FlagProps } from './dist/commonjs/elements/Flag'; export { default as Header, HeaderProps } from './dist/commonjs/elements/Header'; -export { default as HeaderContent, HeaderContentProps } from './dist/commonjs/elements/Header/HeaderContent'; -export { default as HeaderSubheader, HeaderSubheaderProps } from './dist/commonjs/elements/Header/HeaderSubheader'; +export { + default as HeaderContent, + HeaderContentProps +} from './dist/commonjs/elements/Header/HeaderContent'; +export { + default as HeaderSubheader, + HeaderSubheaderProps +} from './dist/commonjs/elements/Header/HeaderSubheader'; export { default as Icon, IconProps } from './dist/commonjs/elements/Icon'; export { default as IconGroup, IconGroupProps } from './dist/commonjs/elements/Icon/IconGroup'; @@ -98,12 +155,21 @@ export { default as ImageGroup, ImageGroupProps } from './dist/commonjs/elements export { default as Input, InputProps, InputOnChangeData } from './dist/commonjs/elements/Input'; export { default as Label, LabelProps } from './dist/commonjs/elements/Label'; -export { default as LabelDetail, LabelDetailProps } from './dist/commonjs/elements/Label/LabelDetail'; +export { + default as LabelDetail, + LabelDetailProps +} from './dist/commonjs/elements/Label/LabelDetail'; export { default as LabelGroup, LabelGroupProps } from './dist/commonjs/elements/Label/LabelGroup'; export { default as List, ListProps } from './dist/commonjs/elements/List'; -export { default as ListContent, ListContentProps } from './dist/commonjs/elements/List/ListContent'; -export { default as ListDescription, ListDescriptionProps } from './dist/commonjs/elements/List/ListDescription'; +export { + default as ListContent, + ListContentProps +} from './dist/commonjs/elements/List/ListContent'; +export { + default as ListDescription, + ListDescriptionProps +} from './dist/commonjs/elements/List/ListDescription'; export { default as ListHeader, ListHeaderProps } from './dist/commonjs/elements/List/ListHeader'; export { default as ListIcon, ListIconProps } from './dist/commonjs/elements/List/ListIcon'; export { default as ListItem, ListItemProps } from './dist/commonjs/elements/List/ListItem'; @@ -114,14 +180,26 @@ export { default as Loader, LoaderProps } from './dist/commonjs/elements/Loader' export { default as Rail, RailProps } from './dist/commonjs/elements/Rail'; export { default as Reveal, RevealProps } from './dist/commonjs/elements/Reveal'; -export { default as RevealContent, RevealContentProps } from './dist/commonjs/elements/Reveal/RevealContent'; +export { + default as RevealContent, + RevealContentProps +} from './dist/commonjs/elements/Reveal/RevealContent'; export { default as Segment, SegmentProps } from './dist/commonjs/elements/Segment'; -export { default as SegmentGroup, SegmentGroupProps } from './dist/commonjs/elements/Segment/SegmentGroup'; +export { + default as SegmentGroup, + SegmentGroupProps +} from './dist/commonjs/elements/Segment/SegmentGroup'; export { default as Step, StepProps } from './dist/commonjs/elements/Step'; -export { default as StepContent, StepContentProps } from './dist/commonjs/elements/Step/StepContent'; -export { default as StepDescription, StepDescriptionProps } from './dist/commonjs/elements/Step/StepDescription'; +export { + default as StepContent, + StepContentProps +} from './dist/commonjs/elements/Step/StepContent'; +export { + default as StepDescription, + StepDescriptionProps +} from './dist/commonjs/elements/Step/StepDescription'; export { default as StepGroup, StepGroupProps } from './dist/commonjs/elements/Step/StepGroup'; export { default as StepTitle, StepTitleProps } from './dist/commonjs/elements/Step/StepTitle'; @@ -135,20 +213,48 @@ export { AccordionAccordionProps, AccordionPanelProps } from './dist/commonjs/modules/Accordion/AccordionAccordion'; -export { default as AccordionContent, AccordionContentProps } from './dist/commonjs/modules/Accordion/AccordionContent'; -export { default as AccordionTitle, AccordionTitleProps } from './dist/commonjs/modules/Accordion/AccordionTitle'; +export { + default as AccordionContent, + AccordionContentProps +} from './dist/commonjs/modules/Accordion/AccordionContent'; +export { + default as AccordionTitle, + AccordionTitleProps +} from './dist/commonjs/modules/Accordion/AccordionTitle'; export { default as Checkbox, CheckboxProps } from './dist/commonjs/modules/Checkbox'; export { default as Dimmer, DimmerProps } from './dist/commonjs/modules/Dimmer'; -export { default as DimmerDimmable, DimmerDimmableProps } from './dist/commonjs/modules/Dimmer/DimmerDimmable'; -export { default as DimmerInner, DimmerInnerProps } from './dist/commonjs/modules/Dimmer/DimmerInner'; +export { + default as DimmerDimmable, + DimmerDimmableProps +} from './dist/commonjs/modules/Dimmer/DimmerDimmable'; +export { + default as DimmerInner, + DimmerInnerProps +} from './dist/commonjs/modules/Dimmer/DimmerInner'; -export { default as Dropdown, DropdownProps, DropdownOnSearchChangeData } from './dist/commonjs/modules/Dropdown'; -export { default as DropdownDivider, DropdownDividerProps } from './dist/commonjs/modules/Dropdown/DropdownDivider'; -export { default as DropdownHeader, DropdownHeaderProps } from './dist/commonjs/modules/Dropdown/DropdownHeader'; -export { default as DropdownItem, DropdownItemProps } from './dist/commonjs/modules/Dropdown/DropdownItem'; -export { default as DropdownMenu, DropdownMenuProps } from './dist/commonjs/modules/Dropdown/DropdownMenu'; +export { + default as Dropdown, + DropdownProps, + DropdownOnSearchChangeData +} from './dist/commonjs/modules/Dropdown'; +export { + default as DropdownDivider, + DropdownDividerProps +} from './dist/commonjs/modules/Dropdown/DropdownDivider'; +export { + default as DropdownHeader, + DropdownHeaderProps +} from './dist/commonjs/modules/Dropdown/DropdownHeader'; +export { + default as DropdownItem, + DropdownItemProps +} from './dist/commonjs/modules/Dropdown/DropdownItem'; +export { + default as DropdownMenu, + DropdownMenuProps +} from './dist/commonjs/modules/Dropdown/DropdownMenu'; export { default as DropdownSearchInput, DropdownSearchInputProps @@ -157,14 +263,32 @@ export { export { default as Embed, EmbedProps } from './dist/commonjs/modules/Embed'; export { default as Modal, ModalProps } from './dist/commonjs/modules/Modal'; -export { default as ModalActions, ModalActionsProps } from './dist/commonjs/modules/Modal/ModalActions'; -export { default as ModalContent, ModalContentProps } from './dist/commonjs/modules/Modal/ModalContent'; -export { default as ModalDescription, ModalDescriptionProps } from './dist/commonjs/modules/Modal/ModalDescription'; -export { default as ModalHeader, ModalHeaderProps } from './dist/commonjs/modules/Modal/ModalHeader'; +export { + default as ModalActions, + ModalActionsProps +} from './dist/commonjs/modules/Modal/ModalActions'; +export { + default as ModalContent, + ModalContentProps +} from './dist/commonjs/modules/Modal/ModalContent'; +export { + default as ModalDescription, + ModalDescriptionProps +} from './dist/commonjs/modules/Modal/ModalDescription'; +export { + default as ModalHeader, + ModalHeaderProps +} from './dist/commonjs/modules/Modal/ModalHeader'; export { default as Popup, PopupProps } from './dist/commonjs/modules/Popup'; -export { default as PopupContent, PopupContentProps } from './dist/commonjs/modules/Popup/PopupContent'; -export { default as PopupHeader, PopupHeaderProps } from './dist/commonjs/modules/Popup/PopupHeader'; +export { + default as PopupContent, + PopupContentProps +} from './dist/commonjs/modules/Popup/PopupContent'; +export { + default as PopupHeader, + PopupHeaderProps +} from './dist/commonjs/modules/Popup/PopupHeader'; export { default as Progress, ProgressProps } from './dist/commonjs/modules/Progress'; @@ -172,13 +296,28 @@ export { default as Rating, RatingProps } from './dist/commonjs/modules/Rating'; export { default as RatingIcon, RatingIconProps } from './dist/commonjs/modules/Rating/RatingIcon'; export { default as Search, SearchProps, SearchResultData } from './dist/commonjs/modules/Search'; -export { default as SearchCategory, SearchCategoryProps } from './dist/commonjs/modules/Search/SearchCategory'; -export { default as SearchResult, SearchResultProps } from './dist/commonjs/modules/Search/SearchResult'; -export { default as SearchResults, SearchResultsProps } from './dist/commonjs/modules/Search/SearchResults'; +export { + default as SearchCategory, + SearchCategoryProps +} from './dist/commonjs/modules/Search/SearchCategory'; +export { + default as SearchResult, + SearchResultProps +} from './dist/commonjs/modules/Search/SearchResult'; +export { + default as SearchResults, + SearchResultsProps +} from './dist/commonjs/modules/Search/SearchResults'; export { default as Sidebar, SidebarProps } from './dist/commonjs/modules/Sidebar'; -export { default as SidebarPushable, SidebarPushableProps } from './dist/commonjs/modules/Sidebar/SidebarPushable'; -export { default as SidebarPusher, SidebarPusherProps } from './dist/commonjs/modules/Sidebar/SidebarPusher'; +export { + default as SidebarPushable, + SidebarPushableProps +} from './dist/commonjs/modules/Sidebar/SidebarPushable'; +export { + default as SidebarPusher, + SidebarPusherProps +} from './dist/commonjs/modules/Sidebar/SidebarPusher'; export { default as Sticky, StickyProps } from './dist/commonjs/modules/Sticky'; @@ -191,27 +330,57 @@ export { TransitionPropDuration, TRANSITION_STATUSES } from './dist/commonjs/modules/Transition'; -export { default as TransitionGroup, TransitionGroupProps } from './dist/commonjs/modules/Transition/TransitionGroup'; +export { + default as TransitionGroup, + TransitionGroupProps +} from './dist/commonjs/modules/Transition/TransitionGroup'; // Views export { default as Advertisement, AdvertisementProps } from './dist/commonjs/views/Advertisement'; export { default as Card, CardProps } from './dist/commonjs/views/Card'; export { default as CardContent, CardContentProps } from './dist/commonjs/views/Card/CardContent'; -export { default as CardDescription, CardDescriptionProps } from './dist/commonjs/views/Card/CardDescription'; +export { + default as CardDescription, + CardDescriptionProps +} from './dist/commonjs/views/Card/CardDescription'; export { default as CardGroup, CardGroupProps } from './dist/commonjs/views/Card/CardGroup'; export { default as CardHeader, CardHeaderProps } from './dist/commonjs/views/Card/CardHeader'; export { default as CardMeta, CardMetaProps } from './dist/commonjs/views/Card/CardMeta'; export { default as Comment, CommentProps } from './dist/commonjs/views/Comment'; -export { default as CommentAction, CommentActionProps } from './dist/commonjs/views/Comment/CommentAction'; -export { default as CommentActions, CommentActionsProps } from './dist/commonjs/views/Comment/CommentActions'; -export { default as CommentAuthor, CommentAuthorProps } from './dist/commonjs/views/Comment/CommentAuthor'; -export { default as CommentAvatar, CommentAvatarProps } from './dist/commonjs/views/Comment/CommentAvatar'; -export { default as CommentContent, CommentContentProps } from './dist/commonjs/views/Comment/CommentContent'; -export { default as CommentGroup, CommentGroupProps } from './dist/commonjs/views/Comment/CommentGroup'; -export { default as CommentMetadata, CommentMetadataProps } from './dist/commonjs/views/Comment/CommentMetadata'; -export { default as CommentText, CommentTextProps } from './dist/commonjs/views/Comment/CommentText'; +export { + default as CommentAction, + CommentActionProps +} from './dist/commonjs/views/Comment/CommentAction'; +export { + default as CommentActions, + CommentActionsProps +} from './dist/commonjs/views/Comment/CommentActions'; +export { + default as CommentAuthor, + CommentAuthorProps +} from './dist/commonjs/views/Comment/CommentAuthor'; +export { + default as CommentAvatar, + CommentAvatarProps +} from './dist/commonjs/views/Comment/CommentAvatar'; +export { + default as CommentContent, + CommentContentProps +} from './dist/commonjs/views/Comment/CommentContent'; +export { + default as CommentGroup, + CommentGroupProps +} from './dist/commonjs/views/Comment/CommentGroup'; +export { + default as CommentMetadata, + CommentMetadataProps +} from './dist/commonjs/views/Comment/CommentMetadata'; +export { + default as CommentText, + CommentTextProps +} from './dist/commonjs/views/Comment/CommentText'; export { default as Feed, FeedProps } from './dist/commonjs/views/Feed'; export { default as FeedContent, FeedContentProps } from './dist/commonjs/views/Feed/FeedContent'; @@ -226,7 +395,10 @@ export { default as FeedUser, FeedUserProps } from './dist/commonjs/views/Feed/F export { default as Item, ItemProps } from './dist/commonjs/views/Item'; export { default as ItemContent, ItemContentProps } from './dist/commonjs/views/Item/ItemContent'; -export { default as ItemDescription, ItemDescriptionProps } from './dist/commonjs/views/Item/ItemDescription'; +export { + default as ItemDescription, + ItemDescriptionProps +} from './dist/commonjs/views/Item/ItemDescription'; export { default as ItemExtra, ItemExtraProps } from './dist/commonjs/views/Item/ItemExtra'; export { default as ItemGroup, ItemGroupProps } from './dist/commonjs/views/Item/ItemGroup'; export { default as ItemHeader, ItemHeaderProps } from './dist/commonjs/views/Item/ItemHeader'; @@ -234,6 +406,15 @@ export { default as ItemImage, ItemImageProps } from './dist/commonjs/views/Item export { default as ItemMeta, ItemMetaProps } from './dist/commonjs/views/Item/ItemMeta'; export { default as Statistic, StatisticProps } from './dist/commonjs/views/Statistic'; -export { default as StatisticGroup, StatisticGroupProps } from './dist/commonjs/views/Statistic/StatisticGroup'; -export { default as StatisticLabel, StatisticLabelProps } from './dist/commonjs/views/Statistic/StatisticLabel'; -export { default as StatisticValue, StatisticValueProps } from './dist/commonjs/views/Statistic/StatisticValue'; +export { + default as StatisticGroup, + StatisticGroupProps +} from './dist/commonjs/views/Statistic/StatisticGroup'; +export { + default as StatisticLabel, + StatisticLabelProps +} from './dist/commonjs/views/Statistic/StatisticLabel'; +export { + default as StatisticValue, + StatisticValueProps +} from './dist/commonjs/views/Statistic/StatisticValue'; diff --git a/karma.conf.babel.js b/karma.conf.babel.js index 33d07b5164..dbf2bd74aa 100644 --- a/karma.conf.babel.js +++ b/karma.conf.babel.js @@ -1,12 +1,16 @@ +import fs from 'fs' import { executablePath } from 'puppeteer' + import config from './config' import webpackConfig from './webpack.config.babel' process.env.CHROME_BIN = executablePath() +const { paths } = config + const formatError = (msg) => { // filter out empty lines and node_modules - if (!msg.trim() || /~/.test(msg)) return '' + if (!msg.trim() || /~/.test(msg) || /node_modules\//.test(msg)) return '' // indent the error beneath the it() message let newLine = ` ${msg}` @@ -24,8 +28,12 @@ const formatError = (msg) => { export default (karmaConfig) => { karmaConfig.set({ - basePath: process.cwd(), + basePath: __dirname, browsers: ['puppeteer'], + browserConsoleLogOptions: { + level: 'log', + terminal: true, + }, client: { mocha: { reporter: 'html', // change Karma's debug.html to mocha web reporter @@ -33,7 +41,7 @@ export default (karmaConfig) => { }, }, coverageReporter: { - reporters: [{ type: 'lcov', dir: 'coverage', subdir: '.' }, { type: 'text-summary' }], + reporters: [{ type: 'lcov', dir: 'coverage', subdir: '.' }], includeAllSources: true, }, customLaunchers: { @@ -47,15 +55,30 @@ export default (karmaConfig) => { ], }, }, - files: ['./test/tests.bundle.js'], + files: [ + { pattern: 'docs/app/logo.png', watched: false, included: false, served: true }, + { pattern: 'docs/app/assets/**/*.jpg', watched: false, included: false, served: true }, + { pattern: 'docs/app/assets/**/*.png', watched: false, included: false, served: true }, + './test/tests.bundle.js', + ], formatError, frameworks: ['mocha'], + // make karma serve all files that the web server does: /* => /docs/app/* + proxies: fs.readdirSync(paths.docsSrc()).reduce((acc, file) => { + const isDir = fs.statSync(paths.docsSrc(file)).isDirectory() + const trailingSlash = isDir ? '/' : '' + + const original = `/${file}${trailingSlash}` + acc[original] = `/base/docs/app/${file}${trailingSlash}` + return acc + }, {}), reporters: ['mocha', 'coverage'], + reportSlowerThan: 100, singleRun: true, preprocessors: { // do not include 'coverage' preprocessor for karma-coverage // code is already instrumented by babel-plugin-__coverage__ - './test/tests.bundle.js': ['webpack'], + 'test/tests.bundle.js': ['webpack'], }, webpack: { entry: './test/tests.bundle.js', diff --git a/src/addons/Select/Select.d.ts b/src/addons/Select/Select.d.ts index 058e7fa6f3..4ae53334ce 100644 --- a/src/addons/Select/Select.d.ts +++ b/src/addons/Select/Select.d.ts @@ -3,11 +3,14 @@ import * as React from 'react'; import { DropdownProps } from '../../modules/Dropdown'; import DropdownDivider from '../../modules/Dropdown/DropdownDivider'; import DropdownHeader from '../../modules/Dropdown/DropdownHeader'; -import DropdownItem from '../../modules/Dropdown/DropdownItem'; +import DropdownItem, { DropdownItemProps } from '../../modules/Dropdown/DropdownItem'; import DropdownMenu from '../../modules/Dropdown/DropdownMenu'; export interface SelectProps extends DropdownProps { [key: string]: any; + + /** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */ + options: Array; } interface SelectComponent extends React.StatelessComponent { diff --git a/src/addons/Select/Select.js b/src/addons/Select/Select.js index dd12b4ea32..f6051dc2ca 100644 --- a/src/addons/Select/Select.js +++ b/src/addons/Select/Select.js @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types' import React from 'react' import Dropdown from '../../modules/Dropdown' @@ -11,6 +12,11 @@ function Select(props) { return } +Select.propTypes = { + /** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */ + options: PropTypes.arrayOf(PropTypes.shape(Dropdown.Item.propTypes)).isRequired, +} + Select.Divider = Dropdown.Divider Select.Header = Dropdown.Header Select.Item = Dropdown.Item diff --git a/src/behaviors/Visibility/Visibility.js b/src/behaviors/Visibility/Visibility.js index e617244074..7f031b4483 100644 --- a/src/behaviors/Visibility/Visibility.js +++ b/src/behaviors/Visibility/Visibility.js @@ -203,6 +203,8 @@ export default class Visibility extends Component { } componentDidMount() { + this.mounted = true + if (!isBrowser()) return const { context, fireOnMount, updateOn } = this.props @@ -216,6 +218,7 @@ export default class Visibility extends Component { const { context } = this.props this.unattachHandlers(context) + this.mounted = false } attachHandlers(context, updateOn) { @@ -300,6 +303,8 @@ export default class Visibility extends Component { } update = () => { + if (!this.mounted) return + this.ticking = false this.oldCalculations = this.calculations diff --git a/src/collections/Form/FormSelect.d.ts b/src/collections/Form/FormSelect.d.ts index d889c56644..9ebcb18e1c 100644 --- a/src/collections/Form/FormSelect.d.ts +++ b/src/collections/Form/FormSelect.d.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import { SelectProps } from '../../addons/Select'; +import { DropdownItemProps } from '../../modules/Dropdown/DropdownItem'; import { FormFieldProps } from './FormField'; export interface FormSelectProps extends FormFieldProps, SelectProps { @@ -11,6 +12,9 @@ export interface FormSelectProps extends FormFieldProps, SelectProps { /** A FormField control prop. */ control?: any; + + /** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */ + options: Array; } declare const FormSelect: React.StatelessComponent; diff --git a/src/collections/Form/FormSelect.js b/src/collections/Form/FormSelect.js index 3c7844a850..e6e0a58fc2 100644 --- a/src/collections/Form/FormSelect.js +++ b/src/collections/Form/FormSelect.js @@ -1,7 +1,9 @@ +import PropTypes from 'prop-types' import React from 'react' import { customPropTypes, getElementType, getUnhandledProps } from '../../lib' import Select from '../../addons/Select' +import Dropdown from '../../modules/Dropdown' import FormField from './FormField' /** @@ -10,11 +12,11 @@ import FormField from './FormField' * @see Select */ function FormSelect(props) { - const { control } = props + const { control, options } = props const rest = getUnhandledProps(FormSelect, props) const ElementType = getElementType(FormSelect, props) - return + return } FormSelect.propTypes = { @@ -23,6 +25,9 @@ FormSelect.propTypes = { /** A FormField control prop. */ control: FormField.propTypes.control, + + /** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */ + options: PropTypes.arrayOf(PropTypes.shape(Dropdown.Item.propTypes)).isRequired, } FormSelect.defaultProps = { diff --git a/src/modules/Embed/Embed.js b/src/modules/Embed/Embed.js index 413ab5eecf..3205a82562 100644 --- a/src/modules/Embed/Embed.js +++ b/src/modules/Embed/Embed.js @@ -86,10 +86,6 @@ export default class Embed extends Component { static autoControlledProps = ['active'] - static defaultProps = { - icon: 'video play', - } - getSrc() { const { autoplay = true, @@ -145,9 +141,11 @@ export default class Embed extends Component { const rest = getUnhandledProps(Embed, this.props) const ElementType = getElementType(Embed, this.props) + const iconShorthand = icon !== undefined ? icon : 'video play' + return ( - {Icon.create(icon, { autoGenerateKey: false })} + {Icon.create(iconShorthand, { autoGenerateKey: false })} {placeholder && } {this.renderEmbed()} diff --git a/src/modules/Popup/Popup.js b/src/modules/Popup/Popup.js index 1f9ec60ef5..0312fba9c8 100644 --- a/src/modules/Popup/Popup.js +++ b/src/modules/Popup/Popup.js @@ -146,6 +146,14 @@ export default class Popup extends Component { state = {} + componentDidMount() { + this.mounted = true + } + + componentWillUnmount() { + this.mounted = false + } + computePopupStyle(positions) { const style = { position: 'absolute' } @@ -295,7 +303,9 @@ export default class Popup extends Component { this.setState({ closed: true }) eventStack.unsub('scroll', this.hideOnScroll, { target: window }) - setTimeout(() => this.setState({ closed: false }), 50) + setTimeout(() => { + if (this.mounted) this.setState({ closed: false }) + }, 50) this.handleClose(e) } diff --git a/src/modules/Progress/Progress.js b/src/modules/Progress/Progress.js index 9e7d727a8d..7c98c6255b 100644 --- a/src/modules/Progress/Progress.js +++ b/src/modules/Progress/Progress.js @@ -111,6 +111,7 @@ class Progress extends Component { getPercent = () => { const { precision, progress, total, value } = this.props const percent = _.clamp(this.calculatePercent(), 0, 100) + if (!_.isUndefined(total) && !_.isUndefined(value) && progress === 'value') { return (value / total) * 100 } @@ -175,7 +176,7 @@ class Progress extends Component { ) const rest = getUnhandledProps(Progress, this.props) const ElementType = getElementType(Progress, this.props) - const percent = this.getPercent() + const percent = this.getPercent() || 0 return ( diff --git a/test/setup.js b/test/setup.js index ec921dea67..975bfdccba 100644 --- a/test/setup.js +++ b/test/setup.js @@ -9,9 +9,9 @@ import Adapter from 'enzyme-adapter-react-16' import dirtyChai from 'dirty-chai' import sinonChai from 'sinon-chai' -// +// ---------------------------------------- // Enzyme -// +// ---------------------------------------- global.enzyme = enzyme global.shallow = enzyme.shallow global.render = enzyme.render @@ -22,18 +22,52 @@ enzyme.configure({ disableLifecycleMethods: true, }) -// +// ---------------------------------------- // Mocha -// +// ---------------------------------------- mocha.setup({ ui: 'bdd', }) -// +// ---------------------------------------- // Chai -// +// ---------------------------------------- global.expect = chai.expect chai.should() chai.use(chaiEnzyme()) chai.use(dirtyChai) chai.use(sinonChai) + +// ---------------------------------------- +// Console +// ---------------------------------------- +// Fail on all activity. +// It is important we overload console here, before consoleUtil.js is loaded and caches it. +let log +let info +let warn +let error + +const throwOnConsole = method => (...args) => { + throw new Error(`console.${method} should never be called but was called with:\n${args.join(' ')}`) +} + +/* eslint-disable no-console */ +beforeEach(() => { + log = console.log + info = console.info + warn = console.warn + error = console.error + + console.log = throwOnConsole('log') + console.info = throwOnConsole('info') + console.warn = throwOnConsole('warn') + console.error = throwOnConsole('error') +}) +afterEach(() => { + console.log = log + console.info = info + console.warn = warn + console.error = error +}) +/* eslint-enable no-console */ diff --git a/test/specs/addons/Select/Select-test.js b/test/specs/addons/Select/Select-test.js index 8efa20d670..a0c59db4e6 100644 --- a/test/specs/addons/Select/Select-test.js +++ b/test/specs/addons/Select/Select-test.js @@ -9,7 +9,7 @@ const requiredProps = { } describe('Select', () => { - common.isConformant(Select, requiredProps) + common.isConformant(Select, { requiredProps }) common.hasSubcomponents(Select, [Dropdown.Divider, Dropdown.Header, Dropdown.Item, Dropdown.Menu]) it('renders a selection Dropdown', () => { diff --git a/test/specs/collections/Form/Form-test.js b/test/specs/collections/Form/Form-test.js index 950187d96e..e39859cde9 100644 --- a/test/specs/collections/Form/Form-test.js +++ b/test/specs/collections/Form/Form-test.js @@ -14,7 +14,7 @@ import FormSelect from 'src/collections/Form/FormSelect' import FormTextArea from 'src/collections/Form/FormTextArea' import { SUI } from 'src/lib' import * as common from 'test/specs/commonTests' -import { sandbox } from 'test/utils' +import { consoleUtil, sandbox } from 'test/utils' describe('Form', () => { common.isConformant(Form) @@ -62,12 +62,14 @@ describe('Form', () => { describe('onSubmit', () => { it('prevents default on the event when there is no action', () => { + // Heads up! + // In this test we pass some invalid values to verify correct work. + consoleUtil.disableOnce() + const event = { preventDefault: sandbox.spy() } shallow(
).simulate('submit', event) - shallow().simulate('submit', event) - shallow().simulate('submit', event) event.preventDefault.should.have.been.calledThrice() diff --git a/test/specs/collections/Form/FormSelect-test.js b/test/specs/collections/Form/FormSelect-test.js index 6f5caf1b8c..a7e07d4389 100644 --- a/test/specs/collections/Form/FormSelect-test.js +++ b/test/specs/collections/Form/FormSelect-test.js @@ -4,12 +4,16 @@ import Select from 'src/addons/Select/Select' import FormSelect from 'src/collections/Form/FormSelect' import * as common from 'test/specs/commonTests' +const requiredProps = { + options: [], +} + describe('FormSelect', () => { - common.isConformant(FormSelect) - common.labelImplementsHtmlForProp(FormSelect) + common.isConformant(FormSelect, { requiredProps }) + common.labelImplementsHtmlForProp(FormSelect, { requiredProps }) it('renders a FormField with a Select control', () => { - shallow() + shallow() .find('FormField') .should.have.prop('control', Select) }) diff --git a/test/specs/collections/Table/Table-test.js b/test/specs/collections/Table/Table-test.js index 31472f8d9e..89e7262924 100644 --- a/test/specs/collections/Table/Table-test.js +++ b/test/specs/collections/Table/Table-test.js @@ -62,8 +62,10 @@ describe('Table', () => { _.without(SUI.SIZES, 'mini', 'tiny', 'medium', 'big', 'huge', 'massive'), ) - it('renders as a table by default', () => { - shallow().should.have.tagName('table') + describe('as', () => { + it('renders as a table by default', () => { + shallow(
).should.have.tagName('table') + }) }) describe('shorthand', () => { @@ -81,13 +83,12 @@ describe('Table', () => { const headerRow = ['Name', 'Status', 'Notes'] - const renderBodyRow = ({ name, status, notes }) => [ - name || { key: 0 }, - status || { key: 1 }, - notes || { key: 2 }, - ] + const renderBodyRow = ({ name, status, notes }, index) => ({ + key: index, + cells: [name || { key: 0 }, status || { key: 1 }, notes || { key: 2 }], + }) - const footerRow = [{ colSpan: 3, content: 'Total' }] + const footerRow = [{ colSpan: 3, content: 'Total', key: 'total' }] const tableData = [ { name: undefined, status: undefined, notes: undefined }, diff --git a/test/specs/collections/Table/TableRow-test.js b/test/specs/collections/Table/TableRow-test.js index 87e026ef2b..c25efda41a 100644 --- a/test/specs/collections/Table/TableRow-test.js +++ b/test/specs/collections/Table/TableRow-test.js @@ -21,30 +21,32 @@ describe('TableRow', () => { common.propKeyOnlyToClassName(TableRow, 'warning') it('renders as a tr by default', () => { - shallow() - .should.have.tagName('tr') + shallow().should.have.tagName('tr') }) describe('shorthand', () => { const cells = ['Name', 'Status', 'Notes'] it('renders empty tr with no shorthand', () => { - const wrapper = mount() - - wrapper.find('td').should.have.lengthOf(0) + shallow() + .find('td') + .should.have.lengthOf(0) }) it('renders the cells', () => { - const wrapper = mount() - - wrapper.find('td').should.have.lengthOf(cells.length) + shallow() + .find('TableCell') + .should.have.lengthOf(cells.length) }) it('renders the cells using cellAs', () => { - const cellAs = 'th' - const wrapper = mount() + const cellWrappers = shallow().find('TableCell') + + cellWrappers.should.have.lengthOf(cells.length) - wrapper.find(cellAs).should.have.lengthOf(cells.length) + cellWrappers.forEach((wrapper) => { + wrapper.shallow().should.have.tagName('th') + }) }) }) }) diff --git a/test/specs/commonTests/implementsClassNameProps.js b/test/specs/commonTests/implementsClassNameProps.js index 9f199d6f5e..216cfb1882 100644 --- a/test/specs/commonTests/implementsClassNameProps.js +++ b/test/specs/commonTests/implementsClassNameProps.js @@ -50,6 +50,7 @@ export const propKeyOnlyToClassName = (Component, propKey, options = {}) => { noDefaultClassNameFromProp(Component, propKey, [], options) it('adds prop name to className', () => { + consoleUtil.disableOnce() shallow(createElement(Component, { ...requiredProps, [propKey]: true })) .should.have.className(className) }) diff --git a/test/specs/commonTests/implementsCommonProps.js b/test/specs/commonTests/implementsCommonProps.js index 9ee1235e7f..116508b277 100644 --- a/test/specs/commonTests/implementsCommonProps.js +++ b/test/specs/commonTests/implementsCommonProps.js @@ -296,9 +296,13 @@ export const implementsWidthProp = (Component, widths = SUI.WIDTHS, options = {} /** * Assert that a Components with a label correctly implements the "id" and "htmlFor" props. + * * @param {React.Component|Function} Component The component to test. + * @param {Object} [options={}] + * @param {Object} [options.requiredProps={}] Props required to render the component. */ -export const labelImplementsHtmlForProp = (Component) => { +export const labelImplementsHtmlForProp = (Component, options = {}) => { + const { requiredProps = {} } = options const { assertRequired } = helpers('labelImplementsHtmlForProp', Component) describe('htmlFor (common)', () => { @@ -308,7 +312,7 @@ export const labelImplementsHtmlForProp = (Component) => { const id = 'id-for-test' const label = 'label-for-test' - const wrapper = mount() + const wrapper = mount() const labelNode = wrapper.find('label') wrapper.should.to.have.descendants(`#${id}`) diff --git a/test/specs/commonTests/implementsCreateMethod.js b/test/specs/commonTests/implementsCreateMethod.js index 5eb6972baa..8aea73ec09 100644 --- a/test/specs/commonTests/implementsCreateMethod.js +++ b/test/specs/commonTests/implementsCreateMethod.js @@ -10,6 +10,11 @@ export default (Component) => { const { name } = _.get(Component, 'prototype.constructor.name') describe('create shorthand method (common)', () => { + beforeEach(() => { + // we generate prop values which may throw warnings + // prevent failures due to console activity + consoleUtil.disableOnce() + }) it('is a static method', () => { Component.should.have.any.keys('create') Component.create.should.be.a('function') diff --git a/test/specs/docs/examples-test.js b/test/specs/docs/examples-test.js index 5ede94185a..36fbec3db0 100644 --- a/test/specs/docs/examples-test.js +++ b/test/specs/docs/examples-test.js @@ -1,40 +1,17 @@ import { createElement } from 'react' - -import { sandbox, consoleUtil } from '../../utils' -import { exampleContext } from '../../../docs/app/utils' +import { exampleContext } from 'docs/app/utils' describe('examples', () => { - /* eslint-disable no-console */ - beforeEach(() => { - // be sure the console is not disabled since we're checking for console errors - consoleUtil.enable() - sandbox.spy(console, 'info') - sandbox.spy(console, 'log') - sandbox.spy(console, 'warn') - sandbox.spy(console, 'error') - }) - afterEach(() => { - console.info.restore() - console.log.restore() - console.warn.restore() - console.error.restore() - }) exampleContext.keys().forEach((path) => { // don't test index files, they repeat errors of individual files if (/index\.js$/.test(path)) return const filename = path.replace(/^.*\/(\w+\.js)$/, '$1') - it(`${filename} renders without console messages`, () => { + it(`${filename} renders without console activity`, () => { // TODO also render the example's path in a just as the docs do const wrapper = mount(createElement(exampleContext(path).default)) - console.info.should.not.have.been.called(`Console info: ${console.info.args}`) - console.log.should.not.have.been.called(`Console log: ${console.log.args}`) - console.warn.should.not.have.been.called(`Console warn: ${console.warn.args}`) - console.error.should.not.have.been.called(`Console error: ${console.error.args}`) - wrapper.unmount() }) }) - /* eslint-enable no-console */ }) diff --git a/test/specs/elements/Header/Header-test.js b/test/specs/elements/Header/Header-test.js index 358ccd71be..bb4257c113 100644 --- a/test/specs/elements/Header/Header-test.js +++ b/test/specs/elements/Header/Header-test.js @@ -55,7 +55,7 @@ describe('Header', () => { describe('content', () => { it('is wrapped in HeaderContent when there is an image src', () => { - shallow(
) + shallow(
) .find('HeaderContent') .shallow() .should.contain.text('Bar') @@ -85,7 +85,7 @@ describe('Header', () => { it('adds HeaderSubheader as child when there is an image', () => { const text = faker.hacker.phrase() - shallow(
) + shallow(
) .find('HeaderSubheader') .should.have.prop('content', text) }) diff --git a/test/specs/elements/List/ListItem-test.js b/test/specs/elements/List/ListItem-test.js index 87d6fbeb53..44af6fc509 100644 --- a/test/specs/elements/List/ListItem-test.js +++ b/test/specs/elements/List/ListItem-test.js @@ -16,8 +16,7 @@ describe('ListItem', () => { describe('as', () => { it('omits className `list` when rendered as `li`', () => { - shallow() - .should.not.have.className('item') + shallow().should.not.have.className('item') }) }) @@ -27,8 +26,7 @@ describe('ListItem', () => { const event = { target: null } const props = { onClick, 'data-foo': 'bar' } - shallow() - .simulate('click', event) + shallow().simulate('click', event) onClick.should.have.been.calledOnce() onClick.should.have.been.calledWithExactly(event, props) @@ -37,8 +35,7 @@ describe('ListItem', () => { it('is not called when is disabled', () => { const onClick = sandbox.spy() - shallow() - .simulate('click') + shallow().simulate('click') onClick.should.have.callCount(0) }) }) @@ -47,15 +44,13 @@ describe('ListItem', () => { it('adds data attribute by default', () => { const value = faker.hacker.phrase() - shallow() - .should.have.data('value', value) + shallow().should.have.data('value', value) }) it('adds attribute when rendered as `li`', () => { const value = faker.hacker.phrase() - shallow() - .should.have.attr('value', value) + shallow().should.have.attr('value', value) }) }) @@ -94,7 +89,9 @@ describe('ListItem', () => { }) it(`renders wrapping ListContent when image and ${key} present`, () => { - const wrapper = shallow() + const wrapper = shallow( + , + ) wrapper.find('Image').should.have.lengthOf(1) wrapper.find('ListContent').should.have.lengthOf(1) @@ -104,20 +101,20 @@ describe('ListItem', () => { describe('role', () => { it('adds role=listitem', () => { - shallow() - .should.have.prop('role', 'listitem') + shallow().should.have.prop('role', 'listitem') }) it('adds role=listitem with children', () => { - shallow(
Test
) - .should.have.prop('role', 'listitem') + shallow( + +
Test
+
, + ).should.have.prop('role', 'listitem') }) it('adds role=listitem with content', () => { - shallow(} />) - .should.have.prop('role', 'listitem') + shallow(} />).should.have.prop('role', 'listitem') }) it('adds role=listitem with icon', () => { - shallow() - .should.have.prop('role', 'listitem') + shallow().should.have.prop('role', 'listitem') }) }) }) diff --git a/test/specs/lib/factories-test.js b/test/specs/lib/factories-test.js index 07b3cec5b8..b54c07b818 100644 --- a/test/specs/lib/factories-test.js +++ b/test/specs/lib/factories-test.js @@ -2,7 +2,7 @@ import _ from 'lodash' import React, { isValidElement } from 'react' import { createShorthand, createShorthandFactory } from 'src/lib' -import { sandbox } from 'test/utils' +import { consoleUtil, sandbox } from 'test/utils' // ---------------------------------------- // Utils @@ -18,7 +18,12 @@ const getShorthand = ({ overrideProps, autoGenerateKey, value, -}) => createShorthand(Component, mapValueToProps, value, { defaultProps, overrideProps, autoGenerateKey }) +}) => + createShorthand(Component, mapValueToProps, value, { + defaultProps, + overrideProps, + autoGenerateKey, + }) // ---------------------------------------- // Common tests @@ -172,6 +177,9 @@ describe('factories', () => { describe('key', () => { it('is not consumed', () => { + // silence React "`key` is not a prop" warning due to accessing props.key + consoleUtil.disableOnce() + getShorthand({ value: { key: 123 } }).props.should.have.property('key') }) @@ -213,25 +221,21 @@ describe('factories', () => { describe('when value is a string', () => { it('is generated from the value', () => { - getShorthand({ value: 'foo' }) - .should.have.property('key', 'foo') + getShorthand({ value: 'foo' }).should.have.property('key', 'foo') }) it('is not generated if autoGenerateKey is false', () => { - getShorthand({ value: 'foo', autoGenerateKey: false }) - .should.have.property('key', null) + getShorthand({ value: 'foo', autoGenerateKey: false }).should.have.property('key', null) }) }) describe('when value is a number', () => { it('is generated from the value', () => { - getShorthand({ value: 123 }) - .should.have.property('key', '123') + getShorthand({ value: 123 }).should.have.property('key', '123') }) it('is not generated if autoGenerateKey is false', () => { - getShorthand({ value: 123, autoGenerateKey: false }) - .should.have.property('key', null) + getShorthand({ value: 123, autoGenerateKey: false }).should.have.property('key', null) }) }) }) diff --git a/test/specs/modules/Dimmer/DimmerInner-test.js b/test/specs/modules/Dimmer/DimmerInner-test.js index 0b9bbed842..53a6765c17 100644 --- a/test/specs/modules/Dimmer/DimmerInner-test.js +++ b/test/specs/modules/Dimmer/DimmerInner-test.js @@ -22,44 +22,50 @@ describe('DimmerInner', () => { describe('onClickOutside', () => { it('called when Dimmer has not children', () => { const onClickOutside = sandbox.spy() - shallow() - .simulate('click') + shallow().simulate('click') onClickOutside.should.have.been.calledOnce() }) it('omitted when click on children', () => { + const element = document.createElement('div') + document.body.appendChild(element) const onClickOutside = sandbox.spy() const wrapper = mount(
{faker.hacker.phrase()}
-
, { - attachTo: document.body, - }) +
, + { + attachTo: element, + }, + ) + + wrapper + .find('div.content') + .childAt(0) + .simulate('click') + onClickOutside.should.have.not.been.called() - wrapper.find('div.content').childAt(0).simulate('click') - onClickOutside.should.have.been.callCount(0) + wrapper.unmount() + document.body.removeChild(element) }) it('called when click on Dimmer', () => { const onClickOutside = sandbox.spy() - mount({faker.hacker.phrase()}) - .simulate('click') + mount( + {faker.hacker.phrase()}, + ).simulate('click') onClickOutside.should.have.been.calledOnce() }) it('called when click on center', () => { const onClickOutside = sandbox.spy() const wrapper = mount( - - {faker.hacker.phrase()} - , + {faker.hacker.phrase()}, ) - wrapper - .find('div.content') - .simulate('click') + wrapper.find('div.content').simulate('click') onClickOutside.should.have.been.calledOnce() }) }) diff --git a/test/specs/modules/Dropdown/Dropdown-test.js b/test/specs/modules/Dropdown/Dropdown-test.js index 4619fa81ef..f05b4b1293 100644 --- a/test/specs/modules/Dropdown/Dropdown-test.js +++ b/test/specs/modules/Dropdown/Dropdown-test.js @@ -35,8 +35,8 @@ const wrapperRender = (...args) => (wrapper = render(...args)) // ---------------------------------------- const getOptions = (count = 5) => _.times(count, (i) => { - const text = `${i}-${faker.hacker.noun}` - const value = `${i}-${_.snakeCase(text)}` + const text = [i, ..._.times(3, faker.hacker.noun)].join(' ') + const value = _.snakeCase(text) return { text, value } }) @@ -1110,7 +1110,7 @@ describe('Dropdown', () => { it('does not display if value is undefined', () => { const text = faker.hacker.noun() - wrapperMount() + wrapperMount() .simulate('click') .find('DropdownItem') .simulate('click') @@ -1128,14 +1128,6 @@ describe('Dropdown', () => { .find('.trigger') .should.contain.text(text) }) - it('ignores the text prop', () => { - const text = faker.hacker.phrase() - const trigger =
{text}
- - wrapperRender( - , - ).should.not.have.descendants('div.text') - }) }) describe('menu', () => { @@ -1231,7 +1223,7 @@ describe('Dropdown', () => { dropdownMenuIsOpen() // click outside - domEvent.click(document) + domEvent.click(document.body) dropdownMenuIsClosed() }) diff --git a/test/specs/modules/Embed/Embed-test.js b/test/specs/modules/Embed/Embed-test.js index 2a204bc5d5..a0c8f15dca 100644 --- a/test/specs/modules/Embed/Embed-test.js +++ b/test/specs/modules/Embed/Embed-test.js @@ -4,11 +4,7 @@ import Embed from 'src/modules/Embed/Embed' import * as common from 'test/specs/commonTests' const assertIframeSrc = (props, srcPart) => { - const { - id = 'default-test-id', - source = 'youtube', - ...rest - } = props + const { id = 'default-test-id', source = 'youtube', ...rest } = props shallow() .find('iframe') @@ -40,7 +36,7 @@ describe('Embed', () => { width: '100%', }, }) - common.implementsIconProp(Embed) + common.implementsIconProp(Embed, { alwaysPresent: true }) common.propKeyOnlyToClassName(Embed, 'active') @@ -48,20 +44,17 @@ describe('Embed', () => { describe('active', () => { it('defaults to false', () => { - shallow() - .should.have.not.state('active') + shallow().should.have.not.state('active') }) it('passes to state', () => { - shallow() - .should.have.state('active', true) + shallow().should.have.state('active', true) }) it('renders nothing when false', () => { const children = 'child text' - shallow({children}) - .should.not.contain(
{children}
) + shallow({children}).should.not.contain(
{children}
) }) }) @@ -93,11 +86,9 @@ describe('Embed', () => { describe('defaultActive', () => { it('sets the initial active state', () => { - shallow() - .should.have.state('active', true) + shallow().should.have.state('active', true) - shallow() - .should.have.state('active', false) + shallow().should.have.state('active', false) }) }) @@ -116,10 +107,9 @@ describe('Embed', () => { }) it('renders img when defined', () => { - const url = 'foo.png' + const url = '/assets/images/wireframe/image.png' - shallow() - .should.contain() + shallow().should.contain() }) }) diff --git a/test/specs/modules/Modal/Modal-test.js b/test/specs/modules/Modal/Modal-test.js index 77691ca6f6..d5b97f42b6 100644 --- a/test/specs/modules/Modal/Modal-test.js +++ b/test/specs/modules/Modal/Modal-test.js @@ -30,12 +30,14 @@ const wrapperShallow = (...args) => (wrapper = shallow(...args)) describe('Modal', () => { beforeEach(() => { + if (wrapper && wrapper.unmount) wrapper.unmount() wrapper = undefined - document.body.innerHTML = '' - }) - afterEach(() => { - if (wrapper && wrapper.unmount) wrapper.unmount() + const dimmer = document.querySelector('.ui.dimmer') + const modal = document.querySelector('.ui.modal') + + if (dimmer) dimmer.parentNode.removeChild(dimmer) + if (modal) modal.parentNode.removeChild(modal) }) common.isConformant(Modal, { rendersPortal: true }) @@ -119,14 +121,13 @@ describe('Modal', () => { describe('onActionClick', () => { it('is called when an action is clicked', () => { const onActionClick = sandbox.spy() - const event = { target: null } const props = { actions: ['OK'], defaultOpen: true, onActionClick } wrapperMount() domEvent.click('.ui.modal .actions .button') onActionClick.should.have.been.calledOnce() - onActionClick.should.have.been.calledWithMatch(event, props) + onActionClick.should.have.been.calledWithMatch({}, props) }) }) @@ -308,7 +309,7 @@ describe('Modal', () => { const spy = sandbox.spy() wrapperMount() - domEvent.click('body') + domEvent.click(document.body) spy.should.not.have.been.called() }) }) @@ -344,7 +345,7 @@ describe('Modal', () => { it('is not called on body click', () => { wrapperMount() - domEvent.click('body') + domEvent.click(document.body) spy.should.not.have.been.calledOnce() }) @@ -388,25 +389,25 @@ describe('Modal', () => { it('closes the modal when Escape is pressed by default', () => { wrapperMount() - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') domEvent.keyDown(document, { key: 'Escape' }) - document.body.childElementCount.should.equal(0) + assertBodyContains('.ui.dimmer', false) }) it('closes the modal when true and Escape is pressed', () => { wrapperMount() - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') domEvent.keyDown(document, { key: 'Escape' }) - document.body.childElementCount.should.equal(0) + assertBodyContains('.ui.dimmer', false) }) it('does not close the modal when false and Escape is pressed', () => { wrapperMount() - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') domEvent.keyDown(document, { key: 'Escape' }) - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') }) }) @@ -417,16 +418,16 @@ describe('Modal', () => { it('closes the modal on document click when true', () => { wrapperMount() - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') domEvent.click(document.body) - document.body.childElementCount.should.equal(0) + assertBodyContains('.ui.dimmer', false) }) it('does not close the modal on document click when false', () => { wrapperMount() - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') domEvent.click(document.body) - document.body.childElementCount.should.equal(1) + assertBodyContains('.ui.dimmer') }) }) diff --git a/test/specs/modules/Popup/Popup-test.js b/test/specs/modules/Popup/Popup-test.js index 1e9926cfdb..ad31989b35 100644 --- a/test/specs/modules/Popup/Popup-test.js +++ b/test/specs/modules/Popup/Popup-test.js @@ -31,7 +31,6 @@ const assertInBody = (...args) => assertIn(document.body, ...args) describe('Popup', () => { beforeEach(() => { wrapper = undefined - document.body.innerHTML = '' }) afterEach(() => { @@ -289,13 +288,13 @@ describe('Popup', () => { it('it appears on hover', (done) => { const trigger = - wrapperMount() + wrapperMount() wrapper.find('button').simulate('mouseenter') setTimeout(() => { assertInBody('.ui.popup.visible') done() - }, 51) + }, 1) }) it('it appears on focus', () => { diff --git a/test/specs/modules/Search/Search-test.js b/test/specs/modules/Search/Search-test.js index b274473219..b783ec080e 100644 --- a/test/specs/modules/Search/Search-test.js +++ b/test/specs/modules/Search/Search-test.js @@ -33,11 +33,11 @@ const wrapperRender = (...args) => (wrapper = render(...args)) // Options // ---------------------------------------- const getOptions = (count = 5) => - _.times(count, () => ({ - title: _.times(3, faker.hacker.noun).join(' '), - description: _.times(3, faker.hacker.noun).join(' '), - image: 'foo.png', - price: faker.finance.amount(0, 100, 2, '$'), + _.times(count, i => ({ + title: [i, ..._.times(3, faker.hacker.noun)].join(' '), + description: [i, ..._.times(3, faker.hacker.noun)].join(' '), + image: '/assets/images/wireframe/image.png', + price: [i, faker.finance.amount(0, 100, 2, '$')].join(' '), })) // ------------------------------- @@ -764,13 +764,16 @@ describe('Search', () => { describe('input props', () => { // Search handles some of html props - const props = _.without(htmlInputAttrs, 'defaultValue') + const props = _.without(htmlInputAttrs, 'defaultValue', 'type') + const booleanProps = ['disabled'] props.forEach((propName) => { it(`passes "${propName}" to the `, () => { - wrapperMount() + const propValue = _.includes(booleanProps, propName) ? true : 'off' + + wrapperMount() .find('input') - .should.have.prop(propName) + .should.have.prop(propName, propValue) }) }) }) diff --git a/test/specs/modules/Search/SearchResult-test.js b/test/specs/modules/Search/SearchResult-test.js index 5986bbed3a..2daf7f5f17 100644 --- a/test/specs/modules/Search/SearchResult-test.js +++ b/test/specs/modules/Search/SearchResult-test.js @@ -5,5 +5,5 @@ const requiredProps = { title: '' } describe('SearchResult', () => { common.isConformant(SearchResult, { requiredProps }) - common.propKeyOnlyToClassName(SearchResult, 'active') + common.propKeyOnlyToClassName(SearchResult, 'active', { requiredProps }) }) diff --git a/test/tests.bundle.js b/test/tests.bundle.js index 3a31e72482..7fd44b1f98 100644 --- a/test/tests.bundle.js +++ b/test/tests.bundle.js @@ -2,10 +2,6 @@ import './setup' const testsContext = require.context('./', true, /-test\.js$/) -// TODO uncomment and fix failing tests -// console.error = (...args) => { throw new Error('console.error was called!\n\n' + args.join(' ')) } -// console.warn = (...args) => { throw new Error('console.warn was called!\n\n' + args.join(' ')) } - // only re-run changed tests, or all if none changed // https://www.npmjs.com/package/karma-webpack-with-fast-source-maps const __karmaWebpackManifest__ = [] diff --git a/test/utils/assertNodeContains.js b/test/utils/assertNodeContains.js index b701b82db5..d09c2b7ed7 100644 --- a/test/utils/assertNodeContains.js +++ b/test/utils/assertNodeContains.js @@ -3,11 +3,14 @@ * * @param {object} parentNode A parent DOM node * @param {string} childSelector A DOM selector for the child node - * @param {boolean} isPresent Indicating whether to assert is present or is not present + * @param {boolean} [isPresent=true] Indicating whether to assert is present or is not present */ export const assertNodeContains = (parentNode, childSelector, isPresent = true) => { const didFind = parentNode.querySelector(childSelector) !== null - didFind.should.equal(isPresent, `${didFind ? 'Found' : 'Did not find'} "${childSelector}" in the ${parentNode}.`) + + if (didFind !== isPresent) { + throw new Error(`"${childSelector}" ${didFind ? 'should not' : 'should'} have been found in ${parentNode}`) + } } /**