使用Webpack等搭建一个适用于React项目的脚手架(3 – Eslint、Jest)

微信扫一扫,分享到朋友圈

使用Webpack等搭建一个适用于React项目的脚手架(3 – Eslint、Jest)

《使用Webpack等搭建一个适用于React项目的脚手架(2 – React Router、Redux、Sass)》
中记录了在项目中使用React-Router、Redux和Sass,以及引入图片。这篇文章主要记录在项目中使用Eslint检查代码风格以及使用Jest等进行单元测试。

使用Eslint

Eslint
是用来让代码风格保持一致以及减少错误发生的。

Eslint官网推荐React检查使用 eslint-plugin-react

这篇官方文档
中得知,需要使用 @typescript-eslint/parser
进行TypeScript检查。在@typescript-eslint/parser的Github仓库中找到 检查TypeScript的方法

本文按照上面提到的这些参考路径进行Eslint配置。(其实在安装好eslint之后,可以用 npx eslint --init
创建一个配置。执行 npx eslint --init
后会有一些提问,Eslint会根据回答创建配置文件并安装依赖。)

首先安装依赖:

npm i --save-dev eslint eslint-loader eslint-plugin-react
npm i --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
复制代码

创建配置文件: touch .eslintrc.js
。.eslintrc.js文件内容如下:

module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
],
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended'
],
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint/eslint-plugin',
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-use-before-define': 'off'
},
}
],
settings: {
react: {
version: 'detect',
}
},
rules: {
'react/jsx-no-undef': 'off'
},
env: {
browser: true,
es2020: true,
node: true,
commonjs: true,
},
globals: {
React: true,
ReduxConnect: true,
Axios: true,
UseEffect: true,
},
ignorePatterns: ['dist/'],
}
复制代码

eslint:recommended
表示启用 Eslint推荐规则
plugin:react/recommended
启用React推荐规则。

overrides
中进行配置,只对.ts文件和.tsx文件进行TypeScript相关的处理,比如只对.ts、.tsx文件使用 @typescript-eslint/parser
处理器。

设置 react: { version: 'detect' }
表示自动设置检查的React版本是安装的React的版本。

env
中的配置表示支持browser、es2020、node、commonjs中预定义的全局变量。

globals
中配置之前的步骤中设置的全局变量,比如React。

ignorePatterns
配置忽略的文件,数组的每一个元素相当于.eslintignore中的一行,.eslintignore能覆盖ignorePatterns的配置。Eslint默认会忽略/node_modules/* 和 /bower_components/*文件目录,所以没在ignorePatterns中添加这两个目录。

根据需要关掉了几条规则:

1.关掉在React中不允许未声明变量的规则,因为我们全局设置了React变量,但是Eslint会报错 error 'React' is not defined react/jsx-no-undef
,所以把这个规则关掉。

'react/jsx-no-undef': 'off'
复制代码

2.关掉不能在声明前使用的规则,还是因为已经在Webpack中定义了全局变量React,但是Eslint无法进行判断,所以会报错 'React' was used before it was defined

'@typescript-eslint/no-use-before-define': 'off'
复制代码

3.关掉不能使用 // @ts-ignore
的规则:

'@typescript-eslint/ban-ts-ignore': 'off',
复制代码

4.关掉不能使用any定义类型的规则(当然尽量不要使用any定义类型):

'@typescript-eslint/no-explicit-any': 'off',
复制代码

package.json
中添加:

"scripts": {
...
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
复制代码

执行 npm run lint
,会看见检查的结果,根据提示调整代码。

在Webpack( config/webpack.dev.js
)中配置如下,表示在对.js、.jsx、.ts、.tsx文件进行处理前先使用 eslint-loader
对文件进行Eslint检查。

{
enforce: 'pre',
test: /(.js(x?))|(.ts(x?))$/,
exclude: /[\/]node_modules[\/]/,
loader: 'eslint-loader',
},
复制代码

执行 npm start
启动webpack-dev-server,当文件修改并保存之后会重新打包,在打包前会进行Eslint检查,可以根据检查结果进行代码调整。

使用Jest

Jest
是一个专注于简化的JavaScript测试框架。本文参考 Jest官方文档–使用React
react-testing-library–基础例子
进行了简单的DOM测试。React Redux测试和React Router测试等可以查看 react-testing-library–更多例子

安装依赖:

npm i --save-dev jest @testing-library/react
复制代码

根据 Jest官方文档–使用Babel
的内容,安装Jest的时候自动安装了 babel-jest
babel-jest
会根据babel配置自动转换文件,( 《使用Webpack等搭建一个适用于React项目的脚手架(1 – React、TypeScript)》中已经在.babelrc.js中进行了babel配置,所以这里不用再考虑如何处理tsx文件
)。

在项目根目录创建一个 __tests__
文件夹。 __tests__
文件夹下包含测试文件,创建了3个文件夹分别表示测试组件、测试页面、测试函数。

├── __tests__
│   ├── components
│   ├── functions
│   └── pages
│       └── Novel.test.tsx
复制代码

如果要对 __tests__
文件夹中的内容进行类型检查,需要安装jest类型定义包,并且在tsconfig.json文件夹中引入 __tests__
文件夹。

npm i --save-dev @types/jest
复制代码

在tsconfig.json的include中添加上 __tests__
目录:

...
"include": [
...
"__tests__/*",
"__tests__/**/*",
]
}
复制代码

1.写一个例子

调整 src/pages/Novel/Novel.tsx
文件内容如下:

export default function Novel(): JSX.Element {
const [ novels, setNovels ] = UseState(['一号小说', '二号小说']);
function deleteNovel (index): void {
if (!novels[index]) return;
const nextNovels = [...novels];
nextNovels.splice(index, 1);
setNovels(nextNovels);
}
return (
<div>
<ul>
{novels.map((novel, index) => {
return <li key={index} onClick={deleteNovel.bind(this, index)} style={{ cursor: 'pointer' }}>{novel}</li>;
})}
</ul>
</div>
);
}
复制代码

组件渲染小说列表,点击某选项就删除该选项。比如点击渲染出的 <li>一号小说</li>
<li>一号小说</li>
就会被删除。

tests
/pages/Novel.test.tsx 文件内容如下:

import { render, fireEvent, screen } from '@testing-library/react';
import Novel from '@/pages/Novel/Novel.tsx';
test('novel item should not exist after deletion', () => {
const novelName = '一号小说';
render(<Novel />);
expect(screen.queryByText(novelName)).toBeTruthy();
fireEvent.click(screen.queryByText(novelName));
expect(screen.queryByText(novelName)).toBeNull();
});
复制代码

这个测试文件简单地测试以下内容:

​ 组件渲染成DOM后,textContent为’一号小说’的元素(A元素)是存在的,点击A元素之后,DOM中就没有A元素了。

关于代码中用到的test、fireEvent等更详细的信息可以查看 Jest官网–使用matchers
testing-library官网
firEvent模拟事件

2.Jest配置

在前几篇文章的代码中,我们已经设置了React等全局变量,以及使用@作为src目录的路径别名。Jest中也需要做相应的配置,否则执行 npx jest
会报错。

在根目录下创建一个jest.config.js文件,用于进行 jest配置

touch jest.config.js
复制代码

jest.config.js文件内容如下:

module.exports = {
setupFiles: ['<rootDir>/__tests__/jest.setup.js'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
testPathIgnorePatterns: ['<rootDir>/__tests__/jest.setup.js', '/node_modules/']
}
复制代码

(1)配置全局变量

jest配置中有一个globals属性用于配置全局变量,但是globals对象设置的对象必须是JSON可序列化的,而函数在使用JSON.stringify()转换的时候会被忽略,所以需要使用setupFiles属性。 <rootDir>
是jest配置文件所在的文件目录。

jest.setup.js文件内容如下:

import React, { useState } from 'react';
global['React'] = React;
global['UseState'] = useState;
复制代码

因为这里要测试的组件只用到的React和UseState,所以只配置了React和useState,需要用到别的全局变量时,可以按照相同的方法配置。

(2)配置路径别名

在moduleNameMapper属性中设置 '^@/(.*)$': '<rootDir>/src/$1'
用于配置路径别名,就像之前我们在Webpack和TypeScript中配置的那样,用@代表src目录。一开始我使用的是 '^@(.*)$': '<rootDir>/src$1'
,这样执行 npx jest
的时候会报错:

● Test suite failed to run
Configuration error:
Could not locate module @babel/code-frame mapped as:
.../simple-scaffold02/src$1.
复制代码

经过反复 横跳
检查,两边的正则表达式都是没错的,但是仔细观察报错信息,发现这样配置把@babel/code-frame引用也映射到src路径下了,然而src路径下是没有babel/code-frame的,自然报错了。所以需要以加一根斜杠的方式匹配路径 '^@/(.*)$': '<rootDir>/src/$1'

(3)设置忽略文件

Jest默认会将匹配正则表达式 (/__tests__/.*|(\.|/)(test|spec))\.[jt]sx?$
的文件作为测试文件,前面的jest.setup.js文件是放在 __tests__
文件夹中的,但它是用来设置全局变量的,并不是测试文件。所以需要在testPathIgnorePatterns中配置忽略它。

(4)Webpack和TypeScript新增配置。

因为新增了一个全局变量UseState代表React的useState模块,所以在Webpack和TypeScript中也需要加上这个全局变量:

config/webpack.common.js:

new webpack.ProvidePlugin({
...
UseState: ['react', 'useState'],
}),
复制代码

typings/react.d.ts:

import React, { useEffect, useState } from 'react';
declare global {
...
const UseState: typeof useState;
}
复制代码

(5)修改 package.json

"scripts": {
...
"test": "jest"
},
复制代码

执行 npm run test
执行测试用例,得到结果:

> jest
PASS  __tests__/pages/Novel.test.tsx
✓ novel item should not exist after deletion (33ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.241s
复制代码

(6)检查代码风格是否统一

执行 npm run lint
进行检查,发现因为新创建的jest.setup.js文件中的 import React, { useState } from 'react';
造成了一个报错:

error  Parsing error: 'import' and 'export' may appear only with 'sourceType: module
复制代码

只有配置了 sourceType: module
才能使用import和export。之前的代码中也用了import,但因为是在ts文件和tsx文件中,所以eslint没有报这个错。

在**.eslintrc.js**中添加上以下内容即可:

module.exports = {
...
parserOptions: {
sourceType: 'module',
},
}
复制代码

其他

1.根据需要忽略不想使用git上传的文件

touch .gitignore
复制代码

文件内容如下:

.DS_Store
.vscode
dist
node_modules
复制代码

比较全的gitignore配置可以参考 gitignore例子合集

2.创建README.md

# npm start
start up project.
# npm run build
build for production environment.
# npm run buid:dev
build for development environment.
# npm run lint
use elint to find problems in code.
# npm run test
user jest to test code.
复制代码

下一篇: 《使用Webpack等搭建一个适用于React项目的脚手架(4 – 优化)》

docker 启动镜像后执行 shell 命令

上一篇

Android Binder之旅

下一篇

你也可能喜欢

使用Webpack等搭建一个适用于React项目的脚手架(3 – Eslint、Jest)

长按储存图像,分享给朋友