testing-library / dom-testing-library Goto Github PK
View Code? Open in Web Editor NEWπ Simple and complete DOM testing utilities that encourage good testing practices.
Home Page: https://testing-library.com/dom
License: MIT License
π Simple and complete DOM testing utilities that encourage good testing practices.
Home Page: https://testing-library.com/dom
License: MIT License
There seems to be an ongoing effort in the jest library to add support for async matchers. I wonder how that affects the need to use something like this library's wait
utility.
dom-testing-library
version: 3.4.0react
version: -NA-node
version: 9.11.1npm
(or yarn
) version: -NA-fireEvent.click()
on disabled elements.
I was expecting fireEvent.click()
not to have dispatched click
event on disabled
elements.
fireEvent
is ignoring the disabled
attribute on the element.
The following test case events.js
is failing.
describe('Disabled element', () => {
it(`should not fire any event on disabled element`, () => {
const node = document.createElement('input')
const disabledAttr = document.createAttribute('disabled')
node.setAttributeNode(disabledAttr);
const spy = jest.fn()
node.addEventListener('click', spy)
fireEvent.click(node)
expect(spy).toHaveBeenCalledTimes(0)
})
});
Currently, the fireEvent
is ignoring the disabled
attribute in elements.
A form control that is disabled must prevent any click events that are queued on the user interaction task source from being dispatched on the element.
β The HTML Spec
fireEvent
can check whether the element has disabled
attribute and skip dispatching the event.
However I'm not sure whether to ignore all MouseEvents (like Chrome/Safari) or just ignore click
events.
dom-testing-library
version: 3.5.1react
version: 16.4.1node
version: 10.4.1npm
(or yarn
) version: yarn 1.7.0Usual installation,
"testEnvironment": "node",
as jest configuration, then something like following in tests:
import { getByText } from 'dom-testing-library'
it('Suggests cities to search by', async () => {
const container = createJSDOM(...)
getByText(container, 'foobar')
})
Tried to use dom-testing-library with node environment in Jest + creating own jsdom instance in a helper and passing it to dom-testing-library.
β Test suite failed to run
ReferenceError: window is not defined
at Object.<anonymous> (node_modules/mutationobserver-shim/dist/mutationobserver.min.js:3:1)
at Object.<anonymous> (node_modules/dom-testing-library/dist/wait-for-element.js:8:1)
at Object.<anonymous> (node_modules/dom-testing-library/dist/index.js:32:23)
As above, enable node jest environment and try to use dom-testing-library with some custom container
check if shim is loaded on given container and install it only then? generally something to support no global window object in jest environment
I'd like waitForElement
to always require a callback
, and always resolve to a truthy value (an element).
As of today, this is the type definition for waitForElement
:
export function waitForElement<T>(
callback?: () => T,
options?: {
container?: HTMLElement
timeout?: number
mutationObserverOptions?: MutationObserverInit
},
): Promise<T | undefined>
I question why the promise resolves to T | undefined
.
The reason for the above type definition probably is that callback
is optional. and when a callback function is not passed, the promise indeed resolves to undefined
. But what's the point of calling waitForElement
if we're never going to pass a callback?
Refactor the waitForElement
implementation to require the callback
or fail. And to not resolve to undefined in its absence. Update the type definitions to reflect this, and the promise resolve type to not allow undefined
.
In Javascript this is no big deal. Because you know, if you provided a callback, that by definition the promise will only resolves when this callback returns a truthy value (i.e. an element). But for TypeScript users, it can be cumbersome to have to explicitly handle the non-truths case that will actually never happen:
const table = await waitForElement(() => container.querySelector('table'));
if (!table) {
// this will never happen
}
// ...
Admittedly, there's a more concise way, using table!
from that point on. But again, why the need?
There's practically nothing to do here. Using waitForElement
without a callback should not be a normal use case. So this would impact very few people.
Even the type definitions shown in the README for waitForElement
do not match the promise resolve type in the actual type definitions. So in terms of documentation, this is already covered as it should've been in the code.
dom-testing-library
version: 3.5.1node
version: 8.11.2npm
(or yarn
) version: 5.6.0const { waitForElement } = require("dom-testing-library");
it("shouldn't hang after succeeding", async () => {
let didMakeMutation = false;
const createElement = () => document.createElement("div");
const container = createElement();
const myCallback = () => {
return didMakeMutation;
};
const result = waitForElement(myCallback, { container });
container.appendChild(createElement());
didMakeMutation = true;
await new Promise((resolve) => setTimeout(resolve, 50));
});
I'm trying to use waitForElement
to wait until an element is created in the dom.
I found that my test is passing, but the test runner (mocha) doesn't exit - it gets into infinite setTimeout loop.
Repo: https://github.com/grigasp/dom-testing-library-waitforelement-issue
npm install
npm run test
The test succeeds, but the test runner (mocha) doesn't ever exit, because waitForElement
gets into infinite setTimeout
loop.
I think the problem is that waitForElement
calls observer.disconnect()
from the onMutation()
-> onDone()
callback. MutationObserver
, after calling the onMutation()
callback, immediately recreates the listener, and then gets into the infinite setTimeout
loop.
waitForElement
should somehow call disconnect()
after MutationObserver
recreates the listener.
react-testing-library
version: 4.1.2react
version: 16.1.0node
version: 8.11.3npm
(or yarn
) version: 5.6.0Test
test('render error message if products fail to load', async () => {
axios.get.mockReturnValue(new Promise((resolve, reject) => reject('some error')));
const { queryByText, getByText, debug } = renderComponent();
expect(queryByText('Loadingβ¦')).toBeInTheDOM();
expect(queryByText('Failed to load products')).not.toBeInTheDOM();
await waitForElement(() => getByText('Failed to load products'));
expect(queryByText('Failed to load products')).toBeInTheDOM();
});
React component segment
render() {
const {
loading,
error,
products,
favourites,
sortedBy
} = this.props;
if (loading) return <div data-test-id="loading-products">Loadingβ¦</div>
if (error) return <div data-test-id="products-error">Failed to load product</div>
Testing a component error state
When test is executed the component remains in loading state, even when error flips to true
and loading false
(confirmed by console logging).
However when I wrap the text "Failed to load product" inside of an additional element such as a <span />
the test passes.
It appears to me that waitForElement
is expecting a change in the markup shape rather than just text in this instanceβ¦
Repository
run yarn test:reactTestingLibrary
Watch all the tests pass.
Then remove the <span />
from Products.js L39
The test should fail.
I'm having to change the shape of the returned JSX in order to satisfy tests.
Following the discussion in testing-library/jest-dom#53 and testing-library/jest-dom#59, it is becoming apparent the need to express user actions on a web page using a higher-level abstraction than fireEvent
.
This issue is to bring the discussion to this repo, where the potential solution actually lies.
See this comment from @kentcdodds for details on how the api could look like.
I'd also like to add that we could take a look at similar cypress APIs. I wonder even if having something like what the above linked comment suggests, would be duplicating what cypress already provides. Of course this project is independent from cypress and it could well provide similar functionality.
Hey guys, looks like 2.4.0 is the latest release here but npm still has 2.3.2, any chance for publishing?
Accessing <select>
elements by the selected option's text
In my experience using dom-testing-library, I've had no issues accessing and interacting with DOM elements in the ways that a user would, except for <select>
elements. Unless a label is associated with <select>
elements, they are a headache to access, and even harder to do so in a way the software is used.
I would like to get a conversation started for how we can better support <select>
elements moving forward.
Here's an example of a select element that would be difficult to access and use:
<select>
<option value="">State</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
</select>
The first <option>
is commonly used in <select>
elements, like a placeholder attribute is used for text inputs. What the user sees is the text value of the first option, but it's not possible to access the <select>
element given that text value.
What I have been doing on my projects has been to create a getSelectByText()
function, though I would hate to have specific, one-use functions or options added to dom-testing-library.
Maybe a user should be able to select a <select>
element by using getByText()
using the selector option. I can't think of any really elegant solutions, but would be curious to hear from other users of the project.
For the time being, I've been using this to access <select>
values based on the innerText of the selected option:
function getSelectByText(text) {
const selectElems = document.querySelectorAll('select')
for (let i = 0; i < selectElems.length; i++) {
const selectInstance = selectElems[i]
const selectedText = selectInstance.selectedOptions[0].innerText
if (selectedText === text) return selectInstance
}
throw new Error(`Unable to find a select element with the value: ${text}`)
}
master
branch failed. π¨I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. Iβm sure you can resolve this πͺ.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here is some links that can help you:
If those donβt help, or if this issue is reporting something you think isnβt right, you can always ask the humans behind semantic-release.
semantic-release cannot push the version tag to the branch master
on remote Git repository.
Please refer to the authentication configuration documentation to configure the Git credentials on your CI environment.
Good luck with your project β¨
Your semantic-release bot π¦π
At risk of sounding like I want to bloat the set of custom matchers provided by this lib, I'd like to propose .toHaveClass('dropdown-toggle')
. The class
attribute in html is designed to have multiple classes encoded in it, and it's often nice to be able to reliably check if an element has a certain class while ignoring the rest of what's encoded in the class
attribute value.
// ...
// <button data-testid="delete-button" type="submit" class="btn btn-danger">
// Delete item
// </button>
expect(getByTestId('delete-button')).toHaveClass('btn-danger')
Raising it as an issue first to see if it's considered useful for this lib, and also to discuss potential names:
toHaveClass
toIncludeClass
toContainClass
Or all of the above but ending with *ClassName
instead of just *Class
.
dom-testing-library
version: 3.8.2react
version: N/Anode
version: v10.11.0 (but it doesn't matter)npm
(or yarn
) version: yarn 1.10.1
(but it doesn't matter)See the reproduction section below.
<div>
containing a <select>
with two <option>
s.<select>
using getBySelectText
change
event on the fetched <select>
to select another valid option<select>
from the <div>
again using the newly selected option's label (child element)The <select>
cannot be fetched by its new "select text":
FAIL ./index.test.js
β should respond to changes (4520ms)
β should respond to changes
Unable to find a <select> element with the selected option's text: Option 2
<div>
<select>
<option
value="option-1"
>
Option 1
</option>
<option
value="option-2"
>
Option 2
</option>
</select>
</div>
22 | // but this times out because the text hasn't been updated.
23 | // No changes are reflected in the DOM, which is probably whyβ¦
> 24 | return waitForElement(() => getBySelectText(container, 'Option 2'));
| ^
25 | });
26 |
at getElementError (node_modules/dom-testing-library/dist/query-helpers.js:29:10)
at getAllBySelectText (node_modules/dom-testing-library/dist/queries.js:375:45)
at firstResultOrNull (node_modules/dom-testing-library/dist/query-helpers.js:37:30)
at getBySelectText (node_modules/dom-testing-library/dist/queries.js:385:42)
at getBySelectText (index.test.js:24:33)
at onMutation (node_modules/dom-testing-library/dist/wait-for-element.js:48:22)
at node_modules/dom-testing-library/dist/wait-for-element.js:66:7
at waitForElement (node_modules/dom-testing-library/dist/wait-for-element.js:26:10)
at Object.waitForElement (index.test.js:24:12)
const { getBySelectText, fireEvent, wait, waitForElement } = require('dom-testing-library');
it('should respond to changes', () => {
const select = document.createElement('select');
select.innerHTML = `
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
`;
const container = document.createElement('div');
container.appendChild(select);
const foundSelectBeforeEvent = getBySelectText(container, 'Option 1');
expect(foundSelectBeforeEvent.value).toBe('option-1');
fireEvent.change(foundSelectBeforeEvent, { target: { value: 'option-2' }});
// It has been updated, and this passes
expect(foundSelectBeforeEvent.value).toBe('option-2');
// I would then expect to be able to get the <select> again by the new text,
// but this times out because the text hasn't been updated.
// No changes are reflected in the DOM, which is probably whyβ¦
return waitForElement(() => getBySelectText(container, 'Option 2'));
});
I've tried (among other things):
wait
ing for a tick to let updates happenfocus
and blur
before and after firing the change
to force a rerenderwaitForElement
(it just fails in the same way, but faster)The <select>
element doesn't seem to update its displayed value, so it behaves differently from what a user experiences in a browser.
The <select>
should be selectable using the label of the selected option. I don't know how to fix it, though π
dom-testing-library
version: 2.9.1react
version:node
version:npm
(or yarn
) version:import React, { Component } from 'react';
export class Input extends Component {
constructor(props) {
super();
const {
value
} = props;
this.state = {
value: value || ''
};
}
handleChange = (evt) => {
const {
onChange
} = this.props;
this.setState(prevState => ({
value: evt.target.value
}));
if (onChange) {
onChange( evt.target.value);
}
}
render() {
const {
value
} = this.state;
return (
<input onChange={this.handleChange} value={value} data-testid="input" />
)
}
}
// test
test('will call onChange prop if changes occur', async () => {
const spy = jest.fn();
const {getByText, getByTestId, container} = render(<Input value="test" onChange={spy} />);
expect(getByTestId('input')).toHaveAttribute('value', 'test');
const input = getByTestId('input');
input.value = 'testing';
fireEvent.change(
input
);
expect(getByTestId('input')).toHaveAttribute('value', 'testing');
expect(spy).toHaveBeenCalled();
// expect(container.firstChild).toMatchSnapshot();
});
writing a test for an input.
component was using event.currentTarget.value
, but got an error in the test because currentTarget was not defined.
change the code above to use current target instead.
People should know their abstractions and it doesn't make sense to me that we have this here. Instead people should import 'jest-dom/extend-expect'
.
Doing this will help people know where they should go to learn about that abstraction.
This will be a breaking change.
I'm thinking there could be room for a utility for dispatching events in elements. Similar to react's test utils Simulate utilities except with actual browser events. What should the API look like?
So I ran into an issue when I was working with cypress. I wanted to .click()
a button which had the exact same wording as the headline right above. getByText
always took the headline instead of the button so I had to use .get('button:contains("[...]")')
to be able to get the button.
So I thought to be able to always use getByText
it would be nice to be able to specify the type of node I want to get as part of the options
maybe something like getByText(container, 'text', { node: 'button' })
.
Of course I would volunteer to do it if it was discussed and approved.
I had an element that looks like this:
<label for="activity.username">Username</label>
<input id="activity.username" />
and calling: getByLabelText('Username')
throws because it won't find the with an id of activity.username
. Having a period in the id
is valid HTML.
The reason is that on this line:
the CSS selector #
needs to be escaped for a characters that are not valid CSS labels. In this case it is trying to run container.querySelector('#activity.username')
but that needs to be escaped to container.querySelector('#activity\\.username')
. (double slash to escape in javascript, so the actual string would only have one slash) (see MDN docs).
An alternative would be to use the [id='activity.shift']
selector, as that finds the element without escaping:
So the whole line would be:
return container.querySelector(`[id='${label.getAttribute('for')}']`)
Regarding the deprecation of toBeInTheDOM
we should either find better assertions or use toBeInTheDocument
: testing-library/jest-dom#40
Anyone wanna try that?
For dom-testing-library, most of the examples aren't actually attached to document.body
so toBeInTheDocument
wont work. We'll have to think of something better on a case-by-case basis I think.
I was recently debugging an issue where I passed "container" to fireEvent.click()
instead of a button reference. What if there was a variant of fireEvent
that warns/errors when firing events on elements that aren't normally interactable? button
and a
would be safe, plus any semantic/aria-
elements.
API ideas:
fireEvent.clickSafe(element, options)
fireEvent.accessibleClick(element, options)
fireEvent.click(element, { allowAny: true })
Detect target element tag name and attributes when firing events
This would be a non-breaking change
Hi Kent! Thanks so much for this and RTL, it's made testing really quite easy π
If I have this...
<button>
<span>testing</span>
</button>
...I have found myself having to use .closest()
when trying to find the button, where the span
isn't guaranteed to exist, and this works really well.
getByText('testing').closest('button')
However, I thought this would be what the selector
option was supposed to provide but that didn't seem to work for me. I get:
Unable to find an element with the text: testing. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Therefore I'm not really sure if this is a bug report or feature request. If...
getByText('testing', { selector: 'button'})
... should find the button, then this is a bug report. If it's not, then maybe...
getByText('testing', { closest: 'button'})
...could be useful and some docs to explain this situation? Or maybe this is all overkill, what are your thoughts?
Happy to help, just want to know if I'm barking up the correct tree to begin with.
dom-testing-library
version: 2.2.0react-testing-library
version: 3.0.2react
version: 16.3.1node
version: v8.9.4npm
(or yarn
) version: 6.0.0Upon updating to the latest version of react-testing-library I now receive the following error when running npm test
ReferenceError: Unknown plugin "@babel/proposal-class-properties" specified in "/Users/ec/Payload/payload/node_modules/dom-testing-library/node_modules/wait-for-expect/.babelrc" at 0, attempted to resolve relative to "/Users/ec/Payload/payload/node_modules/dom-testing-library/node_modules/wait-for-expect"
at node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
at Array.map (<anonymous>)
at Function.normalisePlugins (node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
at OptionManager.mergeOptions (node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
at OptionManager.init (node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12)
at File.initOptions (node_modules/babel-core/lib/transformation/file/index.js:212:65)
at new File (node_modules/babel-core/lib/transformation/file/index.js:135:24)
at Pipeline.transform (node_modules/babel-core/lib/transformation/pipeline.js:46:16)
I'm not entirely sure if this a config issue on my end or with the wait-for-expect
package within dom-testing-library, but I decided to ask here first.
dom-testing-library
version: 2.3.2cypress-testing-library
version: 2.0.0cypress
version: 2.1.0react
version: 16.3.2node
version: 8.4.0npm
version: 5.3.0cy.getByLabelText("something not on the page");
I'm selecting an element using getByLabelText
, but my code isn't setting the htmlFor
correctly, so it couldn't find it.
cypress-testing-library
passes the window.document
as container
to the query.getByLabelText
can't find the element, it wants to log a friendly error message using prettyDOM
.prettyDOM
uses outerHTML.length
to determine whether to truncate. However, document.outerHTML
is undefined resulting in the error.TypeError: Cannot read property 'length' of undefined
at prettyDOM (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:898:59)
at debugDOM (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:920:35)
at getAllByLabelText (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:1181:216)
at firstResultOrNull (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:932:30)
at getByLabelText (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:1194:28)
at container (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:82:28)
at onMutation (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:1309:22)
at http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:1327:7
at new Promise (<anonymous>)
at waitForElement (http://localhost:3001/__cypress/tests?p=cypress\support\index.js-147:1289:10)
From previous event:
at Context.thenFn (http://localhost:3001/__cypress/runner/cypress_runner.js:56945:26)
at Context.then (http://localhost:3001/__cypress/runner/cypress_runner.js:57207:21)
at Context.<anonymous> (http://localhost:3001/__cypress/runner/cypress_runner.js:63338:21)
at http://localhost:3001/__cypress/runner/cypress_runner.js:63053:33
From previous event:
at runCommand (http://localhost:3001/__cypress/runner/cypress_runner.js:63043:14)
at next (http://localhost:3001/__cypress/runner/cypress_runner.js:63125:14)
at http://localhost:3001/__cypress/runner/cypress_runner.js:63137:18
I've spent a little bit of time to try and test this, but with JSDOM, this doesn't seem to be easy. prettyDOM(document)
crashes before it hits the outerHTML.length
part due to pretty-format
package (RangeError: Invalid string length
).
RangeError: Invalid string length
at printListItems (node_modules/pretty-format/build/collections.js:105:33)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printListItems (node_modules/pretty-format/build/collections.js:105:35)
at printComplexValue (node_modules/pretty-format/build/index.js:176:141)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
at printComplexValue (node_modules/pretty-format/build/index.js:187:169)
at printer (node_modules/pretty-format/build/index.js:236:10)
at printObjectProperties (node_modules/pretty-format/build/collections.js:140:21)
I believe this can be reproduced by just using cypress-testing-library
and selecting something that isn't on the page? I'll do some more testing in the weekend.
prettyDOM
function shouldn't assume outerHTML
is defined.
Maybe a more accurate description would be: I don't know how to write a test case for this within dom-testing-library
to test a pull request.
I'm not sure why outerHTML
is being used, since it's the debugContent
that is being truncated. So my suggestion would be to check debugContent.length
instead. Otherwise add an && htmlContent.outerHTML
before the length
check.
There is a <title>
element that can be rendered within an SVG element, see MDN, and would be nice to be considered when doing a getByTitle
assertion. Currently it does work with getByText
, but that might be incorrect considering it behaves more like a title attribute than text content.
If accepted, I will work up a PR adding support for it.
Would need to find the <title>
element and ensure it's parent is a <svg>
element and allow that to be included in the assertion.
We currently use getByText
as a selector.
dom-testing-library
version: 2.4.0react
version: 16.3.2import "dom-testing-library/extend-expect"
import React from "react"
import { render } from "react-testing-library"
const App = () => "Hello World!"
test("App renders without crashing", async () => {
const { queryByText } = render(<App />)
expect(queryByText("Hello World!")).toBeInTheDOM()
})
I ran the test.
It returned an error:
Cannot read property 'match' of null
TypeError: Cannot read property 'match' of null
at module.exports.str (https://j4rkx7jmlv.codesandbox.io/node_modules/strip-indent/index.js:3:20)
https://codesandbox.io/s/j4rkx7jmlv
I expect queryByText()
to work with components that return a string.
Improve queryByText()
so it supports components that return a string.
The Matcher import in typings/get-queries-for-element.d.ts
is incorrect.
dom-testing-library
version: 2.3.1react
version: 16.3.1node
version: 8.11.1npm
(or yarn
) version: 6.0.1The current (incorrect) code is:
import {Matcher} from 'matches'
The correct import is as follows:
import {Matcher} from './matches'
Attempt to call ReactTesting.renderIntoDocument(...)
in a foo.test.tsx
file fails.
Unfortunately the code is in a private GitHub repo that can't be shared here.
TypeScript compilation of the app fails and returns the following error message:
node_modules/dom-testing-library/typings/get-queries-for-element.d.ts
(1,23): Cannot find module 'matches'.
dom-testing-library
version: 2.1.1It looks like all the matchers are designed to return the first element matching a query. Would it make sense to also export matchers that return all of the matching nodes as well (e.g., document.querySelectorAll
vs document.querySelector
)? Some use cases are testing the length or sort order of lists, handling pagination, etc.
Would you be interested in adding a helper for checking style rules? I taught a workshop on testing using the library and ended up with students stuck trying to check (for example) a button's background-colour had correctly changed.
Getting a DOM element's style rules is kind of gross, it so might be nice to expose a util like getStyleValue
.
It could be as simple as:
function getStyleValue(element, property) {
return window.getComputedStyle(element).getPropertyValue(property);
}
If this is beyond the scope/doesn't fit the philosophy of the library then feel free to close β€οΈ
dom-testing-library
version: 1.1.0node
version: N/Anpm
(or yarn
) version: N/ARelevant code or config
function checkHtmlElement(htmlElement) {
if (!(htmlElement instanceof HTMLElement)) {
throw new Error(
`The given subject is a ${getDisplayName(
htmlElement,
)}, not an HTMLElement`,
)
}
}
// ...
const extensions = {
toBeInTheDOM(received) {
if (received) {
checkHtmlElement(received)
}
return {
pass: !!received,
message: () => {
const to = this.isNot ? 'not to' : 'to'
return getMessage(
matcherHint(
`${this.isNot ? '.not' : ''}.toBeInTheDOM`,
'element',
'',
),
'Expected',
`element ${to} be present`,
'Received',
received,
)
},
}
},
What you did:
.toBeInTheDOM()
in a test with an element disconnected from the DOM, like here; the test passed, surprisingly.jest-extensions.js
What happened:
Discovered that toBeInTheDOM
does not check that the element is attached to a full DOM tree.
Reproduction repository:
Problem description:
toBeInTheDOM
, according to its name, has to ensure that the given element is in the DOM, i.e. attached to the full DOM tree, not hanging as a standalone element or in a standalone subtree.
Suggested solution:
Find out if the element is actually attached to a DOM document via element.ownerDocument.contains(element)
, and report an assertion failure if it's not.
dom-testing-library
version: 2.6.3react
version: n/anode
version: 8.4.0npm
(or yarn
) version: 6.1.0Using any dom-testing-library functionality within a running ember project (ember s) and viewing tests at "/tests";
Attempted to use dom-testing-library along side standard Ember tooling for testing. When assertions failed (elements not found, etc) the debug output is produced.
DTL outputs entire container in the browser window. It appears to be formatted for CLI and is unreadable.
Sorry, I wasn't sure how to pull dom-testing-library into ember-twiddle. Unpkg didn't seem to work.
This makes it almost impossible to debug. This is especially true if using document
as your container.
Issue possibly around here with detecting environment (thanks @kentcdodds):
https://github.com/kentcdodds/dom-testing-library/blob/ba44c14f93b4def2b1f8ce0eea8cbaf5c777c735/src/queries.js#L15
I found that in an Ember test
const inNode = typeof module !== 'undefined' && module.exports
will be undefined because, while typeof module
is 'object', there's still no module.exports
.
I'm not sure what's making DTL think it's in Node or if that is even the true issue.
A new querySelector to select elements based on role. I have a dialog that while I can query inside the dialog just fine I cannot first a click to close the dialog because all the current queries target elements inside the dialog so the out of bounds click handlers do not fire.
const queryAllByRole = queryAllByAttribute.bind(null, 'role')
const queryByRole = queryByAttribute.bind(null, 'role')
const queryAllByRole = queryAllByAttribute.bind(null, 'role')
function getAllByRole(container, id, ...rest) {
const els = queryAllByRole(container, id, ...rest)
if (!els.length) {
throw getElementError(
`Unable to find an element by role=${id}`,
container,
)
}
return els
}
function getByRole(...args) {
return firstResultOrNull(getAllByRole, ...args)
}
I'm using Material UI where there is a lot of abstraction in DOM that I can't put data-testid on the outer dialog. So targeting it by testid won't work for triggering the onClose event.
The only thing I've considered is writing the
const dialogConstainer = container.querySelector('[role="dialog"]')
And then writing assertions from there.
This is a non breaking change as it is only an addition to the existing query set.
getByRole
getByRole(
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
collapseWhitespace?: boolean = false,
trim?: boolean = true,
}): HTMLElement`
A shortcut to container.querySelector(`[role="${yourRole}"]`)
(and it
also accepts a TextMatch
).
// <div role="dialog">...</div>
const dialogContainer = getByTestrole(container, 'dialog')
Would you be interested in a function like this? The only annoying thing I've found with using jsdom is that it's hard to debug if you don't know what's going on or are confused. In the browser you can just look at it, but you can't with jsdom. This gives me most of what I want:
function prettyDOM(node, indent = 0) {
let str = '';
if (node) {
str += ' '.repeat(indent) + (node.tagName || node.textContent) + '\n';
for (let child of node.childNodes) {
str += debugDOM(child, indent + 2);
}
}
return str;
}
You can now do console.log(prettyDOM(node))
and get an output like this in the terminal:
dom-testing-library
version: 2.7.0react
version: 16.4.1redux-form
version: 7.4.2node
version: 8.11.3npm
(or yarn
) version: 6.1.0import React from "react";
import { renderWithRedux, generate } from "test-utils";
import { cleanup, fireEvent } from "react-testing-library";
import LoginFormContainer from "../LoginFormContainer";
afterEach(cleanup);
describe("LoginFormContainer", () => {
test("Submits Login with email and password", () => {
//Arrange--------------
const fakeUser = generate.loginForm();
let submitLogin = jest.fn();
const props = { submitLogin };
const { getByText, getByPlaceholderText } = renderWithRedux(
<LoginFormContainer {...props} />
);
const emailNode = getByPlaceholderText("E-mail address");
const passwordNode = getByPlaceholderText("Password");
//Act--------------
emailNode.value = fakeUser.email;
^^^^^ [ts] Property 'value' does not exist on type 'HTMLElement'.
passwordNode.value = fakeUser.password;
^^^^^ [ts] Property 'value' does not exist on type 'HTMLElement'.
fireEvent.change(emailNode);
fireEvent.change(passwordNode);
fireEvent.click(getByText("Login"));
//Assert--------------
expect(submitLogin).toHaveBeenCalledTimes(1);
expect(submitLogin).toHaveBeenCalledWith(fakeUser);
});
});
renderWithRedux
is the same as the examples from react-testing-library
import React from "react";
import { createStore } from "redux";
import { Provider } from "react-redux";
import { render } from "react-testing-library";
import reducer from "../services/reducer";
export default function renderWithRedux(
ui: React.ReactNode,
{ initialState, store = createStore(reducer, initialState) } = {}
) {
return {
...render(<Provider store={store}>{ui}</Provider>),
// adding `store` to the returned utilities to allow us
// to reference it in our tests (just try to avoid using
// this to test implementation details).
store
};
}
I am using redux-form
for my forms.
I want to:
submitLogin
is firedreact-testing-library: fireEvent If you want to trigger the
onChange
handler of a controlled component with a differentevent.target.value
, sending value througheventProperties
won't work like it does withSimulate
.
You need to change the element'svalue
property, then usefireEvent
to fire achange
DOM event.
I get TypeScript errors from the returned Nodes from getByPlaceholderText
, because it returns a type HTMLElement
.
const emailNode = getByPlaceholderText("E-mail address");
const passwordNode = getByPlaceholderText("Password");
emailNode.value = fakeUser.email;
^^^^^ [ts] Property 'value' does not exist on type 'HTMLElement'.
passwordNode.value = fakeUser.password;
^^^^^ [ts] Property 'value' does not exist on type 'HTMLElement'.
I'm not very well versed with TypeScript, but I'm wondering if updating the type definition for queries.d.ts
, so that type GetByAttribute
returns an intersection of HTMLElement & HTMLInputElement
makes sense?
// queries.d.ts
export type GetByAttribute = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => HTMLElement & HTMLInputElement
^^^^^^^^^^^^^^^^^ Add intersection type?
export const getByPlaceholderText: GetByAttribute
In the meantime, I'm just typecasting:
const emailNode = getByPlaceholderText("E-mail address") as HTMLInputElement;
const passwordNode = getByPlaceholderText("Password") as HTMLInputElement;
Well, I think there is no much to say. At this time react-testing-library has a file for typings.
Is there any plans to add some for this project?
Best regards.
react-testing-library
version: 5.2.0dom-testing-library
version: 3.8.3react
version:16.5.2node
version: v8.12.0npm
version: 6.4.1Running via create-react-app v1 through react-testing-library.
Ran tests in CI. Once I saw they were broken, cleared my local node_modules and started having the same issues
1/5th of our tests started breaking in our CI about an hour ago when 3.8.3 came out with the following errors:
InputEvent is not supported
at Function.fireEvent.(anonymous function) [as change] (node_modules/dom-testing-library/dist/events.js:344:13)
at Object.it ([redacted]/index.test.js:42:36)
at <anonymous>
The index.test.js line the error points to is
const firstNameField = await waitForElement(() =>
getByLabelText('First Name')
);
fireEvent.change(firstNameField, { // line 42
target: { value: 'testFirstName' },
});
Run previously working tests.
This was just a bugfix release so shouldn't cause this many issues, right?
Still digging into what changed and is causing the issue.
I am trying to test a component where based on the selection of a SELECT
it should shows a specific component. The component has a input field. In my unit test I want to check if the expected input field is shown and not the other one. As this is what the user would expect. Now I am using getByTestId
to get item:
<input data-testid="entry-weight" type="text" id="itemWeight" name="itemWeight" value={itemWeight} onChange={e => this.updateState(e.target.name, e.target.value)} />
I am using the following code for that: getByTestId('calculator-weight')
this works fine in the unit test I am doing the following to verify it's not available:
const onFormSubmit = jest.fn()
const BasicForm = (
<Formik initialValues={TEST_DATA} onSubmit={onFormSubmit}><DataEntryWizardRowItem item={TEST_DATA} index={0} /></Formik>
)
const { getByLabelText, getByTestId, debug, ...other } = render(BasicForm)
const measuremeentTypeField = getByLabelText('Measurement')
fireEvent.change(measuremeentTypeField, {target: {value: 'quantity'}})
const calculatorField = getByTestId('calculator-weight') // use testId because both components end up having the same field label
try {
const weightField = getByTestId('entry-weight')
expect(weightField).toBeUndefined() // fail the test if we get here
} catch(error) {
expect(error).toBeDefined()
}
I am not sure if the try-catch is the right way to do? Maybe it would be useful to check if an item doesn't exist? Currently, when it doesn't exist it's throwing an error which breaks the unit test.
The aria-labelledby
attribute doesn't require the labeling element to be a <label>
tag, but dom-testing-lib only searches for label
s in queryByLabelText
.
I'd like to add a way to query for anything by ARIA label, which would allow doing things like selecting a card item by its heading text and then doing scoped queries for related elements like an "edit" button.
This would largely replace the need for workarounds like getByText
-> .$closest()
parent testid.
To simplify the wait()
API I think we could merge wait()
and waitForElement()
.
The implementation would be something like:
const isElement = obj => obj && obj.nodeType === Node.ELEMENT_NODE
function wait(callback = () => {}) {
const result = callback()
return isElement(result) ? waitForElement(callback) : waitForExpect(callback)
}
dom-testing-library
version: 3.8.1Typings for query by role are named with a T
inside (queryByTRole
, queryAllByTRole
, getByTRole
, getAllByTRole
) which makes TS compiler fail.
Rename exports in typings/queries.d.ts
.
dom-testing-library
version: 1.4.0node
version: N/Anpm
(or yarn
) version: N/Acy.getByText('Create')
Used getByText
with a text that is not unique across the page (which is often the case).
Wrong element was found.
N/A
When attempting to find an element by text, label, or other visual cues,
This issue is here to start a larger discussion, I've got no complete coded solution yet. It's closely related to Cypress but visual element finding is DOM-specific, not specific to Cypress, as proven by my proof-of-concept powered by the same Guiding Principles that uses Puppeteer.
In the mentioned proof-of-concept I experimented with visual element finding and came to an API that allows finding more than one element around some viewport point and filter them after that (potentially, with a text matcher, but the filter is generic). https://github.com/sompylasar/puppeye/blob/d01aa117d3bacf8a458b03dffebef081380b54d9/test/smoke.test.ts#L145-L183
const checkboxLabelCenterLeftPagePoint = {
x: checkboxLabelElement.pageRect.left,
y: checkboxLabelElement.centerPagePoint.y,
} as puppeye.TPagePoint;
const [checkboxElement] = await puppeye.findElements(
pageModel,
puppeye.makeElementsAroundFinder({
viewportPoint: puppeye.convertPagePointToViewportPoint(
pageModel.getPageState(),
checkboxLabelCenterLeftPagePoint,
),
filter: (el) =>
el.viewportRect.area > 10 * 10 &&
el.viewportRect.area < 40 * 40 &&
el.viewportRect.ratio > 0.9,
maxDistancePx: 100,
}),
);
await puppeye.moveMouseIntoElementRect(pageModel, checkboxElement);
await puppeye.clickElement(pageModel, checkboxElement);
const [continueButton] = await puppeye.findElements(
pageModel,
puppeye.makeElementsBelowFinder({
viewportPoint: puppeye.convertPagePointToViewportPoint(
pageModel.getPageState(),
checkboxLabelElement.centerPagePoint,
),
filter: (el) =>
el.isInteractive && el.textNormalized === puppeye.normalizeText('Continue'),
}),
);
expect(continueButton).toMatchObject({
text: 'Continue',
});
await puppeye.clickElement(pageModel, continueButton);
Cypress has some heuristics for ordering for the similar functionality they have (contains
) described here: https://docs.cypress.io/api/commands/contains.html#Preferences β but the only scoping they have is based on DOM selectors. Cypress has something similar, coordinate-based, for the click
action (click different parts of rectangles) but doesn't have that for scoping and ordering of elements.
Many tooltip libraries remove the title
attribute and add data-original-title
with the same value. I'm using tippy.js. Bootstrap does this too.
Is this something that getByTitle
should support out of the box?
// <span data-original-title="Tooltip message" id="2" />
const elementWithTooltip = getByTitle(container, 'Tooltip message')
I'd like to introduce the possibility of using React Testing Library at my place of work, however the standard format this is baked into all of our codebases is data-test-id
. This is used by teams of testers and it would be almost impossible to get them to change this.
As it stands we cannot use the getByTestId
function in our projects.
This is how we'd currently have to use the library:
const product = container.querySelector('[data-test-id="product"]');
I'm not sure how this would be possible to implement in a sensible manner looking at the codebase given that it directly uses the string data-testid
that gets passed into querySelector()
.
Perhaps we could look at some global config as I am sure there are other teams who are formatting data-testids differently.
'byText' queires, getByText()
, for example, works greate in writing tests that closely resembles users actions, but it has a drawback, expecially on integration tests, which usually requires many components to be rendered.
Sometimes a user might be interested in a particular text, but only on a prticular section of the page. Take the bellow screenshot for example.
So the user sends a 'lol' and 'today' message and wants to make sure the message is sent. She'll have to look for the message "byText" 'lol' to see when/if the message has been markded as sent. It is only the "byText" queries that can help us do this job, but here's the problem: There are many 'lol' and 'today' texts on the page, but our user is only looking at or interested in the 'lol' text INSIDE-THE-MESSAGES-SECTION.
getByText
in it's current form will not help us in such case, because it will grab the text 'lol' anywhere it founds it on the page, which in turn require our tests to rely on DOM structure to get the text where're interested in.
Provide an option to tell the 'byText' queries where-in-the-document they should get the text. Something like this:
getByText('some text', {in: node})
I think this could be easily implemented by some minor editting the queryAllByText fucntion.
We could simply check to see if the in
option is present, then use it as the baseElement of the querySelectorAll(). We only need to touch too lines in the function. Something like this:
function queryAllByText(
container,
text,
{selector = '*', exact = true, collapseWhitespace = true, trim = true, ...options} = {},
) {
const matcher = exact ? matches : fuzzyMatches
const matchOpts = {collapseWhitespace, trim}
// determin what the baseElement should be
const baseElement = options.in ? options.in : container;
return Array.from(baseElement.querySelectorAll(selector)).filter(node =>
matcher(getNodeText(node), node, text, matchOpts),
)
}
N/A
To use this feature, user will need to first grab the in
node, then use it as the in
option. Like this:
const messageArea = getByTestId('message-area');
getByText('some text', {in: messageArea})
dom-testing-library
version: 1.1.0node
version: N/Anpm
(or yarn
) version: N/ARelevant code or config
import { getByTestId } from "dom-testing-library";
document.getElementById("test").innerHTML = `
<div data-testid="supertest-123"></div>
`;
document.getElementById("output").innerHTML = `
${getByTestId(document, "tEsT")
.outerHTML.replace(/</g, "<")
.replace(/>/g, ">")}
`;
What you did:
data-testid="supertest-123"
to an empty DOM.getByTestId
by string "tEsT"
.What happened:
The element with data-testid="supertest-123"
was found.
Reproduction repository:
https://codesandbox.io/s/pwrpl1yy9x?module=%2Fsrc%2Findex.js
Problem description:
The current behavior is unexpected, the identifiers aren't supposed to be matched partially and case-insensitively.
Suggested solution:
Use case-sensitive full-string match in queryByTestId
, getByTestId
.
Would be nice to have chai assertions within this library
master
branch failed. π¨I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.
You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. Iβm sure you can resolve this πͺ.
Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.
Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master
branch. You can also manually restart the failed CI job that runs semantic-release.
If you are not sure how to resolve this, here is some links that can help you:
If those donβt help, or if this issue is reporting something you think isnβt right, you can always ask the humans behind semantic-release.
semantic-release cannot push the version tag to the branch master
on remote Git repository.
Please refer to the authentication configuration documentation to configure the Git credentials on your CI environment.
Good luck with your project β¨
Your semantic-release bot π¦π
dom-testing-library
version: currentreact
version: 16.4.1node
version: 9.11.1npm
(or yarn
) version: 1.6.0const Button = () => (
<button
alt="Menu Toggle"
>
<span aria-hidden="true" />
<span aria-hidden="true" />
<span aria-hidden="true" />
</button>
)
test('hamburger toggles menu', () => {
const { getByAltText } = render(
<Button />
);
expect(getByAltText('Menu Toggle')).toBeDefined();
});
getByAltText('Menu Toggle')
doesn't find the element
getByAltText('Menu')
finds the element
The automatic debug output / prettyDOM is hard to pick through with JSS, since a rather large style block is added to the DOM with JSS classes. There should be a way to avoid JSS output (the material-ui library in our case dumps out 10,000 lines of CSS, which is thought-provoking in itself, but here we are).
I was thinking of an option to prune style blocks from the output; @kentcdodds suggested that defaulting the debug output to the container instead of the documentElement might be a better idea.
Same as testing-library/react-testing-library#76 which would also expose this API
Make exact match the default instead of partial case-insensitive match. Regex would be recommended for those cases instead.
/mystring/
/mystring/i
/^mystring/
/mystring$/
/^mystring$/i
One thing that would more involved to replicate with inline regexes is that the current implementation trims the string and replaces symbols with spaces:
- const normalizedText = textToMatch.trim().replace(/\s+/g, ' ')
See testing-library/react-testing-library#74 (comment)
I could see implementing this by exposing the final options argument to the getAllByAttribute
helper to the public methods that use it so that exact: true
could toggled.
https://github.com/kentcdodds/dom-testing-library/blob/master/src/queries.js#L73
Leave the matcher alone
Show common regex patterns like the ones above^ in the docs.
Yes
There's a block of code used by vue-testing-library, react-testing-library and in the tests here:
const containerHelpers = Object.entries(queries).reduce(
(helpers, [key, fn]) => {
helpers[key] = fn.bind(null, element)
return helpers
},
{},
)
I propose adding a bindQueriesToElement
utility function to encapsulate this code.
Ability to find elements from a rendered DOM by some sort of CSS selector, a la jQuery.
For example, I have this DOM:
<button disabled="" type="button" class="ant-btn ant-btn-primary ant-btn-circle ant-btn-icon-only"><i class="anticon anticon-plus"><svg viewBox="64 64 896 896" class="" data-icon="plus" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path snip></path></svg></i></button>
It's a <Button/>
component from Ant Design but not important.
Now of the existing methods allow me to get this DOM element. I could modify the main code and inject a data-testid="add-button"
and then use getByTestId
but then it feels like the code has to work for the tests rather than the other way around.
const { getByCSSSelector } = render(<Button disabled={false} icon="plus"/>)
fireEvent(getByCSSSelector('button')).click()
// or...
console.log(getByCSSSelector('button i.anticon'))
// or...
console.log(getByCSSSelector('button i svg'))
Another challenge I found was I was trying to see if a DOM element existed based on it's classList
. The DOM element, when rendered would (depending on business logic) set (or not set) className="spin-animation"
.
Then I'd be able to do:
const { getByCSSSelector } = render(<MyComponent key={value}/>)
expect(getByCSSSelector('div.spin-animation')).toBeInTheDocument()
No migration needed. A better name that CSSSelector
maybe. Perhaps just querySelector
or getBySelector
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.