你如何用 Jest 测试 less CSS 的功能?
How do you test the functionality of less CSS with Jest?
我最近加入了一个新组织,他们使用了很多 CSS 到 hide/show 元素。
首先,这是好的做法吗?我一直(在大多数情况下)使用布尔值显示和隐藏组件,以在 dom 中添加或删除它(这也很容易测试)
在尝试使用 @testing-library/react
添加测试时,我发现使用 identity-obj-proxy
模块可以看到 类。
然而,当试图测试一个元素是否可见的功能时,它变得困难,因为我认为 less 代码没有被编译。
是否可以编译更少的代码以便在测试中反映出来?
会不会是和使用的classnames模块有关?
未通过测试
it('should open and close when clicked', async () => {
render(
<Collapse
label="Collapse"
testId="collapse-test"
isHidden
>
<div>
<h1>just some demo text</h1>
</div>
</Collapse>
)
const content = screen.getByTestId('collapse-test-content')
expect(content).not.toBeVisible()
userEvent.click(screen.getByTestId('collapse-test-button'))
await waitFor(() => expect(content).toBeVisible())
})
====================result====================
expect(element).not.toBeVisible()
Received element is visible:
<div aria-expanded="false" class="accordionContent contentHidden" data-testid="collapse-test-content" />
38 | )
39 | const content = screen.getByTestId('collapse-test-content')
> 40 | expect(content).not.toBeVisible()
| ^
41 | userEvent.click(screen.getByTestId('collapse-test-button'))
42 | await waitFor(() => expect(content).toBeVisible())
43 | })
组件
import React, { useState } from 'react'
import cn from 'classnames'
import styles from './styles.less'
const AccordionContent = ({ children, hidden, testId }) => {
const displayClass = hidden ? styles.contentHidden : styles.contentBlock
const accordionContentClass = cn(styles.accordionContent, displayClass)
return (
<div
className={ accordionContentClass }
aria-expanded={ !hidden }
data-testid={ `${testId}-content` }
>
{children}
</div>
)
}
const CollapseComponent= ({
isHidden,
onClick,
label,
children,
testId
}) => {
const [hidden, toggleHidden] = useState(isHidden)
const handleOnpress = () => {
toggleHidden((curr) => !curr)
if (onClick) { onClick }
}
return (
<div
className={ styles.accordionWrapper }
data-testid={ testId }
>
<AccordionButton
onPress={ handleOnpress }
buttonLabel={ label }
testId={ testId }
/>
<AccordionContent
hidden={ !!hidden }
testId={ testId }
>
{children}
</AccordionContent>
</div>
)
}
styles.less
.accordion-content {
background-color: @preservica-gray-1;
display: flex;
}
.content-hidden {
display: none;
}
.content-block {
display: flex;
}
jest.config
const config = {
testEnvironment: 'jsdom',
coverageThreshold: {
global: {
statements: 80,
branches: 75,
functions: 75,
lines: 80
}
},
testPathIgnorePatterns: [
"./src/components/atoms/Icons",
"./src/models"
],
coveragePathIgnorePatterns: [
"./src/components/atoms/Icons",
"./src/models"
],
setupFilesAfterEnv: [
"<rootDir>/src/setupTests.ts"
],
moduleNameMapper: {
"\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\.(css|less)$": "identity-obj-proxy",
"^@root(.*)$": "<rootDir>/src",
"^common(.*)$": "<rootDir>/src/common",
"^translation(.*)$": "<rootDir>/src/translation",
"^view(.*)$": "<rootDir>/src/view",
"^actions(.*)$": "<rootDir>/src/actions",
"^usecases(.*)$": "<rootDir>/src/usecases",
"^repository(.*)$": "<rootDir>/src/repository",
"^models(.*)$": "<rootDir>/src/models",
"^router(.*)$": "<rootDir>/src/router",
},
transform: {
"^.+\.(ts|tsx|js|jsx)$": "ts-jest",
},
snapshotSerializers: [
"enzyme-to-json/serializer"
]
}
您可以在 Jest docs. You could perhaps write your own module name mapper or custom transform to load and process the Less files. However, you'd have to figure out how to actually inject the CSS into the code under test (that's something that Webpack normally handles). Something like jest-transform-css 中阅读更多关于 Jest 如何处理模拟 CSS 模块的信息。
就个人而言,我只是测试 CSS class 是否存在,就像@jonrsharpe 建议的那样。从 test pyramid 的角度考虑:您的 Jest 测试可能应该集中在单元测试级别,强调速度和简单性。理想情况下,单元测试足够快,以至于您可以 运行 几乎立即进行它们,无论何时保存文件;增加解析和插入 Less CSS 的复杂性可能会产生不利影响。
如果单元测试不测试整个堆栈也没关系;你有其他测试,在金字塔更高的地方,来做到这一点。例如,您可以在实际浏览器中对您的应用程序进行一些 Cypress 测试 运行 并验证几个控件实际上是隐藏的,那么可以安全地假设 (1) Jest验证所有控件设置正确的 class 加上 (2) 赛普拉斯验证一些具有正确 class 的控件被正确隐藏意味着 (3) 所有控件都被正确隐藏。
为了使您的测试更加 self-documenting,并在您更改控件的显示和隐藏方式时使它们更易于维护,您可以使用 Jest 的 expect.extend 来制作您自己的匹配器。也许是这样的(未经测试):
expect.extend({
toBeVisibleViaCss(received) {
const pass = !received.classList.contains('content-hidden');
const what = pass ? 'not visible' : 'visible';
return {
message: () => `expected ${received} to be ${what}`,
pass,
};
},
});
First of all, is this good practice? I have always (in most cases) shown and hidden components using a boolean to add or remove it from the dom (this is also easy to test).
通过CSS隐藏组件当然不是我习惯的。在不知道您的代码库的情况下,我想知道开发人员是否习惯了以前的 jQuery-style 通过操纵 class 列表来隐藏的方法。我知道保持组件始终呈现的主要优点是您可以根据需要为它们的过渡设置动画。我不确定性能如何比较;浏览器可能会发现切换 CSS class 比添加或删除元素更快,但是删除元素意味着 React 需要渲染的内容更少,这有助于提高性能。
我最近加入了一个新组织,他们使用了很多 CSS 到 hide/show 元素。
首先,这是好的做法吗?我一直(在大多数情况下)使用布尔值显示和隐藏组件,以在 dom 中添加或删除它(这也很容易测试)
在尝试使用 @testing-library/react
添加测试时,我发现使用 identity-obj-proxy
模块可以看到 类。
然而,当试图测试一个元素是否可见的功能时,它变得困难,因为我认为 less 代码没有被编译。
是否可以编译更少的代码以便在测试中反映出来?
会不会是和使用的classnames模块有关?
未通过测试
it('should open and close when clicked', async () => {
render(
<Collapse
label="Collapse"
testId="collapse-test"
isHidden
>
<div>
<h1>just some demo text</h1>
</div>
</Collapse>
)
const content = screen.getByTestId('collapse-test-content')
expect(content).not.toBeVisible()
userEvent.click(screen.getByTestId('collapse-test-button'))
await waitFor(() => expect(content).toBeVisible())
})
====================result====================
expect(element).not.toBeVisible()
Received element is visible:
<div aria-expanded="false" class="accordionContent contentHidden" data-testid="collapse-test-content" />
38 | )
39 | const content = screen.getByTestId('collapse-test-content')
> 40 | expect(content).not.toBeVisible()
| ^
41 | userEvent.click(screen.getByTestId('collapse-test-button'))
42 | await waitFor(() => expect(content).toBeVisible())
43 | })
组件
import React, { useState } from 'react'
import cn from 'classnames'
import styles from './styles.less'
const AccordionContent = ({ children, hidden, testId }) => {
const displayClass = hidden ? styles.contentHidden : styles.contentBlock
const accordionContentClass = cn(styles.accordionContent, displayClass)
return (
<div
className={ accordionContentClass }
aria-expanded={ !hidden }
data-testid={ `${testId}-content` }
>
{children}
</div>
)
}
const CollapseComponent= ({
isHidden,
onClick,
label,
children,
testId
}) => {
const [hidden, toggleHidden] = useState(isHidden)
const handleOnpress = () => {
toggleHidden((curr) => !curr)
if (onClick) { onClick }
}
return (
<div
className={ styles.accordionWrapper }
data-testid={ testId }
>
<AccordionButton
onPress={ handleOnpress }
buttonLabel={ label }
testId={ testId }
/>
<AccordionContent
hidden={ !!hidden }
testId={ testId }
>
{children}
</AccordionContent>
</div>
)
}
styles.less
.accordion-content {
background-color: @preservica-gray-1;
display: flex;
}
.content-hidden {
display: none;
}
.content-block {
display: flex;
}
jest.config
const config = {
testEnvironment: 'jsdom',
coverageThreshold: {
global: {
statements: 80,
branches: 75,
functions: 75,
lines: 80
}
},
testPathIgnorePatterns: [
"./src/components/atoms/Icons",
"./src/models"
],
coveragePathIgnorePatterns: [
"./src/components/atoms/Icons",
"./src/models"
],
setupFilesAfterEnv: [
"<rootDir>/src/setupTests.ts"
],
moduleNameMapper: {
"\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\.(css|less)$": "identity-obj-proxy",
"^@root(.*)$": "<rootDir>/src",
"^common(.*)$": "<rootDir>/src/common",
"^translation(.*)$": "<rootDir>/src/translation",
"^view(.*)$": "<rootDir>/src/view",
"^actions(.*)$": "<rootDir>/src/actions",
"^usecases(.*)$": "<rootDir>/src/usecases",
"^repository(.*)$": "<rootDir>/src/repository",
"^models(.*)$": "<rootDir>/src/models",
"^router(.*)$": "<rootDir>/src/router",
},
transform: {
"^.+\.(ts|tsx|js|jsx)$": "ts-jest",
},
snapshotSerializers: [
"enzyme-to-json/serializer"
]
}
您可以在 Jest docs. You could perhaps write your own module name mapper or custom transform to load and process the Less files. However, you'd have to figure out how to actually inject the CSS into the code under test (that's something that Webpack normally handles). Something like jest-transform-css 中阅读更多关于 Jest 如何处理模拟 CSS 模块的信息。
就个人而言,我只是测试 CSS class 是否存在,就像@jonrsharpe 建议的那样。从 test pyramid 的角度考虑:您的 Jest 测试可能应该集中在单元测试级别,强调速度和简单性。理想情况下,单元测试足够快,以至于您可以 运行 几乎立即进行它们,无论何时保存文件;增加解析和插入 Less CSS 的复杂性可能会产生不利影响。
如果单元测试不测试整个堆栈也没关系;你有其他测试,在金字塔更高的地方,来做到这一点。例如,您可以在实际浏览器中对您的应用程序进行一些 Cypress 测试 运行 并验证几个控件实际上是隐藏的,那么可以安全地假设 (1) Jest验证所有控件设置正确的 class 加上 (2) 赛普拉斯验证一些具有正确 class 的控件被正确隐藏意味着 (3) 所有控件都被正确隐藏。
为了使您的测试更加 self-documenting,并在您更改控件的显示和隐藏方式时使它们更易于维护,您可以使用 Jest 的 expect.extend 来制作您自己的匹配器。也许是这样的(未经测试):
expect.extend({
toBeVisibleViaCss(received) {
const pass = !received.classList.contains('content-hidden');
const what = pass ? 'not visible' : 'visible';
return {
message: () => `expected ${received} to be ${what}`,
pass,
};
},
});
First of all, is this good practice? I have always (in most cases) shown and hidden components using a boolean to add or remove it from the dom (this is also easy to test).
通过CSS隐藏组件当然不是我习惯的。在不知道您的代码库的情况下,我想知道开发人员是否习惯了以前的 jQuery-style 通过操纵 class 列表来隐藏的方法。我知道保持组件始终呈现的主要优点是您可以根据需要为它们的过渡设置动画。我不确定性能如何比较;浏览器可能会发现切换 CSS class 比添加或删除元素更快,但是删除元素意味着 React 需要渲染的内容更少,这有助于提高性能。