├── .babelrc
├── .github
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .tav.yml
├── LICENSE.txt
├── README.md
├── jest.config.js
├── other
└── cricket.png
├── package.json
├── rollup.config.js
├── src
├── __tests__
│ └── select-event.test.tsx
├── act-compat.ts
└── index.ts
├── tsconfig.build.json
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["@babel/plugin-proposal-class-properties"],
3 | "presets": ["@babel/preset-react", "@babel/preset-typescript"],
4 | "env": {
5 | "test": {
6 | "presets": ["@babel/preset-env", "@babel/preset-typescript"],
7 | "plugins": ["@babel/plugin-transform-runtime"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | ignore:
9 | - dependency-name: "@types/react-select"
10 | versions:
11 | - 4.0.11
12 | - 4.0.12
13 | - 4.0.7
14 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - '+([0-9])?(.{+([0-9]),x}).x'
6 | - 'main'
7 | - 'master'
8 | - 'ci'
9 | - 'test'
10 | - 'next'
11 | - 'next-major'
12 | - 'beta'
13 | - 'alpha'
14 | - '!all-contributors/**'
15 | pull_request: {}
16 | jobs:
17 | build:
18 | runs-on: ubuntu-latest
19 | strategy:
20 | matrix:
21 | node: ['14', '16']
22 | name: Build & test (Node ${{ matrix.node }})
23 | steps:
24 | - uses: actions/checkout@v2
25 | - name: Setup node
26 | uses: actions/setup-node@v2
27 | with:
28 | node-version: ${{ matrix.node }}
29 |
30 | # Install & build & test:
31 | - run: npm install
32 | - run: npm run type:check
33 | - run: npm run prettier:check
34 | - run: npm run test:all-versions
35 | - run: npm run build
36 | - run: npm pack
37 |
38 | coverage:
39 | needs: build
40 | runs-on: ubuntu-latest
41 | steps:
42 | - uses: actions/checkout@v2
43 | - name: Setup node
44 | uses: actions/setup-node@v2
45 | with:
46 | node-version: 16
47 | - run: npm install
48 | - run: npm install -g codecov
49 | - run: npm test -- --coverage # enforce 100% coverage when testing against the latest react-select
50 | - run: codecov
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | lib
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .babelrc
2 | .git
3 | .gitignore
4 | .npmignore
5 | .npmrc
6 | .prettierrc
7 | .tav.yml
8 | *.tgz
9 | coverage
10 | jest.config.js
11 | node_modules
12 | other
13 | rollup.config.js
14 | tsconfig.build.json
15 | tsconfig.json
16 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.tav.yml:
--------------------------------------------------------------------------------
1 | react-select:
2 | - versions: ^2.1 || ^3.0.2
3 | commands: npm test
4 | # we can't test agains 2.0.0 because of https://github.com/JedWatson/react-select/pull/2903
5 | peerDependencies:
6 | - react@16
7 | - react-dom@16
8 | - versions: ^4 || ^5
9 | commands: npm test
10 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2019 Romain Bertrand
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 | [](https://badge.fury.io/js/react-select-event)
15 | [](https://github.com/romgain/react-select-event/actions)
16 | [](https://codecov.io/gh/romgain/react-select-event)
17 | [](https://github.com/prettier/prettier)
18 |
19 | ## Install
20 |
21 | ```bash
22 | npm install --save-dev react-select-event
23 | ```
24 |
25 | Import `react-select-event` in your unit tests:
26 |
27 | ```js
28 | import selectEvent from "react-select-event";
29 | // or
30 | const selectEvent = require("react-select-event");
31 | ```
32 |
33 | ### Supported versions of `react-select`
34 |
35 | This library is tested against all versions of `react-select` starting from `2.1.0`.
36 |
37 | ## API
38 |
39 | Every helper exported by `react-select-event` takes a handle on the `react-select` input field as its first argument. For instance, this can be: `getByLabelText("Your label name")`.
40 |
41 | ### `select(input: HTMLElement, optionOrOptions: Matcher | Array, config?: object): Promise`
42 |
43 | The `optionOrOptions` parameter can be any valid dom-testing-library [TextMatch](https://testing-library.com/docs/queries/about#textmatch) object (eg. string, regex, function, number).
44 |
45 | Select one or more values in a react-select dropdown.
46 |
47 | ```jsx
48 | const { getByRole, getByLabelText } = render(
49 |
53 | );
54 | expect(getByRole("form")).toHaveFormValues({ food: "" });
55 |
56 | await selectEvent.select(getByLabelText("Food"), ["Strawberry", "Mango"]);
57 | expect(getByRole("form")).toHaveFormValues({ food: ["strawberry", "mango"] });
58 |
59 | await selectEvent.select(getByLabelText("Food"), "Chocolate");
60 | expect(getByRole("form")).toHaveFormValues({
61 | food: ["strawberry", "mango", "chocolate"],
62 | });
63 | ```
64 |
65 | This also works for [async selects](https://react-select.com/async):
66 |
67 | ```jsx
68 | const { getByRole, getByLabelText } = render(
69 |
79 | );
80 | expect(getByRole("form")).toHaveFormValues({ food: "" });
81 |
82 | // start typing to trigger the `loadOptions`
83 | fireEvent.change(getByLabelText("Food"), { target: { value: "Choc" } });
84 | await selectEvent.select(getByLabelText("Food"), "Chocolate");
85 | expect(getByRole("form")).toHaveFormValues({
86 | food: ["chocolate"],
87 | });
88 | ```
89 |
90 | `select` also accepts an optional `config` parameter.
91 | `config.container` can be used to specify a custom container to use when the `react-select` dropdown is rendered
92 | in a portal using `menuPortalTarget`:
93 |
94 | ```jsx
95 | const { getByRole, getByLabelText } = render(
96 |
106 | );
107 | await selectEvent.select(getByLabelText("Food"), ["Strawberry", "Mango"], {
108 | container: document.body,
109 | });
110 | expect(getByRole("form")).toHaveFormValues({ food: ["strawberry", "mango"] });
111 | ```
112 |
113 | The container can also be passed in as a function if it needs to be lazily evaluated:
114 |
115 | ```jsx
116 | const { getByRole, getByLabelText } = render(
117 |
127 | );
128 | await selectEvent.select(getByLabelText("Food"), ["Strawberry", "Mango"], {
129 | container: () => document.body.querySelector("[class$=-menu]"),
130 | });
131 | expect(getByRole("form")).toHaveFormValues({ food: ["strawberry", "mango"] });
132 | ```
133 |
134 | ### `create(input: HTMLElement, option: string, config?: object): Promise }`
135 |
136 | Creates and selects a new item. Only applicable to `react-select` [`Creatable`](https://react-select.com/creatable) elements.
137 |
138 | ```jsx
139 | const { getByRole, getByLabelText } = render(
140 |
144 | );
145 | expect(getByRole("form")).toHaveFormValues({ food: "" });
146 | await selectEvent.create(getByLabelText("Food"), "papaya");
147 | expect(getByRole("form")).toHaveFormValues({ food: "papaya" });
148 | ```
149 |
150 | `create` take an optional `config` parameter:
151 |
152 | - `config.createOptionText` can be used when [creating elements with a custom label text, using the `formatCreateLabel` prop](https://react-select.com/props#creatable-props).
153 | - `config.container` can be used when the `react-select` dropdown is rendered in a portal using `menuPortalTarget`.
154 | - `config.waitForElement` Whether `create` should wait for new option to be populated in the select container. Defaults to `true`.
155 |
156 | ### `clearFirst(input: HTMLElement): Promise`
157 |
158 | Clears the first value in the dropdown.
159 |
160 | ```jsx
161 | const { getByRole, getByLabelText } = render(
162 |
172 | );
173 | expect(getByRole("form")).toHaveFormValues({ food: "chocolate" });
174 | await selectEvent.clearFirst(getByLabelText("Food"));
175 | expect(getByRole("form")).toHaveFormValues({ food: "" });
176 | ```
177 |
178 | ### `clearAll(input: HTMLElement): Promise`
179 |
180 | Clears all values in the dropdown.
181 |
182 | ```jsx
183 | const { getByRole, getByLabelText } = render(
184 |
194 | );
195 | expect(getByRole("form")).toHaveFormValues({
196 | food: ["chocolate", "vanilla", "strawberry"],
197 | });
198 | await selectEvent.clearAll(getByLabelText("Food"));
199 | expect(getByRole("form")).toHaveFormValues({ food: "" });
200 | ```
201 |
202 | ### `openMenu(input: HTMLElement): void`
203 |
204 | Opens the select dropdown menu by focusing the input and simulating a down arrow keypress.
205 |
206 | ```jsx
207 | const { getByLabelText, queryByText } = render(
208 |
212 | );
213 | expect(queryByText("Pizza")).toBeNull();
214 | selectEvent.openMenu(getByLabelText("Food"));
215 | expect(getByText("Pizza")).toBeInTheDocument();
216 | ```
217 |
218 | ## Credits
219 |
220 | All the credit goes to [Daniel](https://stackoverflow.com/users/164268/daniel) and his StackOverflow answer: [https://stackoverflow.com/a/56085734](https://stackoverflow.com/a/56085734).
221 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: "jsdom",
3 | coverageThreshold: {
4 | global: {
5 | branches: 100,
6 | functions: 100,
7 | lines: 100,
8 | statements: 100
9 | }
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/other/cricket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romgain/react-select-event/8619e8b3da349eadfa7321ea4aa2b7eee7209f9f/other/cricket.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-select-event",
3 | "version": "5.5.1",
4 | "description": "Simulate react-select events for react-testing-library",
5 | "main": "lib/react-select-event.cjs.js",
6 | "module": "lib/react-select-event.es.js",
7 | "types": "lib/index.d.ts",
8 | "scripts": {
9 | "clean": "rimraf lib",
10 | "build": "npm run clean && npm run build:lib && npm run build:types",
11 | "build:lib": "rollup -c",
12 | "build:types": "tsc -p tsconfig.build.json",
13 | "prettier:check": "prettier --list-different \"src/**/*.{ts,tsx,js,md}\" \"**/*.md\"",
14 | "prettier:apply": "prettier --write \"src/**/*.{ts,tsx,js}\" \"**/*.md\"",
15 | "type:check": "tsc --noEmit",
16 | "prepublishOnly": "npm run build",
17 | "test": "jest",
18 | "test:all-versions": "tav"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/romgain/react-select-event.git"
23 | },
24 | "keywords": [
25 | "react-testing-library",
26 | "dom-testing-library",
27 | "react-select",
28 | "testing",
29 | "javascript",
30 | "unit-testing"
31 | ],
32 | "author": "Romain Bertrand",
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/romgain/react-select-event/issues"
36 | },
37 | "homepage": "https://github.com/romgain/react-select-event#readme",
38 | "dependencies": {
39 | "@testing-library/dom": ">=7"
40 | },
41 | "devDependencies": {
42 | "@babel/core": "^7.4.5",
43 | "@babel/plugin-proposal-class-properties": "^7.4.4",
44 | "@babel/plugin-transform-runtime": "^7.4.4",
45 | "@babel/preset-env": "^7.4.4",
46 | "@babel/preset-react": "^7.0.0",
47 | "@babel/preset-typescript": "^7.3.3",
48 | "@testing-library/jest-dom": "^5.0.1",
49 | "@testing-library/react": "^12.1.5",
50 | "@types/jest": "^29.1.2",
51 | "@types/react": "^17.0.47",
52 | "@types/react-select": "^5.0.1",
53 | "jest": "^27.0.4",
54 | "prettier": "^2.0.2",
55 | "react": "^17.0.2",
56 | "react-dom": "^17.0.2",
57 | "react-select": "^5.0.0",
58 | "rimraf": "^3.0.0",
59 | "rollup": "^2.0.3",
60 | "rollup-plugin-babel": "^4.3.2",
61 | "rollup-plugin-node-resolve": "^5.0.0",
62 | "test-all-versions": "^5.0.0",
63 | "typescript": "^4.0.2"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from "rollup-plugin-node-resolve";
2 | import babel from "rollup-plugin-babel";
3 | import pkg from "./package.json";
4 |
5 | export default [
6 | {
7 | input: "src/index.ts",
8 | external: ["@testing-library/dom"],
9 | plugins: [
10 | resolve({ extensions: [".js", ".ts"] }),
11 | babel({ extensions: [".js", ".ts"] })
12 | ],
13 | output: [
14 | { file: pkg.main, format: "cjs", exports: "named" },
15 | { file: pkg.module, format: "es" }
16 | ]
17 | }
18 | ];
19 |
--------------------------------------------------------------------------------
/src/__tests__/select-event.test.tsx:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom/extend-expect";
2 |
3 | import { fireEvent, render } from "@testing-library/react";
4 |
5 | import React from "react";
6 | import Select from "react-select";
7 | import selectEvent from "..";
8 |
9 | let Async: any;
10 | let Creatable: any;
11 | let AsyncCreatable: any;
12 | let IS_V2 = false;
13 | try {
14 | // v3
15 | Async = require("react-select/async").default;
16 | Creatable = require("react-select/creatable").default;
17 | AsyncCreatable = require("react-select/async-creatable").default;
18 | } catch (_) {
19 | // v2
20 | Async = require("react-select/lib/Async").default;
21 | Creatable = require("react-select/lib/Creatable").default;
22 | AsyncCreatable = require("react-select/lib/AsyncCreatable").default;
23 | IS_V2 = true;
24 | }
25 |
26 | const skip_on_v2 = IS_V2 ? xit : it;
27 |
28 | type Callback = (options: Options) => void;
29 | interface Option {
30 | label: string;
31 | value: string;
32 | }
33 | type Options = Array