Skip to content

Commit 3a30105

Browse files
levithomasonlayershifter
authored andcommitted
chore(tests): fail on console activity (Semantic-Org#1542)
* chore(karma): serve assets, show logs, improve output * chore(tests): fail on all console activity * test(Popup): fix hover test * test(implementsClassNameProps): fix console activity * test(implementsCreateMethod): fix console activity * fix(mixed): fix lint issues, restore es-shim * fix(GridRow): fix value in test * fix(Header): fix value in test * fix(package): add react-test-renderer * test(TableRow-test): fix console messages * test(Modal): fix console errors * chore(karma): filter node_modules from stack * fix(package): manually merge chromium changes * test(Dropdown|Search): make getOptions always unique * test(Select): fix isConformant options, now fails * test(hasValidTypings): update test descriptions * test(Breadcrumb): add missing section keys * test(Dropdown): fix prop warnings * test(factories): silence prop.key access warning * fix(Select): fix options propTypes and typings * chore(karma): do not print coverage summary * test(FormSelect): define requireProps * test(Advertisement): fix missing requiredProps * test(Embed): fix default icon children conflict * test(Search): prevent duplicate opts in getOptions * chore(yarn): update yarn.lock * fix(Dimmer|Form|Progress): fixes in tests * test(Table): fix broken test * test(Dropdown): fix broken test * test(Search): fix broken tests * fix(Form): fix typings in FormSelect * style(Table): fix lint issue * test(docs): restore unmount * test(mixed): fix wrong image urls * fix(Visibility|Sticky): do not run updates on unmounted components * fix(Popup): do not run setState() when component is unmounted * style(Breadcrumb|Form): restore style changes * style(mixed): restore style changes * fix(Dimmer): update DimmerInner test * style(mixed): restore style-only changes
1 parent 500df9a commit 3a30105

32 files changed

+504
-247
lines changed

index.d.ts

+241-60
Large diffs are not rendered by default.

karma.conf.babel.js

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
import fs from 'fs'
12
import { executablePath } from 'puppeteer'
3+
24
import config from './config'
35
import webpackConfig from './webpack.config.babel'
46

57
process.env.CHROME_BIN = executablePath()
68

9+
const { paths } = config
10+
711
const formatError = (msg) => {
812
// filter out empty lines and node_modules
9-
if (!msg.trim() || /~/.test(msg)) return ''
13+
if (!msg.trim() || /~/.test(msg) || /node_modules\//.test(msg)) return ''
1014

1115
// indent the error beneath the it() message
1216
let newLine = ` ${msg}`
@@ -24,16 +28,20 @@ const formatError = (msg) => {
2428

2529
export default (karmaConfig) => {
2630
karmaConfig.set({
27-
basePath: process.cwd(),
31+
basePath: __dirname,
2832
browsers: ['puppeteer'],
33+
browserConsoleLogOptions: {
34+
level: 'log',
35+
terminal: true,
36+
},
2937
client: {
3038
mocha: {
3139
reporter: 'html', // change Karma's debug.html to mocha web reporter
3240
ui: 'bdd',
3341
},
3442
},
3543
coverageReporter: {
36-
reporters: [{ type: 'lcov', dir: 'coverage', subdir: '.' }, { type: 'text-summary' }],
44+
reporters: [{ type: 'lcov', dir: 'coverage', subdir: '.' }],
3745
includeAllSources: true,
3846
},
3947
customLaunchers: {
@@ -47,15 +55,30 @@ export default (karmaConfig) => {
4755
],
4856
},
4957
},
50-
files: ['./test/tests.bundle.js'],
58+
files: [
59+
{ pattern: 'docs/app/logo.png', watched: false, included: false, served: true },
60+
{ pattern: 'docs/app/assets/**/*.jpg', watched: false, included: false, served: true },
61+
{ pattern: 'docs/app/assets/**/*.png', watched: false, included: false, served: true },
62+
'./test/tests.bundle.js',
63+
],
5164
formatError,
5265
frameworks: ['mocha'],
66+
// make karma serve all files that the web server does: /* => /docs/app/*
67+
proxies: fs.readdirSync(paths.docsSrc()).reduce((acc, file) => {
68+
const isDir = fs.statSync(paths.docsSrc(file)).isDirectory()
69+
const trailingSlash = isDir ? '/' : ''
70+
71+
const original = `/${file}${trailingSlash}`
72+
acc[original] = `/base/docs/app/${file}${trailingSlash}`
73+
return acc
74+
}, {}),
5375
reporters: ['mocha', 'coverage'],
76+
reportSlowerThan: 100,
5477
singleRun: true,
5578
preprocessors: {
5679
// do not include 'coverage' preprocessor for karma-coverage
5780
// code is already instrumented by babel-plugin-__coverage__
58-
'./test/tests.bundle.js': ['webpack'],
81+
'test/tests.bundle.js': ['webpack'],
5982
},
6083
webpack: {
6184
entry: './test/tests.bundle.js',

src/addons/Select/Select.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import * as React from 'react';
33
import { DropdownProps } from '../../modules/Dropdown';
44
import DropdownDivider from '../../modules/Dropdown/DropdownDivider';
55
import DropdownHeader from '../../modules/Dropdown/DropdownHeader';
6-
import DropdownItem from '../../modules/Dropdown/DropdownItem';
6+
import DropdownItem, { DropdownItemProps } from '../../modules/Dropdown/DropdownItem';
77
import DropdownMenu from '../../modules/Dropdown/DropdownMenu';
88

99
export interface SelectProps extends DropdownProps {
1010
[key: string]: any;
11+
12+
/** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */
13+
options: Array<DropdownItemProps>;
1114
}
1215

1316
interface SelectComponent extends React.StatelessComponent<SelectProps> {

src/addons/Select/Select.js

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import PropTypes from 'prop-types'
12
import React from 'react'
23

34
import Dropdown from '../../modules/Dropdown'
@@ -11,6 +12,11 @@ function Select(props) {
1112
return <Dropdown {...props} selection />
1213
}
1314

15+
Select.propTypes = {
16+
/** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */
17+
options: PropTypes.arrayOf(PropTypes.shape(Dropdown.Item.propTypes)).isRequired,
18+
}
19+
1420
Select.Divider = Dropdown.Divider
1521
Select.Header = Dropdown.Header
1622
Select.Item = Dropdown.Item

src/behaviors/Visibility/Visibility.js

+5
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ export default class Visibility extends Component {
203203
}
204204

205205
componentDidMount() {
206+
this.mounted = true
207+
206208
if (!isBrowser()) return
207209
const { context, fireOnMount, updateOn } = this.props
208210

@@ -216,6 +218,7 @@ export default class Visibility extends Component {
216218
const { context } = this.props
217219

218220
this.unattachHandlers(context)
221+
this.mounted = false
219222
}
220223

221224
attachHandlers(context, updateOn) {
@@ -300,6 +303,8 @@ export default class Visibility extends Component {
300303
}
301304

302305
update = () => {
306+
if (!this.mounted) return
307+
303308
this.ticking = false
304309

305310
this.oldCalculations = this.calculations

src/collections/Form/FormSelect.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22

33
import { SelectProps } from '../../addons/Select';
4+
import { DropdownItemProps } from '../../modules/Dropdown/DropdownItem';
45
import { FormFieldProps } from './FormField';
56

67
export interface FormSelectProps extends FormFieldProps, SelectProps {
@@ -11,6 +12,9 @@ export interface FormSelectProps extends FormFieldProps, SelectProps {
1112

1213
/** A FormField control prop. */
1314
control?: any;
15+
16+
/** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */
17+
options: Array<DropdownItemProps>;
1418
}
1519

1620
declare const FormSelect: React.StatelessComponent<FormSelectProps>;

src/collections/Form/FormSelect.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import PropTypes from 'prop-types'
12
import React from 'react'
23

34
import { customPropTypes, getElementType, getUnhandledProps } from '../../lib'
45
import Select from '../../addons/Select'
6+
import Dropdown from '../../modules/Dropdown'
57
import FormField from './FormField'
68

79
/**
@@ -10,11 +12,11 @@ import FormField from './FormField'
1012
* @see Select
1113
*/
1214
function FormSelect(props) {
13-
const { control } = props
15+
const { control, options } = props
1416
const rest = getUnhandledProps(FormSelect, props)
1517
const ElementType = getElementType(FormSelect, props)
1618

17-
return <ElementType {...rest} control={control} />
19+
return <ElementType {...rest} control={control} options={options} />
1820
}
1921

2022
FormSelect.propTypes = {
@@ -23,6 +25,9 @@ FormSelect.propTypes = {
2325

2426
/** A FormField control prop. */
2527
control: FormField.propTypes.control,
28+
29+
/** Array of Dropdown.Item props e.g. `{ text: '', value: '' }` */
30+
options: PropTypes.arrayOf(PropTypes.shape(Dropdown.Item.propTypes)).isRequired,
2631
}
2732

2833
FormSelect.defaultProps = {

src/modules/Embed/Embed.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@ export default class Embed extends Component {
8686

8787
static autoControlledProps = ['active']
8888

89-
static defaultProps = {
90-
icon: 'video play',
91-
}
92-
9389
getSrc() {
9490
const {
9591
autoplay = true,
@@ -145,9 +141,11 @@ export default class Embed extends Component {
145141
const rest = getUnhandledProps(Embed, this.props)
146142
const ElementType = getElementType(Embed, this.props)
147143

144+
const iconShorthand = icon !== undefined ? icon : 'video play'
145+
148146
return (
149147
<ElementType {...rest} className={classes} onClick={this.handleClick}>
150-
{Icon.create(icon, { autoGenerateKey: false })}
148+
{Icon.create(iconShorthand, { autoGenerateKey: false })}
151149
{placeholder && <img className='placeholder' src={placeholder} />}
152150
{this.renderEmbed()}
153151
</ElementType>

src/modules/Popup/Popup.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ export default class Popup extends Component {
146146

147147
state = {}
148148

149+
componentDidMount() {
150+
this.mounted = true
151+
}
152+
153+
componentWillUnmount() {
154+
this.mounted = false
155+
}
156+
149157
computePopupStyle(positions) {
150158
const style = { position: 'absolute' }
151159

@@ -295,7 +303,9 @@ export default class Popup extends Component {
295303
this.setState({ closed: true })
296304

297305
eventStack.unsub('scroll', this.hideOnScroll, { target: window })
298-
setTimeout(() => this.setState({ closed: false }), 50)
306+
setTimeout(() => {
307+
if (this.mounted) this.setState({ closed: false })
308+
}, 50)
299309

300310
this.handleClose(e)
301311
}

src/modules/Progress/Progress.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Progress extends Component {
111111
getPercent = () => {
112112
const { precision, progress, total, value } = this.props
113113
const percent = _.clamp(this.calculatePercent(), 0, 100)
114+
114115
if (!_.isUndefined(total) && !_.isUndefined(value) && progress === 'value') {
115116
return (value / total) * 100
116117
}
@@ -175,7 +176,7 @@ class Progress extends Component {
175176
)
176177
const rest = getUnhandledProps(Progress, this.props)
177178
const ElementType = getElementType(Progress, this.props)
178-
const percent = this.getPercent()
179+
const percent = this.getPercent() || 0
179180

180181
return (
181182
<ElementType {...rest} className={classes} data-percent={Math.floor(percent)}>

test/setup.js

+40-6
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import Adapter from 'enzyme-adapter-react-16'
99
import dirtyChai from 'dirty-chai'
1010
import sinonChai from 'sinon-chai'
1111

12-
//
12+
// ----------------------------------------
1313
// Enzyme
14-
//
14+
// ----------------------------------------
1515
global.enzyme = enzyme
1616
global.shallow = enzyme.shallow
1717
global.render = enzyme.render
@@ -22,18 +22,52 @@ enzyme.configure({
2222
disableLifecycleMethods: true,
2323
})
2424

25-
//
25+
// ----------------------------------------
2626
// Mocha
27-
//
27+
// ----------------------------------------
2828
mocha.setup({
2929
ui: 'bdd',
3030
})
3131

32-
//
32+
// ----------------------------------------
3333
// Chai
34-
//
34+
// ----------------------------------------
3535
global.expect = chai.expect
3636
chai.should()
3737
chai.use(chaiEnzyme())
3838
chai.use(dirtyChai)
3939
chai.use(sinonChai)
40+
41+
// ----------------------------------------
42+
// Console
43+
// ----------------------------------------
44+
// Fail on all activity.
45+
// It is important we overload console here, before consoleUtil.js is loaded and caches it.
46+
let log
47+
let info
48+
let warn
49+
let error
50+
51+
const throwOnConsole = method => (...args) => {
52+
throw new Error(`console.${method} should never be called but was called with:\n${args.join(' ')}`)
53+
}
54+
55+
/* eslint-disable no-console */
56+
beforeEach(() => {
57+
log = console.log
58+
info = console.info
59+
warn = console.warn
60+
error = console.error
61+
62+
console.log = throwOnConsole('log')
63+
console.info = throwOnConsole('info')
64+
console.warn = throwOnConsole('warn')
65+
console.error = throwOnConsole('error')
66+
})
67+
afterEach(() => {
68+
console.log = log
69+
console.info = info
70+
console.warn = warn
71+
console.error = error
72+
})
73+
/* eslint-enable no-console */

test/specs/addons/Select/Select-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const requiredProps = {
99
}
1010

1111
describe('Select', () => {
12-
common.isConformant(Select, requiredProps)
12+
common.isConformant(Select, { requiredProps })
1313
common.hasSubcomponents(Select, [Dropdown.Divider, Dropdown.Header, Dropdown.Item, Dropdown.Menu])
1414

1515
it('renders a selection Dropdown', () => {

test/specs/collections/Form/Form-test.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import FormSelect from 'src/collections/Form/FormSelect'
1414
import FormTextArea from 'src/collections/Form/FormTextArea'
1515
import { SUI } from 'src/lib'
1616
import * as common from 'test/specs/commonTests'
17-
import { sandbox } from 'test/utils'
17+
import { consoleUtil, sandbox } from 'test/utils'
1818

1919
describe('Form', () => {
2020
common.isConformant(Form)
@@ -62,12 +62,14 @@ describe('Form', () => {
6262

6363
describe('onSubmit', () => {
6464
it('prevents default on the event when there is no action', () => {
65+
// Heads up!
66+
// In this test we pass some invalid values to verify correct work.
67+
consoleUtil.disableOnce()
68+
6569
const event = { preventDefault: sandbox.spy() }
6670

6771
shallow(<Form />).simulate('submit', event)
68-
6972
shallow(<Form action={false} />).simulate('submit', event)
70-
7173
shallow(<Form action={null} />).simulate('submit', event)
7274

7375
event.preventDefault.should.have.been.calledThrice()

test/specs/collections/Form/FormSelect-test.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import Select from 'src/addons/Select/Select'
44
import FormSelect from 'src/collections/Form/FormSelect'
55
import * as common from 'test/specs/commonTests'
66

7+
const requiredProps = {
8+
options: [],
9+
}
10+
711
describe('FormSelect', () => {
8-
common.isConformant(FormSelect)
9-
common.labelImplementsHtmlForProp(FormSelect)
12+
common.isConformant(FormSelect, { requiredProps })
13+
common.labelImplementsHtmlForProp(FormSelect, { requiredProps })
1014

1115
it('renders a FormField with a Select control', () => {
12-
shallow(<FormSelect />)
16+
shallow(<FormSelect {...requiredProps} />)
1317
.find('FormField')
1418
.should.have.prop('control', Select)
1519
})

0 commit comments

Comments
 (0)