diff --git a/.all-contributorsrc b/.all-contributorsrc index 3b25725b..fc50632f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -419,6 +419,18 @@ "contributions": [ "code" ] + }, + { + "login": "RoystonS", + "name": "Royston Shufflebotham", + "avatar_url": "https://avatars0.githubusercontent.com/u/19773?v=4", + "profile": "https://github.com/RoystonS", + "contributions": [ + "bug", + "code", + "doc", + "test" + ] } ] } diff --git a/README.md b/README.md index 6bb9270d..65625191 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,21 @@
+ [![Build Status][build-badge]][build] [![Code Coverage][coverage-badge]][coverage] [![version][version-badge]][package] [![downloads][downloads-badge]][npmtrends] [![MIT License][license-badge]][license] -[![All Contributors](https://img.shields.io/badge/all_contributors-42-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-43-orange.svg?style=flat-square)](#contributors) [![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc] [![Watch on GitHub][github-watch-badge]][github-watch] [![Star on GitHub][github-star-badge]][github-star] [![Tweet][twitter-badge]][twitter] +
@@ -102,6 +104,7 @@ when a real user uses it. - [`within` and `getQueriesForElement` APIs](#within-and-getqueriesforelement-apis) - [Debugging](#debugging) - [`prettyDOM`](#prettydom) +- [Configuration](#configuration) - [Implementations](#implementations) - [Using Without Jest](#using-without-jest) - [FAQ](#faq) @@ -456,6 +459,17 @@ const usernameInputElement = getByTestId(container, 'username-input') > `data-testid`s from the blog post > ["Making your UI tests resilient to change"][data-testid-blog-post] +#### Overriding `data-testid` + +The `...ByTestId` functions in `dom-testing-library` use the attribute `data-testid` +by default, following the precedent set by +[React Native Web](https://github.com/kentcdodds/react-testing-library/issues/1) +which uses a `testID` prop to emit a `data-testid` attribute on the element, +and we recommend you adopt that attribute where possible. +But if you already have an existing codebase that uses a different attribute +for this purpose, you can override this value via +`configure({testIdAttribute: 'data-my-test-attribute'})`. + ### `wait` ```typescript @@ -928,6 +942,19 @@ console.log(prettyDOM(div)) This function is what also powers [the automatic debugging output described above](#debugging). +## Configuration + +The library can be configured via the `configure` function, which accepts: + +- a plain JS object; this will be merged into the existing configuration. e.g. `configure({testIdAttribute: 'not-data-testid'})` +- a function; the function will be given the existing configuration, and should return a plain JS object which will be merged as above, e.g. + `configure(existingConfig => ({something: [...existingConfig.something, 'extra value for the something array']}))` + +Configuration options: + +`testIdAttribute`: The attribute used by `getByTestId` and related queries. +Defaults to `data-testid`. See [`getByTestId`](#getbytestid). + ## Implementations This library was not built to be used on its own. The original implementation @@ -1115,6 +1142,7 @@ Thanks goes to these people ([emoji key][emojis]): | [
Jonathan Stoye](http://jonathanstoye.de)
[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonathanStoye "Documentation") | [
Sanghyeon Lee](https://github.com/yongdamsh)
[💡](#example-yongdamsh "Examples") | [
Justice Mba ](https://github.com/Dajust)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [
Wayne Crouch](https://github.com/wgcrouch)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wgcrouch "Code") | [
Ben Elliott](http://benjaminelliott.co.uk)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=benelliott "Code") | [
Ruben Costa](http://nuances.co)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=rubencosta "Code") | [
Robert Smith](http://rbrtsmith.com/)
[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Arbrtsmith "Bug reports") [🤔](#ideas-rbrtsmith "Ideas, Planning, & Feedback") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=rbrtsmith "Documentation") | | [
dadamssg](https://github.com/dadamssg)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dadamssg "Code") | [
Neil Kistner](https://neilkistner.com/)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wyze "Code") | [
Ben Chauvette](http://bdchauvette.net/)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=bdchauvette "Code") | [
Jeff Baumgardt](https://github.com/JeffBaumgardt)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Documentation") | [
Matan Kushner](http://matchai.me)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Documentation") [🤔](#ideas-matchai "Ideas, Planning, & Feedback") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=matchai "Tests") | [
Alex Wendte](http://www.wendtedesigns.com/)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=themostcolm "Tests") | [
Tamas Fodor](https://github.com/ruffle1986)
[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=ruffle1986 "Documentation") | | [
Benjamin Eckardt](https://github.com/BenjaminEckardt)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=BenjaminEckardt "Code") | [
Ryan Campbell](https://github.com/campbellr)
[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=campbellr "Documentation") | [
Taylor Briggs](https://taylor-briggs.com)
[⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=TaylorBriggs "Tests") | [
John Gozde](https://github.com/jgoz)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jgoz "Code") | [
C. T. Lin](https://github.com/chentsulin)
[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=chentsulin "Documentation") | [
Terrence Wong](http://terrencewwong.com)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=terrencewwong "Code") | [
Soo Jae Hwang](https://www.ossfinder.com)
[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=misoguy "Code") | +| [
Royston Shufflebotham](https://github.com/RoystonS)
[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3ARoystonS "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=RoystonS "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=RoystonS "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=RoystonS "Tests") | diff --git a/src/__tests__/config.js b/src/__tests__/config.js new file mode 100644 index 00000000..40123a53 --- /dev/null +++ b/src/__tests__/config.js @@ -0,0 +1,55 @@ +import {configure, getConfig} from '../config' + +describe('configuration API', () => { + let originalConfig + beforeEach(() => { + // Grab the existing configuration so we can restore + // it at the end of the test + configure(existingConfig => { + originalConfig = existingConfig + // Don't change the existing config + return {} + }) + }) + afterEach(() => { + configure(originalConfig) + }) + + beforeEach(() => { + configure({other: 123}) + }) + + describe('getConfig', () => { + test('returns existing configuration', () => { + const conf = getConfig() + expect(conf.testIdAttribute).toEqual('data-testid') + }) + }) + + describe('configure', () => { + test('merges a delta rather than replacing the whole config', () => { + const conf = getConfig() + expect(conf).toMatchObject({testIdAttribute: 'data-testid'}) + }) + + test('overrides existing values', () => { + configure({testIdAttribute: 'new-id'}) + const conf = getConfig() + expect(conf.testIdAttribute).toEqual('new-id') + }) + + test('passes existing config out to config function', () => { + // Create a new config key based on the value of an existing one + configure(existingConfig => ({ + testIdAttribute: `${existingConfig.testIdAttribute}-derived`, + })) + const conf = getConfig() + + // The new value should be there, and existing values should be + // untouched + expect(conf).toMatchObject({ + testIdAttribute: 'data-testid-derived', + }) + }) + }) +}) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 6f1a1f92..af5e7a10 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -1,4 +1,5 @@ import 'jest-dom/extend-expect' +import {configure} from '../config' import {render, renderIntoDocument} from './helpers/test-utils' import document from './helpers/document' @@ -258,16 +259,34 @@ test('query/get select by text with multiple options selected', () => { expect(queryBySelectText('Alaska').id).toEqual('state-select') }) -test('can get elements by data-testid attribute', () => { - const {queryByTestId} = render(`
`) - expect(queryByTestId('firstName')).toBeTruthy() - expect(queryByTestId(/first/)).toBeTruthy() - expect(queryByTestId(testid => testid === 'firstName')).toBeTruthy() - // match should be exact, case-sensitive - expect(queryByTestId('firstname')).not.toBeTruthy() - expect(queryByTestId('first')).not.toBeTruthy() - expect(queryByTestId('firstNamePlusMore')).not.toBeTruthy() - expect(queryByTestId('first-name')).not.toBeTruthy() +describe('query by test id', () => { + test('can get elements by test id', () => { + const {queryByTestId} = render(`
`) + expect(queryByTestId('firstName')).toBeTruthy() + expect(queryByTestId(/first/)).toBeTruthy() + expect(queryByTestId(testid => testid === 'firstName')).toBeTruthy() + // match should be exact, case-sensitive + expect(queryByTestId('firstname')).not.toBeTruthy() + expect(queryByTestId('first')).not.toBeTruthy() + expect(queryByTestId('firstNamePlusMore')).not.toBeTruthy() + expect(queryByTestId('first-name')).not.toBeTruthy() + }) + + test('can override test id attribute', () => { + const {queryByTestId} = render(`
`) + + configure({testIdAttribute: 'data-my-test-id'}) + expect(queryByTestId('theTestId')).toBeTruthy() + + configure({testIdAttribute: 'something-else'}) + expect(queryByTestId('theTestId')).toBeFalsy() + }) + + afterEach(() => { + // Restore the default test id attribute + // even if these tests failed + configure({testIdAttribute: 'data-testid'}) + }) }) test('getAll* matchers return an array', () => { diff --git a/src/config.js b/src/config.js new file mode 100644 index 00000000..5be8381d --- /dev/null +++ b/src/config.js @@ -0,0 +1,24 @@ +// It would be cleaner for this to live inside './queries', but +// other parts of the code assume that all exports from +// './queries' are query functions. +let config = { + testIdAttribute: 'data-testid', +} + +export function configure(newConfig) { + if (typeof newConfig === 'function') { + // Pass the existing config out to the provided function + // and accept a delta in return + newConfig = newConfig(config) + } + + // Merge the incoming config delta + config = { + ...config, + ...newConfig, + } +} + +export function getConfig() { + return config +} diff --git a/src/index.js b/src/index.js index 1be99df6..5b9dbefa 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ export * from './events' export * from './get-queries-for-element' export * from './query-helpers' export * from './pretty-dom' +export {configure} from './config' export { // The original name of bindElementToQueries was weird diff --git a/src/queries.js b/src/queries.js index e75819fe..c0d738a3 100644 --- a/src/queries.js +++ b/src/queries.js @@ -6,6 +6,7 @@ import { queryAllByAttribute, queryByAttribute, } from './query-helpers' +import {getConfig} from './config' // Here are the queries for the library. // The queries here should only be things that are accessible to both users who are using a screen reader @@ -148,10 +149,16 @@ function queryBySelectText(...args) { return firstResultOrNull(queryAllBySelectText, ...args) } +function getTestIdAttribute() { + return getConfig().testIdAttribute +} + const queryByPlaceholderText = queryByAttribute.bind(null, 'placeholder') const queryAllByPlaceholderText = queryAllByAttribute.bind(null, 'placeholder') -const queryByTestId = queryByAttribute.bind(null, 'data-testid') -const queryAllByTestId = queryAllByAttribute.bind(null, 'data-testid') +const queryByTestId = (...args) => + queryByAttribute(getTestIdAttribute(), ...args) +const queryAllByTestId = (...args) => + queryAllByAttribute(getTestIdAttribute(), ...args) const queryByValue = queryByAttribute.bind(null, 'value') const queryAllByValue = queryAllByAttribute.bind(null, 'value') const queryByRole = queryByAttribute.bind(null, 'role') @@ -182,7 +189,7 @@ function getAllByTestId(container, id, ...rest) { const els = queryAllByTestId(container, id, ...rest) if (!els.length) { throw getElementError( - `Unable to find an element by: [data-testid="${id}"]`, + `Unable to find an element by: [${getTestIdAttribute()}="${id}"]`, container, ) } diff --git a/typings/config.d.ts b/typings/config.d.ts new file mode 100644 index 00000000..6bb7c538 --- /dev/null +++ b/typings/config.d.ts @@ -0,0 +1,9 @@ +export interface IConfig { + testIdAttribute: string +} + +export interface IConfigFn { + (existingConfig: IConfig): Partial +} + +export function configure(configDelta: Partial | IConfigFn): void diff --git a/typings/index.d.ts b/typings/index.d.ts index 86064792..e6cf904a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -15,3 +15,4 @@ export * from './get-node-text' export * from './events' export * from './get-queries-for-element' export * from './pretty-dom' +export * from './config'