25 |
26 |
27 | );
28 |
29 | expect(
30 | container.querySelector('[data-test-inner-id="title"]')
31 | ).toBeInstanceOf(HTMLHeadingElement);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Dennis Morello
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.
--------------------------------------------------------------------------------
/test/basic.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { render } from '@testing-library/react';
3 | import Test from '../src';
4 |
5 | describe('Basic actions', () => {
6 | it('Renders without crashing', () => {
7 | render(
8 |
9 |
44 |
45 | );
46 |
47 | expect(container.querySelector('[data-test="title"]')).toBeInstanceOf(
48 | HTMLHeadingElement
49 | );
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/src/components/TestAttribute.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactIs from 'react-is';
3 | import defaultConfig from '../config';
4 | import { ConfigInterface } from '../types';
5 | import TestConfigContext from '../test-config-context';
6 |
7 | interface TestAttributeProps extends ConfigInterface {
8 | id: string;
9 | }
10 |
11 | export const TestAttribute: React.FC = ({
12 | children,
13 | ...props
14 | }) => {
15 | const isProduction = process.env.NODE_ENV === 'production';
16 |
17 | const config = Object.assign(
18 | {},
19 | defaultConfig,
20 | React.useContext(TestConfigContext),
21 | props
22 | );
23 |
24 | function withTestAttribute(nodes: React.ReactNode): React.ReactNode {
25 | const node = React.Children.only(nodes);
26 | const testAttributeName = `data-${config.suffix}`;
27 |
28 | if (ReactIs.isFragment(node)) {
29 | return React.createElement(
30 | 'div',
31 | { [testAttributeName]: config.id },
32 | node
33 | );
34 | } else if (ReactIs.isElement(node)) {
35 | return React.cloneElement(node as React.ReactElement, {
36 | [testAttributeName]: config.id,
37 | });
38 | } else {
39 | return node;
40 | }
41 | }
42 |
43 | return (
44 |
45 | {!config.enableInProductionMode && isProduction
46 | ? children
47 | : withTestAttribute(children)}
48 |
49 | );
50 | };
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-test-attributes",
3 | "description": "React library to add data-* attributes to DOM elements.",
4 | "version": "1.0.0",
5 | "license": "MIT",
6 | "author": {
7 | "name": "Dennis Morello",
8 | "email": "dennismorello@gmail.com",
9 | "url": "https://morello.dev"
10 | },
11 | "keywords": [
12 | "react",
13 | "dom",
14 | "e2e",
15 | "data",
16 | "test",
17 | "testing",
18 | "selenium",
19 | "cypress",
20 | "javascript",
21 | "typescript",
22 | "attributes"
23 | ],
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/dennismorello/react-test-attributes"
27 | },
28 | "bugs": {
29 | "url": "https://github.com/dennismorello/react-test-attributes/issues"
30 | },
31 | "main": "dist/index.js",
32 | "module": "dist/react-test-attributes.esm.js",
33 | "typings": "dist/index.d.ts",
34 | "files": [
35 | "dist"
36 | ],
37 | "scripts": {
38 | "start": "tsdx watch",
39 | "build": "tsdx build",
40 | "test": "tsdx test --passWithNoTests",
41 | "lint": "tsdx lint",
42 | "prepare": "tsdx build",
43 | "postpublish": "PACKAGE_VERSION=$(cat package.json | grep \\\"version\\\" | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') && git tag v$PACKAGE_VERSION && git push --tags"
44 | },
45 | "peerDependencies": {
46 | "react": ">=16"
47 | },
48 | "husky": {
49 | "hooks": {
50 | "pre-commit": "tsdx lint"
51 | }
52 | },
53 | "prettier": {
54 | "printWidth": 80,
55 | "semi": true,
56 | "singleQuote": true,
57 | "trailingComma": "es5"
58 | },
59 | "devDependencies": {
60 | "@testing-library/react": "^9.4.0",
61 | "@types/jest": "^24.0.25",
62 | "@types/react": "^16.9.17",
63 | "@types/react-dom": "^16.9.4",
64 | "@types/react-is": "^16.7.1",
65 | "husky": "^3.1.0",
66 | "react": "^16.12.0",
67 | "react-dom": "^16.12.0",
68 | "tsdx": "^0.12.1",
69 | "tslib": "^1.10.0",
70 | "typescript": "^3.7.4"
71 | },
72 | "dependencies": {
73 | "react-is": "^16.12.0"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Test Attributes
2 |
3 | [](https://www.npmjs.com/package/react-test-attributes/v/latest)
4 | [](https://github.com/dennismorello/react-test-attributes/commits/master)
5 | [](https://www.npmjs.com/package/react-test-attributes/v/latest)
6 | [](https://bundlephobia.com/result?p=react-test-attributes@latest)
7 | [](https://www.npmjs.com/package/react-test-attributes/v/latest)
8 |
9 | [React Test Attributes](https://github.com/dennismorello/react-test-attributes) is a library for React apps that decorates the DOM with custom attributes that can be used to uniquely indentify elements in a page. The main use case is for E2E testing using tools like [Cypress](https://www.cypress.io) or [Selenium](https://selenium.dev).
10 |
11 | ## Table Of Contents
12 |
13 | - [Features](#features)
14 | - [Installation](#installation)
15 | - [Quick Start](#quick-start)
16 | - [Usage](#usage)
17 | - [API](#api)
18 | - [Global Configuration](#global-configuration)
19 | - [Contributing](#contributing)
20 | - [License](#license)
21 |
22 | ## Features
23 |
24 | - 🏷 **TypeScript support** - It is written in TypeScript to make it easier and faster to use the library
25 | - 🍃 **Lightweight** - Almost zero footprint on your project and no other dependencies required
26 | - 🚀 **Production mode** - `data-*` attributes are added to the DOM only when not in production mode
27 | - 🌳 **Tree-shakeable** - Only the parts you use will be included in your final bundle
28 |
29 | ## Installation
30 |
31 | To add this package as a dependency to your app, simply run
32 |
33 | ```sh
34 | npm install react-test-attributes --save
35 | ```
36 |
37 | or, if you are using Yarn (as I strongly suggest):
38 |
39 | ```sh
40 | yarn add react-test-attributes
41 | ```
42 |
43 | ## Quick Start
44 |
45 | Import [React Test Attributes](https://www.npmjs.com/package/react-test-attributes) to your React component (note that, since it is the default export of this package, you can name it whatever you want):
46 |
47 | ```js
48 | import Test from 'react-test-attributes';
49 | ```
50 |
51 | Then simply wrap the components you want to decorate:
52 |
53 | ```jsx
54 |
55 |
56 |
57 | ```
58 |
59 | The resulting DOM will be the following, depending on the value of `NODE_ENV` environment variable when your project is built:
60 |
61 | ```html
62 |
63 |
64 |
65 |
66 |
67 | ```
68 |
69 | ### Usage
70 |
71 | #### API
72 |
73 | The `Test` component accepts the following props:
74 |
75 | - `id` is the value of the added attribute
76 | - `suffix` is the string to append to `"data-"` when building the attribute name (default to `"testid"`)
77 | - `enableInProductionMode` indicates whether or not adding the test attribute in production mode (default to `false`)
78 |
79 | For example, if you want to name the attribute `data-tid` and give it the value `"link-home"` you should write:
80 |
81 | ```jsx
82 |
83 | Home
84 |
85 | ```
86 |
87 | This produces the following DOM:
88 |
89 | ```html
90 | Home
91 | ```
92 |
93 | #### Global Configuration
94 |
95 | The context `TestAttributesConfig` can provide a global configuration to all of its `Test` descendants.
96 |
97 | For example, we can globally override the suffix and enable writing the test attributes also in production mode by doing this:
98 |
99 | ```jsx
100 | import Test, { TestAttributesConfig } from 'react-test-attributes';
101 |
102 | const App = () => {
103 | return (
104 |
107 |
108 |
I am the title
109 |
110 |
111 | Home
112 |
113 |
114 | );
115 | };
116 | ```
117 |
118 | This produces the following DOM:
119 |
120 | ```html
121 |
I am the title
122 | Home
123 | ```
124 |
125 | ## Contributing
126 |
127 | If you find any bug or if you have ideas on how to improve this project, you are more than welcome to open issues and/or making pull requests!
128 |
129 | ## License
130 |
131 | Project source code is licensed under the MIT license. You are free to fork this repository, edit the code, share and use it both for non-commercial and commercial purposes.
132 |
--------------------------------------------------------------------------------