Skip to content

Commit 50735ec

Browse files
committed
Extract from other project
0 parents  commit 50735ec

Some content is hidden

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

57 files changed

+1328
-0
lines changed

.eslintrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const neutrino = require('neutrino');
2+
3+
module.exports = neutrino().eslintrc();

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
.eslintcache
3+
node_modules/

.neutrinorc.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
const airbnbBase = require('@neutrinojs/airbnb-base');
2+
const library = require('@neutrinojs/library');
3+
const jest = require('@neutrinojs/jest');
4+
const typescript = require('neutrinojs-typescript');
5+
const typescriptLint = require('neutrinojs-typescript-eslint');
6+
7+
module.exports = {
8+
options: {
9+
root: __dirname,
10+
mains: {
11+
index: 'index',
12+
'extend-expect': 'extend-expect',
13+
},
14+
},
15+
use: [
16+
typescript({ tsconfig: {
17+
compilerOptions: {
18+
strict: true,
19+
declaration: true,
20+
},
21+
} }),
22+
typescriptLint(),
23+
airbnbBase({
24+
eslint: {
25+
rules: {
26+
'arrow-parens': ['error', 'always'],
27+
'operator-linebreak': ['error', 'after'],
28+
'import/prefer-default-export': ['off'],
29+
'@typescript-eslint/indent': ['error', 2],
30+
'@typescript-eslint/await-thenable': ['error'],
31+
'@typescript-eslint/member-ordering': ['error'],
32+
'@typescript-eslint/no-for-in-array': ['error'],
33+
'@typescript-eslint/no-namespace': ['error', { allowDeclarations: true }],
34+
'@typescript-eslint/no-require-imports': ['error'],
35+
'@typescript-eslint/no-this-alias': ['error'],
36+
'@typescript-eslint/no-unnecessary-qualifier': ['error'],
37+
'@typescript-eslint/no-unnecessary-type-assertion': ['error'],
38+
'@typescript-eslint/no-non-null-assertion': ['off'],
39+
'@typescript-eslint/prefer-function-type': ['error'],
40+
'@typescript-eslint/prefer-includes': ['error'],
41+
'@typescript-eslint/prefer-regexp-exec': ['error'],
42+
'@typescript-eslint/prefer-string-starts-ends-with': ['error'],
43+
'@typescript-eslint/require-array-sort-compare': ['error', { ignoreStringArrays: true }],
44+
'@typescript-eslint/restrict-plus-operands': ['error'],
45+
'@typescript-eslint/unbound-method': ['error'],
46+
'@typescript-eslint/explicit-function-return-type': ['error', {
47+
'allowTypedFunctionExpressions': true,
48+
'allowHigherOrderFunctions': true,
49+
}],
50+
'@typescript-eslint/no-parameter-properties': ['error', {
51+
'allows': ['private readonly', 'protected readonly'],
52+
}],
53+
'jest/expect-expect': ['off'],
54+
},
55+
baseConfig: {
56+
overrides: [{
57+
files: ['**/*.test.*', 'test/**/*'],
58+
rules: {
59+
'import/no-extraneous-dependencies': ['error', {
60+
'devDependencies': true,
61+
}],
62+
'@typescript-eslint/explicit-function-return-type': ['off'],
63+
'@typescript-eslint/explicit-module-boundary-types': ['off'],
64+
},
65+
}],
66+
},
67+
},
68+
}),
69+
library({
70+
name: 'flexible-testing-library-react',
71+
target: 'node',
72+
babel: {
73+
presets: [
74+
['@babel/preset-env', {
75+
useBuiltIns: false,
76+
targets: {
77+
node: '12.13',
78+
},
79+
}],
80+
],
81+
plugins: ['@babel/plugin-transform-react-jsx'],
82+
},
83+
}),
84+
jest({
85+
testEnvironment: 'jsdom',
86+
}),
87+
],
88+
};

.npmrc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Library project (no package locking)
2+
package-lock=false

.vscode/settings.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"editor.tabSize": 2,
3+
"files.exclude": {
4+
"**/.eslintcache": true,
5+
"**/build": true,
6+
"**/node_modules": true
7+
},
8+
"javascript.preferences.quoteStyle": "single",
9+
"typescript.preferences.quoteStyle": "single"
10+
}

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 David Evans
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# Flexible React Testing Library
2+
3+
A thin wrapper around [React Testing Library](https://github.com/testing-library/react-testing-library)
4+
which makes using custom queries easier.
5+
6+
See [this PR](https://github.com/testing-library/dom-testing-library/issues/266) for the
7+
discussion behind this and for reasoning why this isn't in core `@testing-library/dom`.
8+
9+
## Install dependency
10+
11+
```bash
12+
npm install --save-dev flexible-testing-library-react
13+
```
14+
15+
This assumes you are using Jest for testing.
16+
17+
## Usage
18+
19+
This mostly follows the API of React Testing Library but with one important difference:
20+
21+
```jsx
22+
// old
23+
import { screen, render } from '@testing-library/react';
24+
render(<MyComponent />);
25+
screen.getByLabelText('foo').something();
26+
27+
// new
28+
import { screen, render, labelText } from 'flexible-testing-library-react';
29+
render(<MyComponent />);
30+
screen.getBy(labelText('foo')).something();
31+
```
32+
33+
Or the alternative (scoped) syntax:
34+
35+
```jsx
36+
// old
37+
import { render } from '@testing-library/react';
38+
const { getByLabelText } = render(<MyComponent />);
39+
getByLabelText('foo').something();
40+
41+
// new
42+
import { render, labelText } from 'flexible-testing-library-react';
43+
const { getBy } = render(<MyComponent />);
44+
getBy(labelText('foo')).something();
45+
```
46+
47+
Also parameters for `findBy` now live in a more logical place:
48+
49+
```jsx
50+
// old
51+
import { screen, render } from '@testing-library/react';
52+
render(<MyComponent />);
53+
screen.findByTitle('foo', {}, { timeout: 1000 }).something();
54+
55+
// new
56+
import { screen, render, title } from 'flexible-testing-library-react';
57+
render(<MyComponent />);
58+
screen.findBy(title('foo'), { timeout: 1000 }).something();
59+
// no need to pass the empty {} argument to title() any more!
60+
```
61+
62+
A new Jest matcher is also available:
63+
64+
```jsx
65+
import { screen, render, labelText } from 'flexible-testing-library-react';
66+
import 'flexible-testing-library-react/extend-expect';
67+
68+
render(<MyComponent />);
69+
expect(screen).toContainElementWith(labelText('foo'));
70+
expect(screen).not.toContainElementWith(labelText('nope'));
71+
```
72+
73+
(this matcher improves on `toBeInTheDocument`, which has
74+
[problems with negation](https://github.com/testing-library/jest-dom/issues/106))
75+
76+
## Reference
77+
78+
### `getBy`
79+
80+
```javascript
81+
getBy(title('hello'))
82+
```
83+
84+
Returns en element, or throws an exception if no elements were found (or multiple elements
85+
matched).
86+
87+
### `getAllBy`
88+
89+
```javascript
90+
getAllBy(title('hello'))
91+
```
92+
93+
Returns a list of elements, or throws an exception if no elements were found.
94+
95+
### `queryBy`
96+
97+
```javascript
98+
queryBy(title('hello'))
99+
```
100+
101+
Returns an element, or `null` if no elements were found, or throws an exception if multiple
102+
elements matched.
103+
104+
### `queryAllBy`
105+
106+
```javascript
107+
queryAllBy(title('hello'))
108+
```
109+
110+
Returns a list of elements (which could be empty).
111+
112+
### `findBy`
113+
114+
```javascript
115+
await findBy(title('hello'))
116+
await findBy(title('hello'), { timeout: 1000 })
117+
```
118+
119+
Waits until at least one matching element exists and returns it, or throws if the
120+
timeout is reached before a matching element is found. Throws if multiple elements
121+
match.
122+
123+
The second parameter is an optional options dictionary, which is passed directly to
124+
DOM Testing Library's [`waitFor`](https://testing-library.com/docs/dom-testing-library/api-async#waitfor).
125+
126+
### `findAllBy`
127+
128+
```javascript
129+
await findAllBy(title('hello'))
130+
await findAllBy(title('hello'), { timeout: 1000 })
131+
```
132+
133+
Waits until at least one matching element exists and returns a list of all matches,
134+
or throws if the timeout is reached before a matching element is found.
135+
136+
The second parameter is an optional options dictionary, which is passed directly to
137+
DOM Testing Library's [`waitFor`](https://testing-library.com/docs/dom-testing-library/api-async#waitfor).
138+
139+
### Queries
140+
141+
For a list of supported queries, see the
142+
[DOM Testing Library documentation](https://testing-library.com/docs/dom-testing-library/api-queries#queries);
143+
each query is available here.
144+
145+
Examples (note that the options can be omitted but are shown here to demonstrate their usage):
146+
147+
| Function | Example | Upstream Docs |
148+
|----------|---------|---------------|
149+
| `labelText` | `getAllBy(labelText('hello', { exact: false }))` | [ByLabelText](https://testing-library.com/docs/dom-testing-library/api-queries#bylabeltext) |
150+
| `placeholderText` | `getAllBy(placeholderText('hello', { exact: false }))` | [ByPlaceholderText](https://testing-library.com/docs/dom-testing-library/api-queries#byplaceholdertext) |
151+
| `text` | `getAllBy(text('hello', { exact: false }))` | [ByText](https://testing-library.com/docs/dom-testing-library/api-queries#bytext) |
152+
| `altText` | `getAllBy(altText('hello', { exact: false }))` | [ByAltText](https://testing-library.com/docs/dom-testing-library/api-queries#byalttext) |
153+
| `title` | `getAllBy(title('hello', { exact: false }))` | [ByTitle](https://testing-library.com/docs/dom-testing-library/api-queries#bytitle) |
154+
| `displayValue` | `getAllBy(displayValue('hello', { exact: false }))` | [ByDisplayValue](https://testing-library.com/docs/dom-testing-library/api-queries#bydisplayvalue) |
155+
| `role` | `getAllBy(role('tab', { selected: true }))` | [ByRole](https://testing-library.com/docs/dom-testing-library/api-queries#byrole) |
156+
| `testId` | `getAllBy(testId('hello', { exact: false }))` | [ByTestId](https://testing-library.com/docs/dom-testing-library/api-queries#bytestid) |
157+
158+
As a convenience another query is available as a shorthand:
159+
160+
| Function | Description | Example |
161+
|----------|-------------|---------|
162+
| `textFragment` | Same as `text` with `exact: false` in the options. | `getAllBy(textFragment('hello'))` |
163+
164+
For other features, see the main [React Testing Library documentation](https://testing-library.com/docs/react-testing-library/intro).
165+
166+
## Writing custom queries
167+
168+
```javascript
169+
const positionInTable = (column, row) => ({ // parameters can be anything you like
170+
description: `in column ${column}, row ${row}`,
171+
queryAll: (container) => {
172+
// your query implementation here:
173+
const rowElement = container.querySelectorAll('tr')[row];
174+
if (!rowElement) {
175+
return [];
176+
}
177+
const cellElement = rowElement.querySelectorAll('td')[column];
178+
if (!cellElement) {
179+
return [];
180+
}
181+
return [cellElement]; // always return a list, even if there is only one element
182+
},
183+
});
184+
```
185+
186+
This can now be used easily with any of
187+
`getBy`, `getAllBy`, `findBy`, `findAllBy`, `queryBy`, `queryAllBy`:
188+
189+
```jsx
190+
import { screen, render } from 'flexible-testing-library-react';
191+
import { positionInTable } from './positionInTable';
192+
193+
render(<MyComponent />);
194+
screen.getBy(positionInTable(2, 3)).something();
195+
```
196+
197+
And can be used with `toContainElementWith`:
198+
199+
```jsx
200+
import { screen, render } from 'flexible-testing-library-react';
201+
import 'flexible-testing-library-react/extend-expect';
202+
import { positionInTable } from './positionInTable';
203+
204+
render(<MyComponent />);
205+
expect(screen).toContainElementWith(positionInTable(2, 3));
206+
```
207+
208+
### Optional query configuration
209+
210+
As well as a `description` and `queryAll`, you can provide some other (optional)
211+
configuration:
212+
213+
- `multipleErrorDetail`: a string to include in error messages about finding too many
214+
matching elements.
215+
- `missingErrorDetail`: a string to include in error messages about not finding any
216+
element.
217+
- `getAll`: a version of `queryAll` which throws if no elements are found (can be used
218+
to provide more detailed error information). Note that you _must_ specify `queryAll`,
219+
even if you also provide `getAll`.
220+
221+
### TypeScript
222+
223+
The types for custom queries are:
224+
225+
```typescript
226+
import type Query from 'flexible-testing-library-react';
227+
228+
const tableCell = (row: number, column: number): Query => ({
229+
description: `in column ${column}, row ${row}`,
230+
queryAll: (container): NodeListOf<HTMLElement> | HTMLElement[] => { /* implementation here */ },
231+
});
232+
```

build/baseQueries.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { waitForOptions } from '@testing-library/react';
2+
import type Query from './queries/Query';
3+
export declare const queryAllBy: (container: HTMLElement, query: Query) => HTMLElement[];
4+
export declare const queryBy: (container: HTMLElement, query: Query) => HTMLElement | null;
5+
export declare const getAllBy: (container: HTMLElement, query: Query) => HTMLElement[];
6+
export declare const getBy: (container: HTMLElement, query: Query) => HTMLElement;
7+
export declare const findAllBy: (container: HTMLElement, query: Query, waitOptions: waitForOptions) => Promise<HTMLElement[]>;
8+
export declare const findBy: (container: HTMLElement, query: Query, waitOptions: waitForOptions) => Promise<HTMLElement>;
9+
//# sourceMappingURL=baseQueries.d.ts.map

build/baseQueries.d.ts.map

+1
Original file line numberDiff line numberDiff line change

build/extend-expect.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as extensions from './matchers';
2+
export { extensions };
3+
//# sourceMappingURL=extend-expect.d.ts.map

build/extend-expect.d.ts.map

+1
Original file line numberDiff line numberDiff line change

build/extend-expect.js

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)