diff --git a/.all-contributorsrc b/.all-contributorsrc
index ab24e55..bd7f94d 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -165,6 +165,25 @@
"code",
"bug"
]
+ },
+ {
+ "login": "huyenltnguyen",
+ "name": "Huyen Nguyen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25715018?v=4",
+ "profile": "https://github.com/huyenltnguyen",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mdotwills",
+ "name": "Matthew",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5505611?v=4",
+ "profile": "https://github.com/mdotwills",
+ "contributions": [
+ "bug",
+ "code"
+ ]
}
]
}
diff --git a/.github/workflows/eslint-plugin-jest-dom.yml b/.github/workflows/eslint-plugin-jest-dom.yml
deleted file mode 100644
index e8783a3..0000000
--- a/.github/workflows/eslint-plugin-jest-dom.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: eslint-plugin-jest-dom
-on:
- push:
- branches:
- - "master"
- - "alpha"
- pull_request:
-
-jobs:
- test:
- name: "node ${{ matrix.node }} ${{matrix.browser}} ${{ matrix.os }} "
- runs-on: "${{ matrix.os }}"
- strategy:
- matrix:
- os: [ubuntu-latest]
- node: [10, 12, 14, 16]
- steps:
- - uses: actions/setup-node@v2
- with:
- node-version: ${{ matrix.node }}
- - uses: actions/checkout@v2
- - run: INIT_CWD="$(pwd)" npm install
- - run: npm run validate
- - run: npx codecov@3
- release:
- runs-on: ubuntu-latest
- needs: test
- steps:
- - uses: actions/setup-node@v2
- with:
- node-version: 14
- - uses: actions/checkout@v2
- - run: npm install
- - run: npm run build
- - run: ls -asl dist
- - run: npx semantic-release
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml
index aa4f211..23844b4 100644
--- a/.github/workflows/smoke-test.yml
+++ b/.github/workflows/smoke-test.yml
@@ -2,21 +2,23 @@ name: Smoke test
on:
schedule:
- - cron: '0 0 * * SUN'
+ - cron: "0 0 * * SUN"
workflow_dispatch:
jobs:
- lint:
+ test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
- with:
- node-version: 12.11
- - run: yarn install
- - run: yarn build
- - run: yarn link
- - run: yarn link eslint-plugin-jest-dom
- - run: yarn test:smoke
- env:
- CI: true
\ No newline at end of file
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 16
+ - run: |
+ npm install
+ npm run build
+ npm link
+ npm link eslint-plugin-jest-dom
+ - uses: AriPerkkio/eslint-remote-tester-run-action@v3
+ with:
+ issue-title: "Results of weekly scheduled smoke test"
+ eslint-remote-tester-config: smoke-test/eslint-remote-tester.config.js
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
new file mode 100644
index 0000000..9991d22
--- /dev/null
+++ b/.github/workflows/validate.yml
@@ -0,0 +1,100 @@
+name: validate
+on:
+ push:
+ branches:
+ - "+([0-9])?(.{+([0-9]),x}).x"
+ - "main"
+ - "next"
+ - "next-major"
+ - "beta"
+ - "alpha"
+ - "!all-contributors/**"
+ pull_request: {}
+jobs:
+ main:
+ # ignore all-contributors PRs
+ if: ${{ !contains(github.head_ref, 'all-contributors') }}
+ strategy:
+ matrix:
+ eslint: [6.8.0, 6, 7.0.0, 7, 8.0.0, 8]
+ node: [12.22.0, 12, 14.17.0, 14, 16.0.0, 16]
+ runs-on: ubuntu-latest
+ steps:
+ - name: π Cancel Previous Runs
+ uses: styfle/cancel-workflow-action@0.11.0
+
+ - name: β¬οΈ Checkout repo
+ uses: actions/checkout@v3
+
+ - name: β Setup node
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node }}
+
+ - name: π₯ Download deps
+ uses: bahmutov/npm-install@v1
+ with:
+ useLockFile: false
+
+ - name: Install ESLint v${{ matrix.eslint }}
+ run: npm install --no-save --force eslint@${{ matrix.eslint }}
+
+ - name: βΆοΈ Run validate script (without linting)
+ if: ${{ matrix.eslint != 8 }}
+ run: npm run validate -- build,test:coverage
+
+ - name: βΆοΈ Run validate script (with linting)
+ if: ${{ matrix.eslint == 8 }}
+ run: npm run validate
+
+ - name: βΆοΈ Ensure docs are up-to-date
+ if: ${{ matrix.eslint == 8 && matrix.node == 16 }}
+ run: npm run lint:generate-readme-table
+
+ - name: β¬οΈ Upload coverage report
+ uses: codecov/codecov-action@v3
+
+ release:
+ needs: main
+ runs-on: ubuntu-latest
+ if: ${{ github.repository == 'testing-library/eslint-plugin-jest-dom' &&
+ contains('refs/heads/main,refs/heads/beta,refs/heads/next,refs/heads/alpha',
+ github.ref) && github.event_name == 'push' }}
+ steps:
+ - name: π Cancel Previous Runs
+ uses: styfle/cancel-workflow-action@0.11.0
+
+ - name: β¬οΈ Checkout repo
+ uses: actions/checkout@v3
+
+ - name: β Setup node
+ uses: actions/setup-node@v3
+ with:
+ node-version: 16
+
+ - name: π₯ Download deps
+ uses: bahmutov/npm-install@v1
+ with:
+ useLockFile: false
+
+ - name: π Run build script
+ run: npm run build
+
+ - run: ls -asl dist
+
+ - name: π Release
+ uses: cycjimmy/semantic-release-action@v2
+ with:
+ semantic_version: 18
+ branches: |
+ [
+ '+([0-9])?(.{+([0-9]),x}).x',
+ 'main',
+ 'next',
+ 'next-major',
+ {name: 'beta', prerelease: true},
+ {name: 'alpha', prerelease: true}
+ ]
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/README.md b/README.md
index 6c6bf8a..f66f450 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,7 @@
[![version][version-badge]][package]
[![downloads][downloads-badge]][npmtrends]
[![MIT License][license-badge]][license]
-
-
-[](#contributors-)
-
+[![All Contributors][all-contributors-badge]](#contributors-)
[![PRs Welcome][prs-badge]][prs]
[![Code of Conduct][coc-badge]][coc]
@@ -46,7 +43,7 @@ should be installed as one of your project's `devDependencies`:
npm install --save-dev eslint-plugin-jest-dom
```
-This library has a required `peerDependencies` listing for [`eslint`][eslint]
+This library has a required `peerDependencies` listing for [`ESLint`](https://eslint.org/).
## Usage
@@ -95,27 +92,27 @@ module.exports = {
## Supported Rules
-π indicates that a rule is recommended for all users.
-
-π§ indicates that a rule is fixable.
+
-
+πΌ Configurations enabled in.\
+β
Set in the `recommended` configuration.\
+π§ Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
-| Name | π | π§ | Description |
-| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | --------------------------------------------------------------------- |
-| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | π | π§ | prefer toBeChecked over checking attributes |
-| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | π | π§ | Prefer toBeEmpty over checking innerHTML |
-| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | π | π§ | prefer toBeDisabled or toBeEnabled over checking attributes |
-| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | π | π§ | prefer toHaveFocus over checking document.activeElement |
-| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | π | π§ | Prefer .toBeInTheDocument() for asserting the existence of a DOM node |
-| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | π | π§ | prefer toBeRequired over checking properties |
-| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | π | π§ | prefer toHaveAttribute over checking getAttribute/hasAttribute |
-| [prefer-to-have-class](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-class.md) | π | π§ | prefer toHaveClass over checking element className |
-| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | π | π§ | prefer toHaveStyle over checking element style |
-| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | π | π§ | Prefer toHaveTextContent over checking element.textContent |
-| [prefer-to-have-value](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-value.md) | π | π§ | prefer toHaveValue over checking element.value |
+| NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description | πΌ | π§ |
+| :----------------------------------------------------------------------- | :-------------------------------------------------------------------- | :- | :- |
+| [prefer-checked](docs/rules/prefer-checked.md) | prefer toBeChecked over checking attributes | β
| π§ |
+| [prefer-empty](docs/rules/prefer-empty.md) | Prefer toBeEmpty over checking innerHTML | β
| π§ |
+| [prefer-enabled-disabled](docs/rules/prefer-enabled-disabled.md) | prefer toBeDisabled or toBeEnabled over checking attributes | β
| π§ |
+| [prefer-focus](docs/rules/prefer-focus.md) | prefer toHaveFocus over checking document.activeElement | β
| π§ |
+| [prefer-in-document](docs/rules/prefer-in-document.md) | Prefer .toBeInTheDocument() for asserting the existence of a DOM node | β
| π§ |
+| [prefer-required](docs/rules/prefer-required.md) | prefer toBeRequired over checking properties | β
| π§ |
+| [prefer-to-have-attribute](docs/rules/prefer-to-have-attribute.md) | prefer toHaveAttribute over checking getAttribute/hasAttribute | β
| π§ |
+| [prefer-to-have-class](docs/rules/prefer-to-have-class.md) | prefer toHaveClass over checking element className | β
| π§ |
+| [prefer-to-have-style](docs/rules/prefer-to-have-style.md) | prefer toHaveStyle over checking element style | β
| π§ |
+| [prefer-to-have-text-content](docs/rules/prefer-to-have-text-content.md) | Prefer toHaveTextContent over checking element.textContent | β
| π§ |
+| [prefer-to-have-value](docs/rules/prefer-to-have-value.md) | prefer toHaveValue over checking element.value | β
| π§ |
-
+
## Issues
@@ -163,6 +160,8 @@ Thanks goes to these people ([emoji key][emojis]):
 Gareth Jones β οΈ π» π |
+  Huyen Nguyen π |
+  Matthew π π» |
@@ -181,8 +180,8 @@ MIT
[npm]: https://www.npmjs.com
[node]: https://nodejs.org
-[build-badge]: https://img.shields.io/travis/testing-library/eslint-plugin-jest-dom.svg?style=flat-square
-[build]: https://travis-ci.org/testing-library/eslint-plugin-jest-dom
+[build-badge]: https://img.shields.io/github/workflow/status/testing-library/eslint-plugin-jest-dom/validate?logo=github&style=flat-square
+[build]: https://github.com/testing-library/eslint-plugin-jest-dom/actions?query=workflow%3Avalidate
[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/eslint-plugin-jest-dom.svg?style=flat-square
[coverage]: https://codecov.io/github/testing-library/eslint-plugin-jest-dom
[version-badge]: https://img.shields.io/npm/v/eslint-plugin-jest-dom.svg?style=flat-square
@@ -190,11 +189,11 @@ MIT
[downloads-badge]: https://img.shields.io/npm/dm/eslint-plugin-jest-dom.svg?style=flat-square
[npmtrends]: http://www.npmtrends.com/eslint-plugin-jest-dom
[license-badge]: https://img.shields.io/npm/l/eslint-plugin-jest-dom.svg?style=flat-square
-[license]: https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/LICENSE
+[license]: https://github.com/testing-library/eslint-plugin-jest-dom/blob/main/LICENSE
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
[prs]: http://makeapullrequest.com
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
-[coc]: https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/other/CODE_OF_CONDUCT.md
+[coc]: https://github.com/testing-library/eslint-plugin-jest-dom/blob/main/other/CODE_OF_CONDUCT.md
[emojis]: https://github.com/all-contributors/all-contributors#emoji-key
[all-contributors]: https://github.com/all-contributors/all-contributors
[bugs]: https://github.com/testing-library/eslint-plugin-jest-dom/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug
diff --git a/build/generate-readme-table.js b/build/generate-readme-table.js
deleted file mode 100644
index 8e5cd22..0000000
--- a/build/generate-readme-table.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const fs = require("fs");
-const path = require("path");
-const rules = require("..").rules;
-
-const README_LOCATION = path.resolve(__dirname, "..", "README.md");
-const BEGIN_TABLE_MARKER = "\n";
-const END_TABLE_MARKER = "\n";
-
-const expectedTableLines = Object.keys(rules)
- .sort()
- .reduce(
- (lines, ruleId) => {
- const rule = rules[ruleId];
-
- lines.push(
- [
- `[${ruleId}](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/${ruleId}.md)`,
- rule.meta.docs.recommended ? "π" : "",
- rule.meta.fixable ? "π§" : "",
- rule.meta.docs.description,
- ].join(" | ")
- );
-
- return lines;
- },
- ["Name | π | π§ | Description", "----- | ----- | ----- | -----"]
- )
- .join("\n");
-
-const readmeContents = fs.readFileSync(README_LOCATION, "utf8");
-
-if (!readmeContents.includes(BEGIN_TABLE_MARKER)) {
- throw new Error(
- `Could not find '${BEGIN_TABLE_MARKER}' marker in README.md.`
- );
-}
-
-if (!readmeContents.includes(END_TABLE_MARKER)) {
- throw new Error(`Could not find '${END_TABLE_MARKER}' marker in README.md.`);
-}
-
-const linesStartIndex =
- readmeContents.indexOf(BEGIN_TABLE_MARKER) + BEGIN_TABLE_MARKER.length;
-const linesEndIndex = readmeContents.indexOf(END_TABLE_MARKER);
-
-const updatedReadmeContents =
- readmeContents.slice(0, linesStartIndex) +
- expectedTableLines +
- readmeContents.slice(linesEndIndex);
-
-if (module.parent) {
- module.exports = updatedReadmeContents;
-} else {
- fs.writeFileSync(README_LOCATION, updatedReadmeContents);
-}
diff --git a/docs/rules/prefer-checked.md b/docs/rules/prefer-checked.md
index c2a4fc0..295e82a 100644
--- a/docs/rules/prefer-checked.md
+++ b/docs/rules/prefer-checked.md
@@ -1,4 +1,10 @@
-# prefer toBeChecked() or not.toBeChecked() over toHaveProperty('checked', true|false) (prefer-enabled-checked)
+# Prefer toBeChecked over checking attributes (`jest-dom/prefer-checked`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
## Rule Details
diff --git a/docs/rules/prefer-empty.md b/docs/rules/prefer-empty.md
index 30ead57..a9e3614 100644
--- a/docs/rules/prefer-empty.md
+++ b/docs/rules/prefer-empty.md
@@ -1,4 +1,10 @@
-# Prefer toBeEmptyDOMElement over checking innerHTML / firstChild (prefer-empty)
+# Prefer toBeEmpty over checking innerHTML (`jest-dom/prefer-empty`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
This rule ensures people will use toBeEmptyDOMElement() rather than checking dom
nodes/properties. It is primarily aimed at consistently using jest-dom for
@@ -57,7 +63,7 @@ Don't use this rule if you don't care if people use `.toBeEmptyDOMElement()`.
-
+
## Changelog
diff --git a/docs/rules/prefer-enabled-disabled.md b/docs/rules/prefer-enabled-disabled.md
index 7a34e38..34ea28d 100644
--- a/docs/rules/prefer-enabled-disabled.md
+++ b/docs/rules/prefer-enabled-disabled.md
@@ -1,4 +1,10 @@
-# prefer toBeDisabled() or toBeEnabled() over toHaveProperty('disabled', true|false) (prefer-enabled-disabled)
+# Prefer toBeDisabled or toBeEnabled over checking attributes (`jest-dom/prefer-enabled-disabled`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
## Rule Details
diff --git a/docs/rules/prefer-focus.md b/docs/rules/prefer-focus.md
index a7e8dda..7d9e938 100644
--- a/docs/rules/prefer-focus.md
+++ b/docs/rules/prefer-focus.md
@@ -1,4 +1,10 @@
-# prefer-focus
+# Prefer toHaveFocus over checking document.activeElement (`jest-dom/prefer-focus`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
prefer toHaveFocus over checking document.activeElement (prefer-focus)
diff --git a/docs/rules/prefer-in-document.md b/docs/rules/prefer-in-document.md
index ae1072d..a66848d 100644
--- a/docs/rules/prefer-in-document.md
+++ b/docs/rules/prefer-in-document.md
@@ -1,4 +1,10 @@
-# Prefer .toBeInTheDocument in favor of .toHaveLength(1) (prefer-in-document)
+# Prefer .toBeInTheDocument() for asserting the existence of a DOM node (`jest-dom/prefer-in-document`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
## Rule Details
@@ -21,6 +27,10 @@ expect(queryByText("foo")).toEqual(null);
expect(queryByText("foo")).not.toEqual(null);
expect(queryByText("foo")).toBeDefined();
expect(queryByText("foo")).not.toBeDefined();
+expect(queryByText("foo")).toBeTruthy();
+expect(queryByText("foo")).not.toBeTruthy();
+expect(queryByText("foo")).toBeFalsy();
+expect(queryByText("foo")).not.toBeFalsy();
const foo = screen.getByText("foo");
expect(foo).toHaveLength(1);
diff --git a/docs/rules/prefer-required.md b/docs/rules/prefer-required.md
index 437638f..54c6617 100644
--- a/docs/rules/prefer-required.md
+++ b/docs/rules/prefer-required.md
@@ -1,4 +1,10 @@
-# prefer toBeRequired() or not.toBeRequired() over toHaveProperty('required', true|false) (prefer-enabled-required)
+# Prefer toBeRequired over checking properties (`jest-dom/prefer-required`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
## Rule Details
diff --git a/docs/rules/prefer-to-have-attribute.md b/docs/rules/prefer-to-have-attribute.md
index ed887a3..c290ceb 100644
--- a/docs/rules/prefer-to-have-attribute.md
+++ b/docs/rules/prefer-to-have-attribute.md
@@ -1,4 +1,10 @@
-# prefer toHaveAttribute over checking getAttribute/hasAttribute (prefer-to-have-attribute)
+# Prefer toHaveAttribute over checking getAttribute/hasAttribute (`jest-dom/prefer-to-have-attribute`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
This rule is an autofixable rule that reports usages of `getAttribute` or
`hasAttribute` in expect statements in preference of using the jest-dom
diff --git a/docs/rules/prefer-to-have-class.md b/docs/rules/prefer-to-have-class.md
index 5978794..f4f2937 100644
--- a/docs/rules/prefer-to-have-class.md
+++ b/docs/rules/prefer-to-have-class.md
@@ -1,4 +1,10 @@
-# prefer toHaveClass over checking element.class (prefer-to-have-class)
+# Prefer toHaveClass over checking element className (`jest-dom/prefer-to-have-class`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
This rule is an autofixable rule that reports usages of checking element className or classList in expect statements in preference of using the jest-dom
`toHaveClass` matcher.
diff --git a/docs/rules/prefer-to-have-style.md b/docs/rules/prefer-to-have-style.md
index 118030e..bf61d9e 100644
--- a/docs/rules/prefer-to-have-style.md
+++ b/docs/rules/prefer-to-have-style.md
@@ -1,4 +1,10 @@
-# prefer toHaveStyle over checking element.style (prefer-to-have-style)
+# Prefer toHaveStyle over checking element style (`jest-dom/prefer-to-have-style`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
This rule is an autofixable rule that reports usages of checking element.style in expect statements in preference of using the jest-dom
`toHaveStyle` matcher.
diff --git a/docs/rules/prefer-to-have-text-content.md b/docs/rules/prefer-to-have-text-content.md
index f10c6b2..a960908 100644
--- a/docs/rules/prefer-to-have-text-content.md
+++ b/docs/rules/prefer-to-have-text-content.md
@@ -1,4 +1,10 @@
-# Prefer toHaveTextContent over checking element.textContent (prefer-to-have-text-content)
+# Prefer toHaveTextContent over checking element.textContent (`jest-dom/prefer-to-have-text-content`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
Please describe the origin of the rule here.
diff --git a/docs/rules/prefer-to-have-value.md b/docs/rules/prefer-to-have-value.md
index 35a6074..544b2f5 100644
--- a/docs/rules/prefer-to-have-value.md
+++ b/docs/rules/prefer-to-have-value.md
@@ -1,4 +1,10 @@
-# prefer toHaveAttribute over checking getAttribute/hasAttribute (prefer-to-have-attribute)
+# Prefer toHaveValue over checking element.value (`jest-dom/prefer-to-have-value`)
+
+πΌ This rule is enabled in the β
`recommended` config.
+
+π§ This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
+
This rule is an autofixable rule that encourages the use of toHaveValue over checking the value attribute.
diff --git a/other/MAINTAINING.md b/other/MAINTAINING.md
index c78fc0b..e0c40cc 100644
--- a/other/MAINTAINING.md
+++ b/other/MAINTAINING.md
@@ -48,7 +48,7 @@ to release. See the next section on Releases for more about that.
## Release
-Our releases are automatic. They happen whenever code lands into `master`. A
+Our releases are automatic. They happen whenever code lands into `main`. A
travis build gets kicked off and if it's successful, a tool called
[`semantic-release`](https://github.com/semantic-release/semantic-release) is
used to automatically publish a new release to npm as well as a changelog to
diff --git a/package.json b/package.json
index 9271bef..1fd6f3b 100644
--- a/package.json
+++ b/package.json
@@ -30,49 +30,49 @@
],
"scripts": {
"build": "kcd-scripts build",
- "pregenerate-readme-table": "yarn build",
- "generate-readme-table": "node build/generate-readme-table.js",
+ "pregenerate-readme-table": "npm run build",
+ "generate-readme-table": "eslint-doc-generator --ignore-config all",
"lint": "kcd-scripts lint",
+ "lint:generate-readme-table": "npm run generate-readme-table -- --check",
"setup": "npm install && npm run validate -s",
"test": "kcd-scripts test",
- "test:smoke": "eslint-remote-tester --config ./smoke-test/eslint-remote-tester.config.js",
- "test:update": "npm test -- --updateSnapshot --coverage",
+ "test:coverage": "npm test -- --coverage",
+ "test:update": "npm test:coverage -- --updateSnapshot",
"validate": "kcd-scripts validate"
},
"dependencies": {
- "@babel/runtime": "^7.9.6",
- "@testing-library/dom": "^7.28.1",
+ "@babel/runtime": "^7.16.3",
+ "@testing-library/dom": "^8.11.1",
"requireindex": "^1.2.0"
},
"devDependencies": {
- "@typescript-eslint/parser": "^4.8.2",
- "eslint": "7.24",
- "eslint-remote-tester": "^0.3.3",
- "jest-extended": "^0.11.5",
- "kcd-scripts": "6.5.1",
- "typescript": "^4.1.2"
+ "@typescript-eslint/parser": "^5.9.1",
+ "eslint": "^8.7.0",
+ "eslint-doc-generator": "^0.19.0",
+ "eslint-remote-tester": "^3.0.0",
+ "eslint-remote-tester-repositories": "^0.0.7",
+ "kcd-scripts": "^12.0.0",
+ "typescript": "^4.5.3"
},
"peerDependencies": {
- "eslint": ">=6.8"
+ "eslint": "^6.8.0 || ^7.0.0 || ^8.0.0"
},
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
"rules": {
- "babel/quotes": "off",
+ "consistent-return": "off",
"max-lines-per-function": "off",
- "testing-library/no-dom-import": "off",
- "consistent-return": "off"
+ "testing-library/no-dom-import": "off"
}
},
"eslintIgnore": [
"node_modules",
"coverage",
"dist",
- ".cache-eslint-remote-tester",
"eslint-remote-tester-results"
],
"engines": {
- "node": "^10.12.0 || >=12.0.0",
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0",
"npm": ">=6",
"yarn": ">=1"
}
diff --git a/smoke-test/eslint-remote-tester.config.js b/smoke-test/eslint-remote-tester.config.js
index e281c08..6b48dfe 100644
--- a/smoke-test/eslint-remote-tester.config.js
+++ b/smoke-test/eslint-remote-tester.config.js
@@ -1,37 +1,15 @@
-module.exports = {
- /** Repositories to scan */
- repositories: require("./repositories.json"),
+const {
+ getRepositories,
+ getPathIgnorePattern,
+} = require("eslint-remote-tester-repositories");
- /** Extensions of files under scanning */
+module.exports = {
+ repositories: getRepositories({ randomize: true }),
+ pathIgnorePattern: getPathIgnorePattern(),
extensions: ["js", "jsx", "ts", "tsx"],
-
- /** Optional pattern used to exclude paths */
- pathIgnorePattern: `(${[
- "node_modules",
- "\\/\\.", // Any file or directory starting with dot, e.g. ".git"
- "/dist/",
- "/build/",
-
- // Common patterns for minified JS
- "babel\\.js",
- "vendor\\.js",
- "vendors\\.js",
- "chunk\\.js",
- "bundle\\.js",
- "react-dom\\.development\\.js",
- "\\.min\\.js", // Any *.min.js
- ].join("|")})`,
-
- /** Empty array since we are only interested in linter crashes */
- rulesUnderTesting: [],
-
- /** Maximum amount of tasks ran concurrently */
- concurrentTasks: 2,
-
- /** Optional boolean flag used to enable caching of cloned repositories. For CIs it's ideal to disable caching. Defauls to true. */
+ concurrentTasks: 3,
cache: false,
-
- /** ESLint configuration */
+ logLevel: "info",
eslintrc: {
root: true,
env: {
@@ -45,7 +23,6 @@ module.exports = {
jsx: true,
},
},
- plugins: ["jest-dom"],
extends: ["plugin:jest-dom/all"],
},
};
diff --git a/smoke-test/repositories.json b/smoke-test/repositories.json
deleted file mode 100644
index 10c53ac..0000000
--- a/smoke-test/repositories.json
+++ /dev/null
@@ -1,480 +0,0 @@
-[
- "gatsbyjs/gatsby",
- "reduxjs/react-redux",
- "ionic-team/ionic-framework",
- "NervJS/taro",
- "netlify/netlify-cms",
- "Popmotion/popmotion",
- "ctrlplusb/easy-peasy",
- "cdapio/cdap",
- "boltdesignsystem/bolt",
- "webiny/webiny-js",
- "alibaba/formily",
- "trojanowski/react-apollo-hooks",
- "kata-ai/aksara-ui",
- "FirebaseExtended/reactfire",
- "jaredpalmer/the-platform",
- "namespace-ee/react-calendar-timeline",
- "wsmd/react-use-form-state",
- "donavon/use-persisted-state",
- "auth0/cosmos",
- "commercetools/merchant-center-application-kit",
- "pismo/bolt",
- "NDLANO/frontend-packages",
- "anish000kumar/redux-box",
- "concentjs/concent",
- "tdeekens/flopflip",
- "neo4j/neo4j-browser",
- "the-dr-lazy/deox",
- "solid/react-components",
- "cdmbase/fullstack-pro",
- "acl-services/paprika",
- "react-cosmos/react-cosmos-classic",
- "react-boilerplate/react-boilerplate",
- "alvarotrigo/react-fullpage",
- "techniq/react-fetch-component",
- "derrickbeining/react-atom",
- "plougsgaard/react-timeout",
- "vtex/styleguide",
- "Availity/availity-workflow",
- "escaladesports/gatsby-plugin-prefetch-google-fonts",
- "meetalva/alva",
- "apollographql/fullstack-tutorial",
- "marcin-piela/react-fetching-library",
- "wmira/react-sidenav",
- "dericgw/react-tiny-fab",
- "davidjbradshaw/eslint-config-adjunct",
- "livewire/livewire",
- "framer/motion",
- "Rocketseat/unform",
- "kentcdodds/react-hooks",
- "pietrzakadrian/bank",
- "mobxjs/mst-gql",
- "lostpebble/pullstate",
- "davidgilbertson/react-recollect",
- "jxom/react-loads",
- "mpontus/react-modal-hook",
- "willb335/chessboardjsx",
- "proyecto26/ion-phaser",
- "ticketmaster/aurora",
- "goblindegook/littlefoot",
- "roginfarrer/react-collapsed",
- "pagarme/former-kit",
- "quiltdata/t4",
- "inrupt/solid-react-components",
- "poanetwork/tokenbridge",
- "liferay/com-liferay-commerce",
- "inrupt/generator-solid-react",
- "vtex/test-tools",
- "techniq/react-odata",
- "escaladesports/react-netlify-form",
- "homelight/swan-form",
- "repaygithub/cactus",
- "aweary/react-copy-write",
- "kentcdodds/advanced-react-patterns",
- "async-library/react-webworker",
- "theodo/falco",
- "beizhedenglong/react-hooks-lib",
- "skidding/react-testing-examples",
- "saisandeepvaddi/ten-hands",
- "ngxf/platform",
- "WaftTech/WaftEngine",
- "lightspeed/flame",
- "googlemap-react/googlemap-react",
- "devconzm/devconzm",
- "mirego/react-boilerplate",
- "stevey-p/react-localize",
- "aml2610/react-painter",
- "zopaUK/react-components",
- "barnardos/design-system",
- "wangtao0101/resa",
- "easynvest/easyguide",
- "MyCryptoHQ/ui",
- "WTW-IM/es-components",
- "neosiae/react-aria-offcanvas",
- "ubergrape/grape-web-client",
- "gokulkrishh/react-hooks-accordion",
- "azdanov/azdanov.github.io",
- "wx-chevalier/m-fe-commons",
- "we-mak/w-design",
- "sembark/gladio",
- "mmmurray/mmm-scripts",
- "boyney123/mockit",
- "gregberge/react-teleporter",
- "donavon/use-dark-mode",
- "open-source-labs/spearmint",
- "selbekk/calidation",
- "entria/entria-fullstack",
- "ilyalesik/react-fetch-hook",
- "kentcdodds/jest-cypress-react-babel-webpack",
- "revelcw/react-hooks-helper",
- "Ephem/react-aldrin",
- "dessa-oss/atlas",
- "gpietro/react-numpad",
- "topheman/npm-registry-browser",
- "donavon/use-firebase-auth",
- "salvoravida/react-redux-fork",
- "italoiz/unform-community-packages",
- "velopert/learn-react-testing",
- "jamesplease/react-state-context",
- "nyaruka/floweditor",
- "coodoo/xstate-examples",
- "neetjn/v-localize",
- "sesilio/react-script-loader-hoc",
- "ilkeryilmaz/bilyeli",
- "bmcmahen/react-gesture-responder",
- "MaximeHeckel/blog.maximeheckel.com",
- "cellog/ion-router",
- "jdlehman/switcheroo",
- "vrk-kpa/suomifi-ui-components",
- "rahsheen/react-wizard",
- "unicorn-utterances/unicorn-utterances",
- "uber/graph.gl",
- "reime005/react-native-tapjoy",
- "traveloka/react-diode",
- "wyze/bs-jest-dom",
- "Asjas/personal-website",
- "ismailman/react-spho",
- "lessp/react-is-visible",
- "Patrickskiba/weborg",
- "orbs-network/orbs-ethereum-contracts-v1",
- "doitadrian/react-columned",
- "International-Slackline-Association/Slackline-App",
- "charliewilco/downwrite",
- "summersky2014/bsl",
- "alexandernanberg/formin",
- "frinyvonnick/handling-peer-dependencies",
- "prodo-ai/snoopy",
- "jane/react-now-you-see-me",
- "GetJobber/atlantis",
- "quintype/quintype-node-components",
- "thegrinder/react-handy-hooks",
- "alpinejs/alpine",
- "kentcdodds/react-testing-library-course",
- "microsoft/BotFramework-Composer",
- "kentcdodds/modern-react",
- "Coding-Coach/coding-coach",
- "blocknative/assist",
- "tenon-io/tenon-ui",
- "soska/keyboardist",
- "ctaylo21/termy-the-terminal",
- "kqito/use-global-context",
- "adambrgmn/react-oauth-flow",
- "commonlabs-id/next-with-analytics",
- "traveloka/react-accio",
- "factn/resilience-app",
- "byai/topology",
- "empathyco/solid-oh-my-pod",
- "SubstraFoundation/substra-frontend",
- "steamory/portal",
- "donavon/use-firebase-app",
- "Royal-Navy/standards-toolkit",
- "albinotonnina/react-redux-magichat-demo-booking-system",
- "ensdomains/ens-app",
- "SubstraFoundation/substra-ui",
- "tomwayson/create-arcgis-app",
- "sensu/web",
- "reime005/react-native-camera-hooks",
- "ScalablyTyped/Converter",
- "helixbass/ad-hok",
- "First-Peoples-Cultural-Council/fv-web-ui",
- "limistah/react-here-map",
- "leonardodino/forex-web-app",
- "TwinePlatform/twine-visitor",
- "erezrokah/serverless-monitoring-app",
- "awmleer/jorum",
- "heydoctor/synaptik",
- "donavon/splashr",
- "clarisights/KnitUI",
- "AntonRublev360/react-app-integration-tests-sample",
- "fozg/react-light-state",
- "WiredSolutions/react-azure-maps",
- "zulucoda/react-swift-slider",
- "tiendq/youtube-embed-video",
- "mmmurray/react-super-state",
- "Shilza/react-pretty-drawer",
- "rishichawda/minimal-react-boilerplate",
- "0xTracker/0x-tracker-client",
- "prisma/dataguide",
- "virtru/protect-and-track",
- "inthepocket/itp-react-components",
- "summerua/react-uforms",
- "bopen/react-jsonschema-form-async",
- "zurda/github-user-finder",
- "Minivera/react-vitae",
- "gustavobini/react-google-optimize",
- "sean-macfarlane/Mapgram",
- "gazpachu/sugui",
- "jobn/braid",
- "knpwrs/react-compose-components",
- "Spring3/redshape",
- "zhunor/threejs-dem-visualizer",
- "kareemkibue/k2-react-utils",
- "helpscout/brigade",
- "veksen/homepage",
- "alexkhismatulin/react-use-count-down",
- "lightelligence-io/react",
- "5monkeys/django-bananas.js",
- "and-end/obibok",
- "indiana-university/itpeople-app",
- "stevenfitzpatrick/fitzy",
- "debtcollective/disputes",
- "elijahmanor/react-fun",
- "ambanum/political-ads-crowdsourcing-client",
- "eGroupAI/egroup-material",
- "sunilshw/ThreatHunter",
- "neo4j-devtools/relate-by-ui",
- "horacehylee/chrome-ext-youtube-pickup",
- "jguyon/nupum",
- "azdanov/weather-react-native",
- "virtru/virtuoso-design-system",
- "nju33/react-dayo",
- "MaXeraph/Portal",
- "miguelangeltorresfp/official-apollo-fullstack-tutorial",
- "lindsayjopson/apollo_tutorial",
- "wirelineio/shogi",
- "codacy/styleguide",
- "FIL1994/spectre-react-lib",
- "n1analytics/pihanga",
- "diessetechnology/give-me-a-dog",
- "ryanbas21/ryan_blog",
- "umbiliko/um-react-core",
- "ToniRib/apollo_fullstack_tutorial",
- "dominicfallows/df-multichannel-app",
- "groupystinks/formask",
- "ishakuni/frontend",
- "parthibankrpa/libraryFE",
- "rodrigovive/website",
- "Twistbioscience/DesignerComponents",
- "bopen/react-jsonschema-form-field-geolocation",
- "chuntley/dom-testing-extended",
- "testing-library/dom-testing-library",
- "frankieyan/custom-meta-input",
- "villeheikkila/fullstackopen",
- "kentcdodds/learn-react",
- "kentcdodds/create-react-app-react-testing-library-example",
- "avanslaars/egghead-react-boilerplate",
- "leonardomso/react-bolt",
- "rstacruz/penpad",
- "timdeschryver/frontal",
- "zsajjad/rtk-demo",
- "mjtischler/react-muuri",
- "PlayMa256/react-initial-bootstrap",
- "donavon/use-visibility-change",
- "inloco/orion",
- "devthiago/react-ui-hooks",
- "sync/hackerz",
- "chrisjpatty/crooks",
- "aaronnuu/react-layout",
- "thiagoleitedev/rest-fullstack",
- "juliettepretot/hookIntoProps",
- "Raiffeisen-DGTL/ecom-payment-sdk",
- "albizures/react-dynamic-layout",
- "enzsft/react-cookie-consents",
- "leoyli/addon-contexts",
- "mbrn/material-table.com",
- "failure-driven/bdd-workshop-app",
- "jefflau/apollo-client-mock",
- "x48-crypto/yearn-recycler",
- "Lambda-School-Labs/labs9-employee-scheduler",
- "Kodeworks/liquidator",
- "tegraoss/luffie",
- "rysolv/rysolv",
- "ionic-team/stencil-ds-react-template",
- "ionic-team/stencil-ds-plugins-demo",
- "reactgraphqlacademy/bootcamp-hackathon",
- "Yekon08/RiotApi",
- "invinst/invisible-flow",
- "donavon/use-prev-prop",
- "somarmeteorologia/momentum",
- "hamlim/react-preview-editor",
- "x48-crypto/yearn-party",
- "bsonntag/react-use-countdown",
- "jpreecedev/premium-property-finder",
- "TKT-ohjaajarekisteri/TKT-ohjaajarekisteri-front",
- "react-hulks/react-hulks",
- "dreamworld1101/react-redux",
- "victormath12/lego-ds",
- "ilyalesik/react-use-trigger",
- "bibekg/react-audio-viz",
- "bsonntag/react-use-toggle",
- "kyleshevlin/react-edges",
- "minwork/use-double-tap",
- "doitadrian/react-butterfiles",
- "xuqiongkai/ALTER",
- "NoQuarterTeam/split",
- "facebookincubator/fbc-js-core",
- "arcticicestudio/nord-docs",
- "syberos-team/syberh",
- "man-of-sbk/reactjs-phe-phim-clone",
- "reactway/saga",
- "thanglv2/E-commerce-ReactJs",
- "RegioneER/rer.bandi",
- "WojciechMatuszewski/PartyPlanner",
- "amille44420/react-fetcher",
- "tmns/never-forget-app",
- "SFDO-Tooling/MetaDeploy",
- "chingu-voyages/v9-bears-team-38",
- "functionalStoic/react-elements-selector",
- "arminyahya/react-multiple-select-dropdown",
- "xiaofan2406/fiora",
- "9jaswag/eCommerce-frontend",
- "aozora/react-showtime",
- "enBonnet/hacktoberfest-dashboard",
- "derozn/banterstudios",
- "ipatate/react-scroll-progress-read",
- "TobiasWalle/clean-forms",
- "ahmadnoorniazi/react-advance-boilerplate",
- "erictooth/react-accessible-form",
- "emortlock/spartan-ui",
- "zulucoda/react-tunes-player",
- "fitfab/fitfab-ui",
- "bob-lee/react-joanne",
- "ericwooley/action-packed-react",
- "beiatrix/apollo-tutorial",
- "octopusthink/nautilus",
- "gipsy/nyt-demo",
- "echoghi/emilechoghi.com",
- "exogen/react-dynamic-html",
- "Minivera/cartomapper",
- "mmmurray/react-responsive-layout",
- "SpotlightData/nanowire-extensions",
- "sifbuilder/eodoes",
- "hemanthkodandarama/react-js-prototype-app",
- "Valyay/blog",
- "imaginerio/georio",
- "tuanpt-0634/feathers-react-stack",
- "ita-social-projects/horondi_client_fe",
- "roguh/does-it-live-client",
- "azdanov/translations",
- "vtex-apps/store-icons",
- "tedconf/react-ted-bootstrap",
- "chandojo/climbbeta",
- "daumie/buddy-demo-reactjs",
- "kbrock84/react-svg-polygon",
- "vtex-apps/bazaarvoice",
- "Blockle/blockle-ui",
- "hamlim/components",
- "timc1/time-capsule",
- "Lambda-School-Labs/rv-nav-fe",
- "kaykayehnn/megeto",
- "mario-iamb/list_me",
- "baco16g/boilerplate_reactweb",
- "doitadrian/commodo-fields-date",
- "pmjonesg/algos",
- "erictooth/react-menu-window",
- "zepdev/design-system-website",
- "cds-snc/loon-ui",
- "kobi1021/kobi1021.github.io",
- "jbelgard/rvnav-fe",
- "MentorAPM/mentor-ui",
- "perseids-project/woodhouse-js",
- "lifeiscontent/react-svg-injector",
- "GrayStrider/react-boilerplate-gs",
- "DenisRebenok/react-github-profile",
- "yama-group/Ajirni",
- "fixate/react-show-styles",
- "digital-detox/react-svg-use-external",
- "doitadrian/react-hotkeyz",
- "cdock1029/formr",
- "Phlammariont/ferreira-front",
- "qntln/react-redux",
- "pfeilbr/jest-testing-framework-playground",
- "doitadrian/commodo-fields-float",
- "lirongfei123/react-store-driver",
- "zimmerman-zimmerman/Zoom",
- "debtcollective/header",
- "fixate/react-xstream-store",
- "joefazz/CodeXE",
- "rubenamorim/second-personal-page",
- "oleksiizapara/OpenLI",
- "microcood/god-state",
- "kristiyan-ASW-G-08/mern-reddit-clone",
- "olaoluadesanya/route-optimizer",
- "carlnjoku/extendedteam",
- "Alghost/jira-dashboard",
- "neo4j-apps/relatable",
- "spidergon/visibility",
- "quattro004/SolidReactDotnet",
- "andrewcameronsims/zendesk-ticket-viewer",
- "claydiffrient/simplydiffrient.com",
- "linhdamwumbo/search-film",
- "akhinchey/chess-puzzle-react",
- "Kaus1247/my-react-starter",
- "lg-uplus-compsight/compsight-front",
- "kbrock84/react-radial-render",
- "gs195/Weekly-Organiser",
- "thejohnfreeman/loadable",
- "jgprangshu/react-boilerplate-saga",
- "mablin7/preact-gestures",
- "startmt/react-redux-starter",
- "chrislopresto/chrislopresto-gatsby",
- "aileen-r/react-boilerplate",
- "untitled-labs/metabase-custom",
- "TwoStoryRobot/react-search-fuse",
- "saadmas/space-explorer",
- "jgprangshu/react-material-ui",
- "man-of-sbk/reactjs-admin-panel",
- "Blockle/blockle-form",
- "origen-chat/blocks",
- "Reopard226/track-book-styled-components",
- "the-mmm-agency/muskoka-district-rentals",
- "Muosvr/graphql-fullstack-tutorial",
- "chengsig/react-boilerplate-string",
- "justindujardin/rns-redux",
- "utkuakguner/my-boilerplate",
- "knight212/Netfly-CMS",
- "lossless1/react-test",
- "taiyeoguns/news-app-react",
- "Happytreat/ApolloFullStackTutorial",
- "AnnaMinasian/rustapedia",
- "Bruce2107/anibook-ui",
- "aniketbharambe/e-shopping",
- "andreistroescu/music-webapp",
- "Radi-Mortada/TasksApp",
- "Opetushallitus/ehoks-ui",
- "quanlatoi/Learn_boilerplate",
- "austin-pham-goldenowl/test",
- "zcam007/IMDB-Movie-Search",
- "dajk/react-carousel",
- "sinouiincubator/editable-data-table",
- "vtex-apps/slider-layout",
- "Teslima02/root",
- "WangShuXian6/threejs-ts-es6-webpack-starter",
- "ria3100/ts-react-parcel-boilerplate",
- "masoodalamhunzai/Multiple-Emails-Invitation-React",
- "42BV/react-spring-enums",
- "VikrantShirvankar/Employee-App",
- "CuongNguyen97/khoa-luan-tot-nghiep",
- "linh626688/react-boilerplate-template",
- "avirajsidhu/codetheft_client",
- "absreim/apollo-fs-tutorial",
- "dansbands/gifsearch",
- "newmohib/react-boilerplate-api",
- "uditparmar/oreo",
- "newmohib/react-boilerplate-test",
- "uditparmar/testapp",
- "Ali-Doustkani/richtext",
- "mmmurray/eslint-config-mmm",
- "newmohib/react-boilerplate-demo-4",
- "MrKolt01/react-oauth-flow-reborn",
- "hoangmn3s/react-training",
- "winner95725/react1",
- "hoangenouvo/articulate",
- "helixbass/ad-hok-js",
- "prisoner-skills-app/frontend",
- "jrs-innovation-center/design-system",
- "cismet/wupp-topic-maps",
- "arpit-krazybee/react-boilerplate",
- "hageveld/ui",
- "what-zen/what-zen-app",
- "smileyKJ/Onsurity-Demo",
- "jonathanfilbert/newSite2019",
- "Stevenlee731/cache-app",
- "gagaduday/boilerplate",
- "quoclc-nexle/star-club-experiment",
- "ning-cai/learning-react-redux",
- "tejaretailio/ReactBoilerplate",
- "zth/reason-relay",
- "ProtonMail/react-components"
-]
diff --git a/src/__tests__/index.test.js b/src/__tests__/index.test.js
index 9592ad6..ef29c50 100644
--- a/src/__tests__/index.test.js
+++ b/src/__tests__/index.test.js
@@ -1,5 +1,4 @@
-import { rules, generateRecommendedConfig } from "../index";
-import "jest-extended";
+import { generateRecommendedConfig, rules } from "../";
it("should have all the rules", () => {
expect(Object.keys(rules).length).toBeGreaterThan(0);
@@ -8,9 +7,9 @@ it("should have all the rules", () => {
it.each(Object.keys(rules))("%s should export required fields", (ruleName) => {
const rule = rules[ruleName];
expect(rule).toHaveProperty("create", expect.any(Function));
- expect(rule.meta.docs.url).not.toBeEmpty();
+ expect(rule.meta.docs.url).not.toBe("");
expect(rule.meta.docs.category).toBe("Best Practices");
- expect(rule.meta.docs.description).not.toBeEmpty();
+ expect(rule.meta.docs.description).not.toBe("");
});
it("should have a recommended config with recommended rules", () => {
diff --git a/src/__tests__/lib/rules/prefer-empty.js b/src/__tests__/lib/rules/prefer-empty.js
index 9480fd2..5a4b529 100644
--- a/src/__tests__/lib/rules/prefer-empty.js
+++ b/src/__tests__/lib/rules/prefer-empty.js
@@ -18,6 +18,7 @@ import * as rule from "../../../rules/prefer-empty";
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-empty", rule, {
valid: [
+ `expect().toBe(true)`,
`expect(element.innerHTML).toBe('foo')`,
`expect(element.innerHTML).toBe(foo)`,
`expect(element.innerHTML).toBe(foo + bar)`,
diff --git a/src/__tests__/lib/rules/prefer-focus.js b/src/__tests__/lib/rules/prefer-focus.js
index 8e35dc3..aa4be8c 100644
--- a/src/__tests__/lib/rules/prefer-focus.js
+++ b/src/__tests__/lib/rules/prefer-focus.js
@@ -9,6 +9,7 @@ import * as rule from "../../../rules/prefer-focus";
const ruleTester = new RuleTester();
ruleTester.run("prefer-focus", rule, {
valid: [
+ `expect().toBe(true)`,
`expect(input).not.toHaveFocus();`,
`expect(input).toHaveFocus();`,
`expect(document.activeElement).toBeNull()`,
diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js
index 05ff2f5..adc3a8b 100644
--- a/src/__tests__/lib/rules/prefer-in-document.js
+++ b/src/__tests__/lib/rules/prefer-in-document.js
@@ -27,6 +27,7 @@ function invalidCase(code, output) {
}
const valid = [
+ "expect().toBe(true)",
...["getByText", "getByRole"].map((q) => [
`expect(screen.${q}('foo')).toBeInTheDocument()`,
`expect(${q}('foo')).toBeInTheDocument()`,
@@ -50,6 +51,10 @@ const valid = [
expect(foo).not.toHaveLength(0)`,
`let foo;
expect(foo).toHaveLength(1);`,
+ `let foo;
+ expect(foo).toHaveLength()`,
+ `let foo;
+ expect(foo).toHaveLength(1, 2, 3)`,
`expect(screen.notAQuery('foo-bar')).toHaveLength(1)`,
`expect(screen.getAllByText('foo-bar')).toHaveLength(2)`,
`import foo from "./foo";
@@ -87,8 +92,93 @@ const valid = [
`expect(screen.getAllByText("foo")).toHaveLength(getLength())`,
`expect(screen.getAllByText("foo")).toBe(foo)`,
`expect(screen.getAllByText("foo")).toEqual(foo)`,
+ `
+ const element = getByText('value')
+ expect(element).toBeTruthy`,
+ `
+ const element = getByText('value')
+ expect(element).toBe.truthy`,
+ `
+ const element = getByText('value')
+ expect(element).toBeInTheDocument`,
];
const invalid = [
+ invalidCase(
+ `expect(screen.getByText('foo')).toHaveLength()`,
+ `expect(screen.getByText('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getAllByText('foo')).toHaveLength()`,
+ `expect(screen.getByText('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getByRole('foo')).toHaveLength()`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength()`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getByRole('foo')).toHaveLength(0,2,3)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(0,2,3,)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getByRole('foo')).toHaveLength(1,2,3)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(1,2,3,)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(0//comment
+)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(1,//comment
+)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(0,2,3//comment
+)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(1,2,3,//comment
+)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(0,2,//comment
+3,4)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(1,2,//comment
+3,4,)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument(//comment
+)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(0,2/*comment*/,3)`,
+ `expect(screen.getByRole('foo')).not.toBeInTheDocument(/*comment*/)`
+ ),
+ invalidCase(
+ `expect(screen.getAllByRole('foo')).toHaveLength(1,2,/*comment*/3,)`,
+ `expect(screen.getByRole('foo')).toBeInTheDocument(/*comment*/)`
+ ),
invalidCase(
`expect(screen.getByText('foo')).toHaveLength(1)`,
`expect(screen.getByText('foo')).toBeInTheDocument()`
@@ -268,6 +358,22 @@ const invalid = [
`expect(queryByText('foo')) .not .toBeDefined()`,
`expect(queryByText('foo')) .not .toBeInTheDocument()`
),
+ invalidCase(
+ `expect(queryByText('foo')).toBeFalsy()`,
+ `expect(queryByText('foo')).not.toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(queryByText('foo')).not.toBeFalsy()`,
+ `expect(queryByText('foo')).toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(queryByText('foo')).toBeTruthy()`,
+ `expect(queryByText('foo')).toBeInTheDocument()`
+ ),
+ invalidCase(
+ `expect(queryByText('foo')).not.toBeTruthy()`,
+ `expect(queryByText('foo')).not.toBeInTheDocument()`
+ ),
invalidCase(
`let foo;
foo = screen.queryByText('foo');
diff --git a/src/__tests__/lib/rules/prefer-prefer-to-have-class.js b/src/__tests__/lib/rules/prefer-prefer-to-have-class.js
index 3f1f58a..f06b496 100644
--- a/src/__tests__/lib/rules/prefer-prefer-to-have-class.js
+++ b/src/__tests__/lib/rules/prefer-prefer-to-have-class.js
@@ -5,6 +5,7 @@ const errors = [{ messageId: "use-to-have-class" }];
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-class", rule, {
valid: [
+ `expect().toBe(true)`,
`const el = screen.getByText("foo"); expect(el).toHaveClass("bar")`,
`const el = screen.getByText("foo"); expect(el.class).toEqual(foo)`,
`const el = screen.getByText("foo"); expect(el).toHaveAttribute("class")`,
diff --git a/src/__tests__/lib/rules/prefer-to-have-attribute.js b/src/__tests__/lib/rules/prefer-to-have-attribute.js
index cf0cfe3..e55e03e 100644
--- a/src/__tests__/lib/rules/prefer-to-have-attribute.js
+++ b/src/__tests__/lib/rules/prefer-to-have-attribute.js
@@ -18,6 +18,7 @@ import * as rule from "../../../rules/prefer-to-have-attribute";
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-attribute", rule, {
valid: [
+ "expect().toBe(true)",
"expect(element.foo).toBeTruthy()",
"expect(element.getAttributeNode()).toBeNull()",
`expect(element.getAttribute('foo')).toBeGreaterThan(2)`,
@@ -44,8 +45,7 @@ ruleTester.run("prefer-to-have-attribute", rule, {
output: `expect(element).toHaveAttribute('foo', expect.stringContaining('bar'));`,
},
{
- code:
- "expect(element.getAttribute('foo')).toContain(`bar=${encodeURIComponent(baz.id)}`);",
+ code: "expect(element.getAttribute('foo')).toContain(`bar=${encodeURIComponent(baz.id)}`);",
errors: [
{
message: "Use toHaveAttribute instead of asserting on getAttribute",
@@ -72,6 +72,24 @@ ruleTester.run("prefer-to-have-attribute", rule, {
],
output: `expect(getByText("yes")).toHaveAttribute("data-blah", expect.stringMatching(/foo/))`,
},
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toBe("")`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", "")`,
+ },
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toBe('')`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", '')`,
+ },
{
code: `expect(getByText('foo').hasAttribute('foo')).toBe(null)`,
errors: [
@@ -171,6 +189,24 @@ ruleTester.run("prefer-to-have-attribute", rule, {
],
output: 'expect(element).toHaveAttribute("foo", "bar")',
},
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toEqual("")`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", "")`,
+ },
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toEqual('')`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", '')`,
+ },
{
code: 'expect(element.getAttribute("foo")).toStrictEqual("bar")',
errors: [
@@ -180,6 +216,24 @@ ruleTester.run("prefer-to-have-attribute", rule, {
],
output: 'expect(element).toHaveAttribute("foo", "bar")',
},
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toStrictEqual("")`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", "")`,
+ },
+ {
+ code: `expect(getByText("yes").getAttribute("data-blah")).toStrictEqual('')`,
+ errors: [
+ {
+ message: "Use toHaveAttribute instead of asserting on getAttribute",
+ },
+ ],
+ output: `expect(getByText("yes")).toHaveAttribute("data-blah", '')`,
+ },
{
code: 'expect(element.getAttribute("foo")).toBe(null)',
errors: [
diff --git a/src/__tests__/lib/rules/prefer-to-have-style.js b/src/__tests__/lib/rules/prefer-to-have-style.js
index 9c8c21a..ac2b947 100644
--- a/src/__tests__/lib/rules/prefer-to-have-style.js
+++ b/src/__tests__/lib/rules/prefer-to-have-style.js
@@ -7,9 +7,14 @@ const errors = [
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-style", rule, {
valid: [
+ `expect().toBe(true)`,
`expect(el).toHaveStyle({foo:"bar"})`,
`expect(el.style).toMatchSnapshot()`,
+ `expect(el.style).toEqual(1)`,
`expect(el.style).toEqual(foo)`,
+ `expect(el.style[1]).toEqual([])`,
+ `expect(el.style[1]).toEqual({})`,
+ `expect(element.style[0]).toBe(new RegExp('reg'));`,
`expect(el).toHaveAttribute("style")`,
`React.useLayoutEffect(() => {
if (foo) {
@@ -131,5 +136,36 @@ ruleTester.run("prefer-to-have-style", rule, {
errors,
output: `expect(imageElement).not.toHaveStyle(\`box-shadow: inset 0px 0px 0px 400px 40px\`)`,
},
+ {
+ code: `expect(element.style[1]).toEqual('padding');`,
+ errors,
+ output: `expect(element).toHaveStyle({padding: expect.anything()});`,
+ },
+ {
+ code: `expect(element.style[1]).toBe(\`padding\`);`,
+ errors,
+ output: `expect(element).toHaveStyle({[\`padding\`]: expect.anything()});`,
+ },
+ {
+ code: `expect(element.style[1]).not.toEqual('padding');`,
+ errors,
+ },
+ {
+ code: `expect(element.style[1]).not.toBe(\`padding\`);`,
+ errors,
+ },
+ {
+ code: `expect(element.style[1]).toBe(x);`,
+ errors,
+ output: `expect(element).toHaveStyle({[x]: expect.anything()});`,
+ },
+ {
+ code: `expect(element.style[0]).toBe(1);`,
+ errors,
+ },
+ {
+ code: `expect(element.style[0]).toBe(/RegExp/);`,
+ errors,
+ },
],
});
diff --git a/src/__tests__/lib/rules/prefer-to-have-text-content.js b/src/__tests__/lib/rules/prefer-to-have-text-content.js
index 9a8b146..1d47704 100644
--- a/src/__tests__/lib/rules/prefer-to-have-text-content.js
+++ b/src/__tests__/lib/rules/prefer-to-have-text-content.js
@@ -18,6 +18,7 @@ import * as rule from "../../../rules/prefer-to-have-text-content";
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-text-content", rule, {
valid: [
+ `expect().toBe(true)`,
`expect(string).toBe("foo")`,
`expect(element).toHaveTextContent("foo")`,
`expect(container.lastNode).toBe("foo")`,
diff --git a/src/__tests__/lib/rules/prefer-to-have-value.js b/src/__tests__/lib/rules/prefer-to-have-value.js
index d965fbf..aed17c6 100644
--- a/src/__tests__/lib/rules/prefer-to-have-value.js
+++ b/src/__tests__/lib/rules/prefer-to-have-value.js
@@ -20,6 +20,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
const errors = [{ messageId: "use-to-have-value" }];
ruleTester.run("prefer-to-have-value", rule, {
valid: [
+ `expect().toBe(true)`,
`expect(screen.getByRole("radio").value).toEqual("foo")`,
`expect(screen.queryAllByRole("checkbox")[0].value).toStrictEqual("foo")`,
`async function x() { expect((await screen.findByRole("button")).value).toBe("foo") }`,
@@ -51,6 +52,10 @@ ruleTester.run("prefer-to-have-value", rule, {
`const element = { value: 'foo' };
expect(element.value).not.toBe('foo');`,
+ `
+ const res = makePath()();
+ expect(res.value).toEqual('/repositories/create');
+ `,
],
invalid: [
{
diff --git a/src/assignment-ast.js b/src/assignment-ast.js
index dc8a9c8..4f5e987 100644
--- a/src/assignment-ast.js
+++ b/src/assignment-ast.js
@@ -74,7 +74,9 @@ export function getQueryNodeFrom(context, nodeWithValueProp) {
};
}
- const query = queryNode.callee.name || queryNode.callee.property.name;
+ const query =
+ queryNode.callee.name ||
+ (queryNode.callee.property && queryNode.callee.property.name);
const queryArg = queryNode.arguments[0] && queryNode.arguments[0].value;
const isDTLQuery = queries.includes(query);
diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js
index e6f4374..d9a5618 100644
--- a/src/rules/prefer-in-document.js
+++ b/src/rules/prefer-in-document.js
@@ -26,6 +26,7 @@ export const meta = {
function isAntonymMatcher(matcherNode, matcherArguments) {
return (
matcherNode.name === "toBeNull" ||
+ matcherNode.name === "toBeFalsy" ||
usesToBeOrToEqualWithNull(matcherNode, matcherArguments) ||
usesToHaveLengthZero(matcherNode, matcherArguments)
);
@@ -39,11 +40,17 @@ function usesToBeOrToEqualWithNull(matcherNode, matcherArguments) {
}
function usesToHaveLengthZero(matcherNode, matcherArguments) {
- return matcherNode.name === "toHaveLength" && matcherArguments[0].value === 0;
+ // matcherArguments.length === 0: toHaveLength() will cause jest matcher error
+ // matcherArguments[0].value: toHaveLength(0, ...) means zero length
+ return (
+ matcherNode.name === "toHaveLength" &&
+ (matcherArguments.length === 0 || matcherArguments[0].value === 0)
+ );
}
export const create = (context) => {
- const alternativeMatchers = /^(toHaveLength|toBeDefined|toBeNull|toBe|toEqual)$/;
+ const alternativeMatchers =
+ /^(toHaveLength|toBeDefined|toBeNull|toBe|toEqual|toBeTruthy|toBeFalsy)$/;
function getLengthValue(matcherArguments) {
let lengthValue;
@@ -69,6 +76,10 @@ export const create = (context) => {
negatedMatcher,
expect,
}) {
+ if (matcherNode.parent.parent.type !== "CallExpression") {
+ return;
+ }
+
// only report on dom nodes which we can resolve to RTL queries.
if (!queryNode || (!queryNode.name && !queryNode.property)) return;
@@ -111,6 +122,12 @@ export const create = (context) => {
// Remove any arguments in the matcher
for (const argument of Array.from(matcherArguments)) {
+ const sourceCode = context.getSourceCode();
+ const token = sourceCode.getTokenAfter(argument);
+ if (token.value === "," && token.type === "Punctuator") {
+ // Remove commas if toHaveLength had more than one argument or a trailing comma
+ operations.push(fixer.replaceText(token, ""));
+ }
operations.push(fixer.remove(argument));
}
@@ -215,6 +232,11 @@ export const create = (context) => {
node
) {
const arg = node.callee.object.arguments[0];
+
+ if (!arg) {
+ return;
+ }
+
const queryNode =
arg.type === "AwaitExpression" ? arg.argument.callee : arg.callee;
const matcherNode = node.callee.property;
diff --git a/src/rules/prefer-to-have-attribute.js b/src/rules/prefer-to-have-attribute.js
index 89dfcd9..5c0b16d 100644
--- a/src/rules/prefer-to-have-attribute.js
+++ b/src/rules/prefer-to-have-attribute.js
@@ -64,15 +64,14 @@ export const create = (context) => ({
node
) {
const arg = node.parent.parent.parent.arguments;
- const isNullOrEmpty =
- arg.length > 0 && (arg[0].value === null || arg[0].value === "");
+ const isNull = arg.length > 0 && arg[0].value === null;
const sourceCode = context.getSourceCode();
context.report({
node: node.parent,
message: `Use toHaveAttribute instead of asserting on getAttribute`,
fix: (fixer) => {
- const lastFixer = isNullOrEmpty
+ const lastFixer = isNull
? fixer.replaceText(
node.parent.parent.parent.arguments[0],
sourceCode.getText(node.arguments[0])
@@ -86,7 +85,7 @@ export const create = (context) => ({
fixer.removeRange([node.callee.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
- `${isNullOrEmpty ? "not." : ""}toHaveAttribute`
+ `${isNull ? "not." : ""}toHaveAttribute`
),
lastFixer,
];
diff --git a/src/rules/prefer-to-have-class.js b/src/rules/prefer-to-have-class.js
index 0803455..ce950d5 100644
--- a/src/rules/prefer-to-have-class.js
+++ b/src/rules/prefer-to-have-class.js
@@ -116,14 +116,18 @@ export const create = (context) => ({
classValue.type === "CallExpression" &&
classValue.callee.type === "MemberExpression" &&
classValue.callee.object.name === "expect"
- )
+ ) {
return;
+ }
+
context.report({
node: matcher,
messageId,
fix(fixer) {
- if (checkedProp.name === "classList" && matcher.name !== "toContain")
+ if (checkedProp.name === "classList" && matcher.name !== "toContain") {
return;
+ }
+
return [
fixer.removeRange([classNameProp.range[1], checkedProp.range[1]]),
fixer.replaceText(matcher, "toHaveClass"),
@@ -182,8 +186,9 @@ export const create = (context) => ({
node: matcher,
messageId,
fix(fixer) {
- if (className.name === "classList" && matcher.name !== "toContain")
+ if (className.name === "classList" && matcher.name !== "toContain") {
return;
+ }
return [
fixer.removeRange([classNameProp.range[1], className.range[1]]),
@@ -215,8 +220,9 @@ export const create = (context) => ({
if (
(matcher.name === "toHaveAttribute" && classNameValue !== "class") ||
(matcher.name === "toHaveProperty" && classNameValue !== "className")
- )
+ ) {
return;
+ }
const { isDTLQuery } = getQueryNodeFrom(
context,
@@ -255,8 +261,10 @@ export const create = (context) => ({
if (
(matcher.name === "toHaveAttribute" && classNameValue !== "class") ||
(matcher.name === "toHaveProperty" && classNameValue !== "className")
- )
+ ) {
return;
+ }
+
const { isDTLQuery } = getQueryNodeFrom(
context,
node.callee.object.object.arguments[0]
@@ -295,8 +303,9 @@ export const create = (context) => ({
if (
(matcher.name === "toHaveAttribute" && classNameValue !== "class") ||
(matcher.name === "toHaveProperty" && classNameValue !== "className")
- )
+ ) {
return;
+ }
const { isDTLQuery } = getQueryNodeFrom(
context,
diff --git a/src/rules/prefer-to-have-style.js b/src/rules/prefer-to-have-style.js
index 4ee2e9e..1d0dead 100644
--- a/src/rules/prefer-to-have-style.js
+++ b/src/rules/prefer-to-have-style.js
@@ -18,6 +18,13 @@ export const meta = {
};
export const create = (context) => {
+ function getReplacementObjectProperty(styleName) {
+ if (styleName.type === "Literal") {
+ return camelCase(styleName.value);
+ }
+
+ return `[${context.getSourceCode().getText(styleName)}]`;
+ }
function getReplacementStyleParam(styleName, styleValue) {
return styleName.type === "Literal"
? `{${camelCase(styleName.value)}: ${context
@@ -148,7 +155,7 @@ export const create = (context) => {
},
//expect(el.style["foo-bar"]).toBe("baz")
- [`MemberExpression[property.name=style][parent.computed=true][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.arguments.0.type=/(Template)?Literal/][parent.parent.callee.name=expect]`](
+ [`MemberExpression[property.name=style][parent.computed=true][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.arguments.0.type=/((Template)?Literal|Identifier)/][parent.parent.callee.name=expect]`](
node
) {
const styleName = node.parent.property;
@@ -157,10 +164,14 @@ export const create = (context) => {
const startOfStyleMemberExpression = node.object.range[1];
const endOfStyleMemberExpression =
node.parent.parent.arguments[0].range[1];
- context.report({
- node: node.property,
- message: "Use toHaveStyle instead of asserting on element style",
- fix(fixer) {
+
+ let fix = null;
+
+ if (
+ typeof styleValue.value !== "number" &&
+ !(styleValue.value instanceof RegExp)
+ ) {
+ fix = (fixer) => {
return [
fixer.removeRange([
startOfStyleMemberExpression,
@@ -169,10 +180,20 @@ export const create = (context) => {
fixer.replaceText(matcher, "toHaveStyle"),
fixer.replaceText(
styleValue,
- getReplacementStyleParam(styleName, styleValue)
+ typeof styleName.value === "number"
+ ? `{${getReplacementObjectProperty(
+ styleValue
+ )}: expect.anything()}`
+ : getReplacementStyleParam(styleName, styleValue)
),
];
- },
+ };
+ }
+
+ context.report({
+ node: node.property,
+ message: "Use toHaveStyle instead of asserting on element style",
+ fix,
});
},
//expect(el.style["foo-bar"]).not.toBe("baz")
@@ -185,10 +206,10 @@ export const create = (context) => {
const endOfStyleMemberExpression =
node.parent.parent.arguments[0].range[1];
- context.report({
- node: node.property,
- message: "Use toHaveStyle instead of asserting on element style",
- fix(fixer) {
+ let fix = null;
+
+ if (typeof styleName.value !== "number") {
+ fix = (fixer) => {
return [
fixer.removeRange([
node.object.range[1],
@@ -200,7 +221,13 @@ export const create = (context) => {
getReplacementStyleParam(styleName, styleValue)
),
];
- },
+ };
+ }
+
+ context.report({
+ node: node.property,
+ message: "Use toHaveStyle instead of asserting on element style",
+ fix,
});
},
//expect(foo.style).toHaveProperty("foo", "bar")
@@ -225,9 +252,9 @@ export const create = (context) => {
fixer.replaceText(matcher, "toHaveStyle"),
fixer.replaceTextRange(
[styleName.range[0], styleValue.range[1]],
- `{${camelCase(
- styleName.value
- )}: ${context.getSourceCode().getText(styleValue)}}`
+ `{${camelCase(styleName.value)}: ${context
+ .getSourceCode()
+ .getText(styleValue)}}`
),
];
},
@@ -238,10 +265,8 @@ export const create = (context) => {
[`MemberExpression[property.name=style][parent.parent.property.name=not][parent.parent.parent.property.name=toHaveProperty][parent.callee.name=expect]`](
node
) {
- const [
- styleName,
- styleValue,
- ] = node.parent.parent.parent.parent.arguments;
+ const [styleName, styleValue] =
+ node.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.property;
context.report({
@@ -259,9 +284,9 @@ export const create = (context) => {
fixer.replaceText(matcher, "toHaveStyle"),
fixer.replaceTextRange(
[styleName.range[0], styleValue.range[1]],
- `{${camelCase(
- styleName.value
- )}: ${context.getSourceCode().getText(styleValue)}}`
+ `{${camelCase(styleName.value)}: ${context
+ .getSourceCode()
+ .getText(styleValue)}}`
),
];
},