test: set up component snapshot testing (#2102)

* set up snapshot testing

* improvements

* add tests for TextInput as an example

* Fix code style issues with ESLint

* add comment to custom-test-env file

* only set up needed providers

* include style rules in snapshots

* disable redux storage warning

* added setupTests to avoid boilerplate

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
This commit is contained in:
Justin Domingue 2021-07-26 10:41:08 -07:00 committed by GitHub
parent 718003b6f2
commit 8dfd143208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 384 additions and 4 deletions

16
custom-test-env.js Normal file

@ -0,0 +1,16 @@
// Custom test environment to provide `TextEncoder`/`TextDecoder`
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Environment = require('jest-environment-jsdom')
module.exports = class CustomTestEnvironment extends Environment {
async setup() {
await super.setup()
if (typeof this.global.TextEncoder === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { TextEncoder, TextDecoder } = require('util')
this.global.TextEncoder = TextEncoder
this.global.TextDecoder = TextDecoder
}
}
}

@ -20,6 +20,8 @@
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@typechain/ethers-v5": "^7.0.0",
"@types/d3": "^6.7.1",
"@types/jest": "^25.2.1",
@ -77,6 +79,7 @@
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"inter-ui": "^3.13.1",
"jest-styled-components": "^7.0.5",
"lodash.flatmap": "^4.5.0",
"luxon": "^1.25.0",
"ms.macro": "^2.0.0",
@ -134,7 +137,7 @@
"graphql:generate": "graphql-codegen --config codegen.yml",
"postinstall": "yarn compile-contract-types",
"start": "yarn compile-contract-types && react-scripts start",
"test": "react-scripts test --env=jsdom",
"test": "react-scripts test --env=./custom-test-env.js",
"prestart": "yarn graphql:generate && touch src/locales/en-US.po"
},
"eslintConfig": {

@ -0,0 +1,130 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ResizableTextArea renders correctly 1`] = `
<DocumentFragment>
.c0 {
font-size: 12;
outline: none;
border: none;
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
width: 0;
resize: none;
background-color: #F7F8FA;
-webkit-transition: color 300ms step-start;
transition: color 300ms step-start;
color: #000000;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
width: 100%;
line-height: 1.2;
padding: 0px;
-webkit-appearance: textfield;
}
.c0::-webkit-search-decoration {
-webkit-appearance: none;
}
.c0::-webkit-outer-spin-button,
.c0::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.c0::-webkit-input-placeholder {
color: #C3C5CB;
}
.c0::-moz-placeholder {
color: #C3C5CB;
}
.c0:-ms-input-placeholder {
color: #C3C5CB;
}
.c0::placeholder {
color: #C3C5CB;
}
<textarea
autocapitalize="off"
autocomplete="off"
autocorrect="off"
class="c0 testing"
font-size="12"
placeholder="Test Placeholder"
spellcheck="false"
style="height: auto; min-height: 500px;"
>
My test input
</textarea>
</DocumentFragment>
`;
exports[`TextInput renders correctly 1`] = `
<DocumentFragment>
.c0 {
font-size: 12;
outline: none;
border: none;
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
width: 0;
background-color: #F7F8FA;
-webkit-transition: color 300ms step-start;
transition: color 300ms step-start;
color: #000000;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
width: 100%;
padding: 0px;
-webkit-appearance: textfield;
}
.c0::-webkit-search-decoration {
-webkit-appearance: none;
}
.c0::-webkit-outer-spin-button,
.c0::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.c0::-webkit-input-placeholder {
color: #C3C5CB;
}
.c0::-moz-placeholder {
color: #C3C5CB;
}
.c0:-ms-input-placeholder {
color: #C3C5CB;
}
.c0::placeholder {
color: #C3C5CB;
}
<div
class="testing"
>
<input
autocapitalize="off"
autocomplete="off"
autocorrect="off"
class="c0"
font-size="12"
placeholder="Test Placeholder"
spellcheck="false"
type="text"
value="My test input"
/>
</div>
</DocumentFragment>
`;

@ -0,0 +1,68 @@
import { TextInput, ResizingTextArea } from './'
import { render, screen, fireEvent } from 'test-utils'
describe('TextInput', () => {
it('renders correctly', () => {
const { asFragment } = render(
<TextInput
className="testing"
value="My test input"
onUserInput={() => null}
placeholder="Test Placeholder"
fontSize="12"
/>
)
expect(asFragment()).toMatchSnapshot()
})
it('calls the handler on user input', () => {
const onUserInputSpy = jest.fn()
render(
<TextInput
className="testing"
value=""
onUserInput={onUserInputSpy}
placeholder="Test Placeholder"
fontSize="12"
/>
)
fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } })
expect(onUserInputSpy).toHaveBeenCalledWith('New value')
expect(onUserInputSpy).toHaveBeenCalledTimes(1)
})
})
describe('ResizableTextArea', () => {
it('renders correctly', () => {
const { asFragment } = render(
<ResizingTextArea
className="testing"
value="My test input"
onUserInput={() => null}
placeholder="Test Placeholder"
fontSize="12"
/>
)
expect(asFragment()).toMatchSnapshot()
})
it('calls the handler on user input', () => {
const onUserInputSpy = jest.fn()
render(
<ResizingTextArea
className="testing"
value=""
onUserInput={onUserInputSpy}
placeholder="Test Placeholder"
fontSize="12"
/>
)
fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } })
expect(onUserInputSpy).toHaveBeenCalledWith('New value')
expect(onUserInputSpy).toHaveBeenCalledTimes(1)
})
})

5
src/setupTests.ts Normal file

@ -0,0 +1,5 @@
// jest custom assertions
import '@testing-library/jest-dom'
// include style rules in snapshots
import 'jest-styled-components'

@ -39,7 +39,7 @@ const store = configureStore({
.concat(dataApi.middleware)
.concat(routingApi.middleware)
.concat(save({ states: PERSISTED_KEYS, debounce: 1000 })),
preloadedState: load({ states: PERSISTED_KEYS }),
preloadedState: load({ states: PERSISTED_KEYS, disableWarnings: process.env.NODE_ENV === 'test' }),
})
store.dispatch(updateVersion())

19
src/test-utils.tsx Normal file

@ -0,0 +1,19 @@
import React, { FC, ReactElement, ReactNode } from 'react'
import { render, RenderOptions } from '@testing-library/react'
import ThemeProvider from 'theme'
import store from 'state'
import { Provider } from 'react-redux'
const WithProviders: FC = ({ children }: { children?: ReactNode }) => {
return (
<Provider store={store}>
<ThemeProvider>{children}</ThemeProvider>
</Provider>
)
}
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) =>
render(ui, { wrapper: WithProviders, ...options })
export * from '@testing-library/react'
export { customRender as render }

143
yarn.lock

@ -2461,6 +2461,17 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
"@jest/types@^27.0.6":
version "27.0.6"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b"
integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@jimp/bmp@^0.16.1":
version "0.16.1"
resolved "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.1.tgz"
@ -3181,6 +3192,43 @@
lz-string "^1.4.4"
pretty-format "^26.6.2"
"@testing-library/dom@^8.0.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.1.0.tgz#f8358b1883844ea569ba76b7e94582168df5370d"
integrity sha512-kmW9alndr19qd6DABzQ978zKQ+J65gU2Rzkl8hriIetPnwpesRaK4//jEQyYh8fEALmGhomD/LBQqt+o+DL95Q==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
"@types/aria-query" "^4.2.0"
aria-query "^4.2.2"
chalk "^4.1.0"
dom-accessibility-api "^0.5.6"
lz-string "^1.4.4"
pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.14.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.14.1.tgz#8501e16f1e55a55d675fe73eecee32cdaddb9766"
integrity sha512-dfB7HVIgTNCxH22M1+KU6viG5of2ldoA5ly8Ar8xkezKHKXjRvznCdbMbqjYGgO2xjRbwnR+rR8MLUIqF3kKbQ==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
aria-query "^4.2.2"
chalk "^3.0.0"
css "^3.0.0"
css.escape "^1.5.1"
dom-accessibility-api "^0.5.6"
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react@^12.0.0":
version "12.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.0.0.tgz#9aeb2264521522ab9b68f519eaf15136148f164a"
integrity sha512-sh3jhFgEshFyJ/0IxGltRhwZv2kFKfJ3fN1vTZ6hhMXzz9ZbbcTgmDYM4e+zJv+oiVKKEWZPyqPAh4MQBI65gA==
dependencies:
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.0.0"
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@ -3536,6 +3584,14 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@*":
version "26.0.24"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a"
integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==
dependencies:
jest-diff "^26.0.0"
pretty-format "^26.0.0"
"@types/jest@^25.2.1":
version "25.2.3"
resolved "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz"
@ -3825,6 +3881,13 @@
"@testing-library/dom" "^7.11.0"
cypress "*"
"@types/testing-library__jest-dom@^5.9.1":
version "5.14.1"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.1.tgz#014162a5cee6571819d48e999980694e2f657c3c"
integrity sha512-Gk9vaXfbzc5zCXI9eYE9BI5BNHEp4D3FWjgqBE/ePGYElLAP+KvxBcsdkwfIVvezs605oiyd/VrpiHe3Oeg+Aw==
dependencies:
"@types/jest" "*"
"@types/ua-parser-js@^0.7.35":
version "0.7.36"
resolved "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz"
@ -3887,6 +3950,13 @@
dependencies:
"@types/yargs-parser" "*"
"@types/yargs@^16.0.0":
version "16.0.4"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977"
integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
dependencies:
"@types/yargs-parser" "*"
"@types/yauzl@^2.9.1":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a"
@ -4747,6 +4817,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
any-base@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz"
@ -7475,6 +7550,11 @@ css-what@^5.0.0:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
css@^2.0.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@ -7485,6 +7565,15 @@ css@^2.0.0:
source-map-resolve "^0.5.2"
urix "^0.1.0"
css@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
dependencies:
inherits "^2.0.4"
source-map "^0.6.1"
source-map-resolve "^0.6.0"
cssdb@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
@ -8315,6 +8404,11 @@ dom-accessibility-api@^0.5.4:
resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz"
integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==
dom-accessibility-api@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9"
integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==
dom-converter@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@ -11762,7 +11856,7 @@ jest-diff@^25.2.1:
jest-get-type "^25.2.6"
pretty-format "^25.5.0"
jest-diff@^26.6.2:
jest-diff@^26.0.0, jest-diff@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
@ -12047,6 +12141,13 @@ jest-snapshot@^26.6.0, jest-snapshot@^26.6.2:
pretty-format "^26.6.2"
semver "^7.3.2"
jest-styled-components@^7.0.5:
version "7.0.5"
resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-7.0.5.tgz#6da3f1a1c8bd98bccc6bcf9aabfdefdcd88fd92c"
integrity sha512-ZR/r3IKNkgaaVIOThn0Qis4sNQtA352qHjhbxSHeLS3FDIvHSUSJoI2b3kzk+bHHQ1VOeV630usERtnyhyZh4A==
dependencies:
css "^3.0.0"
jest-util@^26.6.0, jest-util@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1"
@ -13226,6 +13327,11 @@ min-document@^2.19.0:
dependencies:
dom-walk "^0.1.0"
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
@ -15163,7 +15269,7 @@ pretty-format@^25.2.1, pretty-format@^25.5.0:
ansi-styles "^4.0.0"
react-is "^16.12.0"
pretty-format@^26.6.0, pretty-format@^26.6.2:
pretty-format@^26.0.0, pretty-format@^26.6.0, pretty-format@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
@ -15173,6 +15279,16 @@ pretty-format@^26.6.0, pretty-format@^26.6.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
pretty-format@^27.0.2:
version "27.0.6"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f"
integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==
dependencies:
"@jest/types" "^27.0.6"
ansi-regex "^5.0.0"
ansi-styles "^5.0.0"
react-is "^17.0.1"
private@^0.1.6, private@^0.1.8:
version "0.1.8"
resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz"
@ -15895,6 +16011,14 @@ recursive-readdir@2.2.2:
dependencies:
minimatch "3.0.4"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
dependencies:
indent-string "^4.0.0"
strip-indent "^3.0.0"
redux-devtools-extension@^2.13.9:
version "2.13.9"
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz#6b764e8028b507adcb75a1cae790f71e6be08ae7"
@ -17045,6 +17169,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-resolve@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
dependencies:
atob "^2.1.2"
decode-uri-component "^0.2.0"
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz"
@ -17474,6 +17606,13 @@ strip-hex-prefix@1.0.0:
dependencies:
is-hex-prefixed "1.0.0"
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
dependencies:
min-indent "^1.0.0"
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"