Skip to content

Commit c3bf628

Browse files
josiasdsaojinchawes13
authored
Migrate enzyme to testing library (#586)
* Set up React Testing Library * Migrate Spinner * convert button tests from enzyme to rtl * remove unused imports * remove unused import * Update jest and set test environment to jsdom * Bump version * update checkbox.test.js tests * checkbox.test.js and checkbox-group.test.js complete * update button.test.js to use userevent * update color-input.test.js to rtl * remove unused imports * update date input * complete color-input and date-input * complete dropdown-checkbox-group * complete hidden input * complete icon-input * Fix date input issues * RTL migration: controls (#602) * Migrate paginator * Add comment * Remove unused import * Migrate tab-bar * RTL migration: modal (#601) * Initial commit * Clean up logs * Add tests more default close interactions * RTL migration: indicators (#600) * Migrate loading container * Migrate flash message * Migrate flash message container * Upgdate assertion * Partial cleanup; Address code review * Remove unecessary test * Remove moment dependency from DateInput test * Update ColorPicker component * Update DropdownCheckboxGroup * Update HiddenInput * Update IconInput * Update Spinner component * Bump minor version * Address comments * Fix trigger on keys util * Migrate color-picker * RTL migration: tables (#603) * RTL migration: labels (#607) * partial label folder tests * Clean up error label * Fix input error tests * Remove unused attribute * Fix input label tests * Migrate labeled-field tests * Fixes --------- Co-authored-by: Alex Jin <alex@launchpadlab.com> * RTL migration: inputs (#604) * Migrate input * Migrate textarea * Remove unused import * Migrate switch * Remove unused import * Migrate range-input * Migrate select * Migrate masked input * Migrate replace empty string value hoc * Migrate blur dirty * Migrate radio group input * Migrate cloudinary uploader * Be more explicit * Use toHaveAttribute * Do not reference id directly * RTL migration: file inputs (#590) * Migrate FileInput to RTL * Migrate CloudinaryFileInput to RTL --------- Co-authored-by: Conor <conor@launchpadlab.com> * Remove enzyme * Add act back to file input * Avoid race conditions with act * Update lock * Add test for read helper * Mock server...better * Address uncovered line in wrap-display-name * Remove unused default * Add coverage for modal * Increase dropdown select coverage * Increase color picker coverage * Increase color-input coverage * Increase to-hex coverage * Increase paginator coverage * Increase masked input coverage * Increase date input coverage * Increase radio group coverage * Improve cloudinary-uploader coverage * Improve coverage for getEnvVar * Improve sortable table coverage * Improve tab-bar coverage * Update trigger on keys * Add comment * Replace act with waitFor --------- Co-authored-by: Alex Jin <alex@launchpadlab.com> Co-authored-by: Conor Hawes <conor@launchpadlab.com>
1 parent f5146b3 commit c3bf628

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2889
-1881
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ module.exports = {
66
node: false,
77
'shared-node-browser': true,
88
},
9+
globals: {
10+
File: true,
11+
},
912
}

docs.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,12 +1700,12 @@ Returns **[String][142]** String with namespace removed
17001700
### Parameters
17011701

17021702
* `fn` **[Function][143]** The function to trigger
1703-
* `keyCodes` **([Number][145] | [String][142] | [Array][146]<([Number][145] | [String][142])>)** Number, String, or Array of key codes
1703+
* `keys` **([String][142] | [Array][146]<[String][142]>)** String or Array of keys
17041704

17051705
### Examples
17061706

17071707
```javascript
1708-
const triggerOnEnter = triggerOnKeys(() => console.log('Hi'), [13])
1708+
const triggerOnEnter = triggerOnKeys(() => console.log('Hi'), ['Enter'])
17091709
function MyExample () { return <Example onKeyPress={triggerOnEnter} /> }
17101710
```
17111711

jest.config.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
module.exports = {
22
testEnvironment: 'jsdom',
3-
'setupFiles': [
4-
'./test/setup.js',
5-
],
6-
"watchPathIgnorePatterns": [
7-
"<rootDir>/node_modules",
8-
]
9-
}
3+
setupFilesAfterEnv: ['./test/setup.js'],
4+
watchPathIgnorePatterns: ['<rootDir>/node_modules'],
5+
}

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@launchpadlab/lp-components",
3-
"version": "9.0.0",
3+
"version": "9.1.0",
44
"engines": {
55
"node": "^18.12"
66
},
@@ -70,14 +70,16 @@
7070
"@storybook/builder-webpack5": "^6.5.14",
7171
"@storybook/manager-webpack5": "^6.5.14",
7272
"@storybook/react": "^6.4.22",
73-
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
73+
"@testing-library/dom": "^9.3.1",
74+
"@testing-library/jest-dom": "^5.16.5",
75+
"@testing-library/react": "^12.1.5",
76+
"@testing-library/user-event": "^14.4.3",
7477
"babel-loader": "^9.1.0",
7578
"bourbon": "^7.2.0",
7679
"bourbon-neat": "^4.0.0",
7780
"core-js": "^3.21.1",
7881
"css-loader": "^6.7.2",
7982
"documentation": "^14.0.2",
80-
"enzyme": "^3.2.0",
8183
"eslint": "^8.46.0",
8284
"husky": "^8.0.3",
8385
"jest": "^29.6.2",

src/controls/color-picker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function ColorPicker({
8080
}}
8181
/>
8282
{isExpanded && (
83-
<div className="popover">
83+
<div className="popover" role="dialog">
8484
<ChromePicker
8585
color={value}
8686
onChange={({ hex }) => onChange(hex)}

src/controls/paginator/page-link.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
33
import { noop, triggerOnKeys } from '../../utils'
44
import classnames from 'classnames'
55

6-
const ENTER_KEY_CODE = 13
6+
const ENTER_KEY = 'Enter'
77

88
const propTypes = {
99
className: PropTypes.string,
@@ -24,7 +24,7 @@ function PageLink({ className, active, onClick, children, ...rest }) {
2424
<a
2525
role="link"
2626
onClick={onClick}
27-
onKeyDown={triggerOnKeys(onClick, ENTER_KEY_CODE)} // keyboard interaction requirement
27+
onKeyDown={triggerOnKeys(onClick, ENTER_KEY)} // keyboard interaction requirement
2828
aria-current={active ? 'page' : false}
2929
tabIndex="0" // add back to natural tab order (automatically removed without an href)
3030
{...rest}

src/controls/tab-bar/focus.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,42 @@
1-
import { KeyCodes } from '../../utils'
1+
const Keys = {
2+
HOME: 'Home',
3+
END: 'End',
4+
LEFT: 'ArrowLeft',
5+
RIGHT: 'ArrowRight',
6+
UP: 'ArrowUp',
7+
DOWN: 'ArrowDown',
8+
}
29

3-
// Funnction that can be passed to event handlers (e.g., onKeyPress) that manages which element should be focused
10+
// Function that can be passed to event handlers (e.g., onKeyPress) that manages which element should be focused
411
// Note: Expected keyboard interaction with arrow keys changes depending on the orientation of the tab list
5-
function manageFocus(e, { vertical = false }) {
12+
function manageFocus(e, { vertical }) {
613
// If not activated while on a tab, then ignore
714
if (!isTabControl(e.target)) return
815

9-
const key = (e.which || e.keyCode || '').toString()
16+
// Key will be set to Unidentified if it cannot be mapped
17+
// Source: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#value
18+
const key = e.key === 'Unidentified' ? e.code : e.key
1019
switch (key) {
11-
case KeyCodes.DOWN: {
20+
case Keys.DOWN: {
1221
if (!vertical) return
1322
return focusNextControl(e)
1423
}
15-
case KeyCodes.UP: {
24+
case Keys.UP: {
1625
if (!vertical) return
1726
return focusPreviousControl(e)
1827
}
19-
case KeyCodes.LEFT: {
28+
case Keys.LEFT: {
2029
if (vertical) return
2130
return focusPreviousControl(e)
2231
}
23-
case KeyCodes.RIGHT: {
32+
case Keys.RIGHT: {
2433
if (vertical) return
2534
return focusNextControl(e)
2635
}
27-
case KeyCodes.HOME: {
36+
case Keys.HOME: {
2837
return focusFirstControl(e)
2938
}
30-
case KeyCodes.END: {
39+
case Keys.END: {
3140
return focusLastControl(e)
3241
}
3342
default:
@@ -81,6 +90,7 @@ function getAdjacentControl(control, { previous = false } = {}) {
8190

8291
// Recursively searches for the closest parent tab list
8392
function getClosestTabList(el) {
93+
/* istanbul ignore next */
8494
if (!el) return
8595
return el.matches('[role="tablist"]')
8696
? el

src/forms/inputs/cloudinary-file-input/cloudinary-uploader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const DEFAULT_REQUEST_OPTIONS = {
7878
const FILE_NAME_PATTERN = /[\s?&#\\%<>]/gi
7979

8080
// Throws an error when a required param is not found
81-
function requireParam(paramName, context = 'Error') {
81+
function requireParam(paramName, context) {
8282
throw new Error(`${context}: required param ${paramName} not provided`)
8383
}
8484

src/forms/inputs/icon-input.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function IconInput(props) {
4848
className: classnames('icon-label', className),
4949
}}
5050
>
51-
<i className={`${icon}-icon`} />
51+
<i className={`${icon}-icon`} data-testid="icon" />
5252
</Input>
5353
)
5454
}

src/indicators/flash-message-container.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ function FlashMessageContainer({ messages, limit, onDismiss, ...rest }) {
4747
return (
4848
<FlashMessage
4949
key={message.id}
50-
message={message}
5150
isError={message.isError}
5251
{...rest}
5352
{...message.props}

src/indicators/spinner.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
3-
import { filterInvalidDOMProps } from '../utils'
43
import classnames from 'classnames'
4+
import { filterInvalidDOMProps } from '../utils'
55

66
/**
77
*
@@ -38,6 +38,7 @@ const defaultProps = {
3838
function Spinner({ className, ...rest }) {
3939
return (
4040
<div
41+
role="progressbar"
4142
className={classnames('spinner', className)}
4243
{...filterInvalidDOMProps(rest)}
4344
/>

src/tables/helpers/get-column-data.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { castArray, has } from '../../utils'
22

33
// Get column info from children via props
4-
function getColumnData(children = [], doDisable) {
4+
function getColumnData(children, doDisable) {
55
const childrenArray = castArray(children)
66
return childrenArray
77
.filter((child) => has(child, 'props'))

src/tables/sortable-table.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,13 @@ function SortableTable({
150150
setSortFunc(() => newSortFunc)
151151
setValueGetter(() => newValueGetter)
152152

153-
if (onChange)
153+
if (onChange) {
154154
onChange({
155155
ascending: newAscending,
156156
sortPath: newSortPath,
157157
sortFunc: newSortFunc,
158158
})
159+
}
159160
}
160161

161162
return (

src/utils/local/key-codes.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// e.which and e.keyCode are deprecated
2+
// This export will be removed in next major release
3+
// Source: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
14
const KEY_CODES = {
25
DOWN: '40',
36
UP: '38',

src/utils/local/trigger-on-keys.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
* @name triggerOnKeys
33
* @type Function
44
* @param {Function} fn - The function to trigger
5-
* @param {Number|String|Array<Number|String>} keyCodes - Number, String, or Array of key codes
5+
* @param {String|Array<String>} keys - String or Array of keys
66
* @returns {Function} - Returns a function that takes an event and watches for keys
77
*
88
* @example
99
*
10-
* const triggerOnEnter = triggerOnKeys(() => console.log('Hi'), [13])
10+
* const triggerOnEnter = triggerOnKeys(() => console.log('Hi'), ['Enter'])
1111
* function MyExample () { return <Example onKeyPress={triggerOnEnter} /> }
1212
*/
1313

@@ -16,7 +16,9 @@ import { castArray, compact } from 'lodash'
1616
function triggerOnKeys(fn, keyCodes) {
1717
const codes = compact(castArray(keyCodes))
1818
return function (e) {
19-
const key = e.which || e.keyCode
19+
// Key will be set to Unidentified if it cannot be mapped
20+
// Source: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#value
21+
const key = e.key === 'Unidentified' ? e.code : e.key
2022
if (!codes.some((keyCode) => keyCode == key)) return
2123

2224
return fn(e)

test/controls/color-picker.test.js

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,50 @@
11
import React from 'react'
2-
import { mount } from 'enzyme'
2+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react'
3+
import userEvent from '@testing-library/user-event'
34
import { ColorPicker } from '../../src/'
45

5-
test('ColorPicker toggles expanded when swatch is clicked', () => {
6-
const wrapper = mount(<ColorPicker />)
7-
expect(wrapper.find('.popover').exists()).toBe(false)
8-
wrapper.find('.swatch').simulate('click')
9-
expect(wrapper.find('.popover').exists()).toBe(true)
10-
wrapper.find('.swatch').simulate('click')
11-
expect(wrapper.find('.popover').exists()).toBe(false)
6+
test('ColorPicker toggles expanded when swatch is clicked', async () => {
7+
const user = userEvent.setup()
8+
render(<ColorPicker />)
9+
const swatchControl = screen.getByRole('button')
10+
11+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
12+
await user.click(swatchControl)
13+
expect(screen.queryByRole('dialog')).toBeInTheDocument()
14+
await user.click(swatchControl)
15+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
1216
})
1317

1418
test('ColorPicker can be externally controlled', () => {
15-
const wrapper = mount(<ColorPicker active={true} />)
16-
expect(wrapper.find('.popover').exists()).toBe(true)
17-
wrapper.setProps({ active: false })
18-
expect(wrapper.find('.popover').exists()).toBe(false)
19+
const { rerender } = render(<ColorPicker active={true} />)
20+
21+
expect(screen.queryByRole('dialog')).toBeInTheDocument()
22+
rerender(<ColorPicker active={false} />)
23+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
1924
})
25+
26+
test('ColorPicker closes when a click is registered outside', async () => {
27+
const user = userEvent.setup()
28+
const { container } = render(<ColorPicker />)
29+
const swatchControl = screen.getByRole('button')
30+
31+
await user.click(swatchControl)
32+
expect(screen.queryByRole('dialog')).toBeInTheDocument()
33+
34+
user.click(container)
35+
await waitForElementToBeRemoved(screen.queryByRole('dialog'))
36+
})
37+
38+
test('ColorPicker calls on change with a hex value', async () => {
39+
const user = userEvent.setup()
40+
const mock = jest.fn()
41+
render(<ColorPicker onChange={mock} />)
42+
const swatchControl = screen.getByRole('button')
43+
44+
await user.click(swatchControl)
45+
const input = screen.getByRole('textbox')
46+
await user.clear(input)
47+
await user.type(input, '639')
48+
49+
expect(mock).toHaveBeenCalledWith('#663399')
50+
})

0 commit comments

Comments
 (0)