-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Render element tree in query error messages #1378
feat: Render element tree in query error messages #1378
Conversation
Love it! I think it would be helpful to also show "testID" prop |
Thanks for taking a look, @thymikee! I'm happy to include |
@stevehanson sounds great. Whichever helps debuggability and doesn't clutter the output is nice. Ultimately we can expose mapping functionality to the users, so they can configure it per use case or test run. Nevertheless, good defaults are going to be crucial for this functionality to be net-helpful :) |
@stevehanson I've support the general idea around this PR. It seems helpful to report element tree to the user on query failure. That being said, the unfiltered element tree can be quite long, especially when all props are included, and some are especially noisy (e.g. style, default touch event handlers, etc). The approach you have taken so far, which allowing only accessibility related ones seems generally good direction. However, I think we should include a little bit more.
The reason for including these, is that a large tree without additional context might be hard to navigate, it will just be a deeply nested tree of |
Other things to watch out for:
|
@mdjastrzebski thank you for the review!
I totally agree with this suggestion, and am mostly aligned with the props you suggested. I wired up the One open question on my mind is if every query should render the same props. It does seem like we would need those props for other queries, like maybe Here are some sample DOM trees from a small real app that uses React Navigation with multiple stacks, including bottom tabs. Just including this as a relevant data point to this discussion: Debug output: all props except style (571 lines)<RNCSafeAreaView
edges={
Array [
"bottom",
"left",
"right",
"top",
]
}
>
<RNCSafeAreaProvider
onInsetsChange={[Function anonymous]}
>
<RNSScreenStack>
<RNSScreen
collapsable={false}
forwardedRef={[Function handleRef]}
gestureResponseDistance={
Object {
"bottom": -1,
"end": -1,
"start": -1,
"top": -1,
}
}
nativeBackButtonDismissalEnabled={false}
onAppear={[Function onAppear]}
onDisappear={[Function onDisappear]}
onDismissed={[Function onDismissed]}
onHeaderBackButtonClicked={[Function onHeaderBackButtonClicked]}
onNativeDismissCancelled={[Function onNativeDismissCancelled]}
onTransitionProgress={[Function anonymous]}
onWillDisappear={[Function onWillDisappear]}
replaceAnimation="push"
stackPresentation="push"
swipeDirection="horizontal"
>
<RNSScreenStackHeaderConfig
backButtonInCustomView={false}
backgroundColor="rgb(255, 255, 255)"
color="#111"
direction="ltr"
disableBackButtonMenu={false}
hidden={true}
hideBackButton={false}
hideShadow={true}
largeTitleHideShadow={false}
title="Tabs"
titleColor="#282828"
topInsetEnabled={false}
translucent={false}
/>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
>
<View>
<View>
<RNSScreenNavigationContainer>
<RNSScreen
activityState={2}
collapsable={false}
forwardedRef={[Function handleRef]}
gestureResponseDistance={
Object {
"bottom": -1,
"end": -1,
"start": -1,
"top": -1,
}
}
>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
>
<View>
<View>
<RNSScreenStack>
<RNSScreen
collapsable={false}
forwardedRef={[Function handleRef]}
gestureResponseDistance={
Object {
"bottom": -1,
"end": -1,
"start": -1,
"top": -1,
}
}
nativeBackButtonDismissalEnabled={false}
onAppear={[Function onAppear]}
onDisappear={[Function onDisappear]}
onDismissed={[Function onDismissed]}
onHeaderBackButtonClicked={[Function onHeaderBackButtonClicked]}
onNativeDismissCancelled={[Function onNativeDismissCancelled]}
onTransitionProgress={[Function anonymous]}
onWillDisappear={[Function onWillDisappear]}
replaceAnimation="push"
stackPresentation="push"
swipeDirection="horizontal"
>
<RNSScreenStackHeaderConfig
backButtonInCustomView={false}
backgroundColor="rgb(255, 255, 255)"
color="#111"
direction="ltr"
disableBackButtonMenu={false}
hidden={true}
hideBackButton={false}
largeTitleHideShadow={false}
title="Home"
titleColor="#282828"
topInsetEnabled={false}
translucent={false}
/>
<View
accessibilityElementsHidden={true}
importantForAccessibility="no-hide-descendants"
>
<View>
<RCTScrollView
bounces={true}
contentContainerStyle={
Array [
Object {
"flexGrow": 1,
"paddingVertical": 20,
},
Object {
"paddingHorizontal": 20,
},
undefined,
]
}
contentInsetAdjustmentBehavior="automatic"
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
testID="HomeScreen"
>
<View>
<Text
accessibilityRole="header"
>
Good Morning,
<Text>
Dale
</Text>
</Text>
<Text>
Welcome to the Example app. Content will be coming soon!
</Text>
<View
accessibilityRole="link"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function onBlur]}
onClick={[Function onClick]}
onFocus={[Function onFocus]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
>
<Text>
Go to article!
</Text>
</View>
<View>
<Text>
Some text
</Text>
</View>
</View>
</RCTScrollView>
</View>
</View>
</RNSScreen>
<RNSScreen
collapsable={false}
forwardedRef={[Function handleRef]}
gestureResponseDistance={
Object {
"bottom": -1,
"end": -1,
"start": -1,
"top": -1,
}
}
nativeBackButtonDismissalEnabled={false}
onAppear={[Function onAppear]}
onDisappear={[Function onDisappear]}
onDismissed={[Function onDismissed]}
onHeaderBackButtonClicked={[Function onHeaderBackButtonClicked]}
onNativeDismissCancelled={[Function onNativeDismissCancelled]}
onTransitionProgress={[Function anonymous]}
onWillDisappear={[Function onWillDisappear]}
replaceAnimation="push"
stackPresentation="push"
swipeDirection="horizontal"
>
<RNSScreenStackHeaderConfig
backButtonInCustomView={false}
backgroundColor="rgb(255, 255, 255)"
color="#111"
direction="ltr"
disableBackButtonMenu={false}
hidden={false}
hideBackButton={false}
largeTitleHideShadow={false}
title=""
titleColor="#282828"
topInsetEnabled={false}
translucent={false}
/>
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
>
<View>
<RCTScrollView
bounces={true}
contentContainerStyle={
Array [
Object {
"flexGrow": 1,
"paddingVertical": 20,
},
Object {
"paddingHorizontal": 20,
},
undefined,
]
}
contentInsetAdjustmentBehavior="automatic"
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
testID="ArticleScreen"
>
<View>
<Text
accessibilityRole="header"
>
Article Heading
</Text>
<Text>
Some text
</Text>
</View>
</RCTScrollView>
</View>
</View>
</RNSScreen>
</RNSScreenStack>
</View>
</View>
</View>
</RNSScreen>
</RNSScreenNavigationContainer>
<View
collapsable={false}
onLayout={[Function handleLayout]}
pointerEvents="auto"
>
<View
pointerEvents="none"
/>
<View
accessibilityRole="tablist"
>
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": true,
}
}
accessibilityStates={
Array [
"selected",
]
}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function onBlur]}
onClick={[Function onClick]}
onFocus={[Function onFocus]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
>
<View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
</View>
<Text>
Home
</Text>
</View>
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": false,
}
}
accessibilityStates={Array []}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function onBlur]}
onClick={[Function onClick]}
onFocus={[Function onFocus]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
>
<View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
</View>
<Text>
Search
</Text>
</View>
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": false,
}
}
accessibilityStates={Array []}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function onBlur]}
onClick={[Function onClick]}
onFocus={[Function onFocus]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
>
<View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
</View>
<Text>
Discover
</Text>
</View>
<View
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": undefined,
"expanded": undefined,
"selected": false,
}
}
accessibilityStates={Array []}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onBlur={[Function onBlur]}
onClick={[Function onClick]}
onFocus={[Function onFocus]}
onResponderGrant={[Function onResponderGrant]}
onResponderMove={[Function onResponderMove]}
onResponderRelease={[Function onResponderRelease]}
onResponderTerminate={[Function onResponderTerminate]}
onResponderTerminationRequest={[Function onResponderTerminationRequest]}
onStartShouldSetResponder={[Function onStartShouldSetResponder]}
>
<View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
<View>
<View
accessible={false}
>
<Image
accessibilityIgnoresInvertColors={false}
source={
Object {
"process": [Function process],
}
}
/>
</View>
</View>
</View>
<Text>
Profile
</Text>
</View>
</View>
</View>
</View>
</View>
</View>
</RNSScreen>
</RNSScreenStack>
</RNCSafeAreaProvider>
</RNCSafeAreaView> Debug output: visibility props, testID, some accessibility-related (173 lines)This output is a lot easier to parse. It includes most of the props listed in the PR review, except <RNCSafeAreaView>
<RNCSafeAreaProvider>
<RNSScreenStack>
<RNSScreen>
<RNSScreenStackHeaderConfig />
<View
accessibilityElementsHidden={false}
importantForAccessibility="auto"
>
<View>
<View>
<RNSScreenNavigationContainer>
<RNSScreen>
<View>
<View>
<View>
<RNSScreenStack>
<RNSScreen>
<RNSScreenStackHeaderConfig />
<View
accessibilityElementsHidden={true}
importantForAccessibility="no-hide-descendants"
>
<View>
<RCTScrollView
testID="HomeScreen"
>
<View>
<Text
accessibilityRole="header"
>
Good Morning,
<Text>
Dale
</Text>
</Text>
<Text>
Welcome to the Example app. Content will be coming soon!
</Text>
<View
accessibilityRole="link"
>
<Text>
Go to article!
</Text>
</View>
<View>
<Text>
Some text
</Text>
</View>
</View>
</RCTScrollView>
</View>
</View>
</RNSScreen>
<RNSScreen>
<RNSScreenStackHeaderConfig />
<View>
<View>
<RCTScrollView
testID="ArticleScreen"
>
<View>
<Text
accessibilityRole="header"
>
Article Heading
</Text>
<Text>
Some text
</Text>
</View>
</RCTScrollView>
</View>
</View>
</RNSScreen>
</RNSScreenStack>
</View>
</View>
</View>
</RNSScreen>
</RNSScreenNavigationContainer>
<View>
<View />
<View
accessibilityRole="tablist"
>
<View
accessibilityRole="button"
>
<View>
<View>
<View>
<Image />
</View>
</View>
<View>
<View>
<Image />
</View>
</View>
</View>
<Text>
Home
</Text>
</View>
<View
accessibilityRole="button"
>
<View>
<View>
<View>
<Image />
</View>
</View>
<View>
<View>
<Image />
</View>
</View>
</View>
<Text>
Search
</Text>
</View>
<View
accessibilityRole="button"
>
<View>
<View>
<View>
<Image />
</View>
</View>
<View>
<View>
<Image />
</View>
</View>
</View>
<Text>
Discover
</Text>
</View>
<View
accessibilityRole="button"
>
<View>
<View>
<View>
<Image />
</View>
</View>
<View>
<View>
<Image />
</View>
</View>
</View>
<Text>
Profile
</Text>
</View>
</View>
</View>
</View>
</View>
</View>
</RNSScreen>
</RNSScreenStack>
</RNCSafeAreaProvider>
</RNCSafeAreaView> With no |
re. So a part from your example:
Could become
|
Re above two examples: the first one seems too verbose, there are many things added by RN itself, like lots of event handlers on Pressables, empty values for On the other hand the filtered our example looked much more lean, but missined some useful props, like IMO we should strive for practical balance here, adding elements that might help user reference the output views to his own components. We can tweak the list later on, as changes to the exact error output should not be considered a breaking change. |
@mdjastrzebski your proposal of filtering out undefined object values and not printing those props altogether if all values are undefined seems like a huge improvement. I'll go ahead and move forward with updating this PR with the comments from your review, including what we just discussed. Thanks again! |
5f97b20
to
300f8e1
Compare
@mdjastrzebski I believe I have implemented all of your suggestions, so this should be ready for re-review. Thank you again for all of your guidance on this PR 🙂 |
onTimeout(error); | ||
const result = onTimeout(error); | ||
if (result) { | ||
error = result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could probably use extra eyes on this change. When a custom onTimeout
was passed to waitFor
, we did not previously allow for this to alter the returned error. This change makes it so the error returned by the custom onTimeout
is the error that is ultimately reported.
This change was needed for our findBy
optimizations to only print the DOM when the timeout is reached.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also just sharing the DOM Testing Library implementation here for comparison.
It looks like that library has a default onTimeout
that takes the error and appends the DOM to the message in a similar way. The difference being that in DOM Testing Library, if a custom onTimeout
is specified in waitFor
, then that custom onTimeout
would be passed the error without the DOM in the message, and the DOM would never be appended to the output.
My implementation adds the DOM output to the error before passing it to the custom onTimeout
. It seems like the best approach is likely to update this PR to more closely match the DOM Testing Library approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in 2a9fb5c. The behavior should now match DOM Testing Library where if a custom onTimeout
is passed, that function receives the error without the element tree. The element tree is only added in the default onTimeout
if no custom one is passed.
@stevehanson looks promising, as the PR is quite large I will need to spend some time reviewing it. So far I've reviewed I wonder if we could find way to avoid these weird numbers popping in the error messages:
|
I've managed to remove console color control codes from the error output. |
@mdjastrzebski it seems the recent commit removes syntax highlighting not just for the tests but for the actual output. I'm not sure if that was an intended side effect, but I find the syntax highlighting to be quite helpful when parsing large DOM trees.
|
@stevehanson That change was intended but you are right, colors might help users analyse the errors. The reason it bothered me was that all error snapshots contained color control characters. I've thing I've found a good solution here. The coloring of output will depend on @stevehanson hope that works for both of us :-) |
I just wanted to provide an update that I was out of the office last week but am back now and plan to help get this completed this week. The main task I am aware of that is left is to update the |
UPDATE: I found that the reason the element tree is not printing is because when we modify the message in I'm noticing a couple interesting things. If I add and run the following failing test: test('sample', async () => {
const { findByText } = render(<View />);
await findByText(/foo/);
}); I notice two issues: the output does not include the element tree like expected, and the wrong frame is highlighted in the stack trace: If I catch the error in the test and call I think this is why our tests for The other issue I pointed out is that the stacktrace highlights the frame from Potential solutionI dug around the Here's the solution that worked for me: function findByQuery(instance: ReactTestInstance) {
let error: Error; // instantiate the error that will ultimately be returned from the timeout
return function findFn(
predicate: Predicate,
queryOptions?: Options & WaitForOptions,
{
onTimeout = (e: unknown) => {
const timeoutError = e as Error;
if (timeoutError.message) {
// take just the message from the timeout error and return our error instead
// we don't care about the timeoutError stacktrace since we called waitFor from here.
error.message = formatErrorMessage(timeoutError.message, true);
}
return error;
},
...waitForOptions
}: WaitForOptions = {}
) {
error = new ErrorWithStack('FIND_BY_ERROR', findFn); // initialize the error here, removing `findFn` from the trace
const deprecatedWaitForOptions =
extractDeprecatedWaitForOptions(queryOptions);
// append formatted DOM to final error
return waitFor(
() =>
getByQuery(instance, { printElementTree: true })(
predicate,
queryOptions
),
{
...deprecatedWaitForOptions,
...waitForOptions,
onTimeout,
}
);
};
} Here is the now-correct failure output with the above code: Other things that did not workI also tried some other approaches, but nothing else I tried worked. The first thing I tried was manually removing onTimeout = (e: unknown) => {
const error = e as Error;
if (error.message) {
error.message = formatErrorMessage(error.message, true);
}
// remove findFn from stacktrace
if (Error.captureStackTrace) {
Error.captureStackTrace(error, findFn);
}
return error;
} This had a weird effect. The element tree now prints, but the text is muted. Additionally, no stack trace is shown. I can push up the solution I shared above but wanted to first leave this comment and gather feedback while I also continue to explore this, because I feel like there might be a better solution. |
I just had a realization and prepended an update to my comment above with more info. |
@stevehanson I think your solution looks promising. The output looks good. Apply this as a code change. |
@mdjastrzebski great! I can push up the code from my previous comment if that is what you are referring to here, but I wanted to make sure you also saw my commit from yesterday (2a9fb5c). That commit is overall a simpler solution with less potential for regression -- when a timeout occurs, we just append the element tree to the message and also replace the original message on I did try
|
@mdjastrzebski I just wanted to check in to see if there is anything else needed from me to push this PR forward. It was unclear to me from the most recent comment which solution you preferred I go with to resolve the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 Looks awesome!
@stevehanson thank you kindly for submitting this PR and all the hard work into polishing it. Our users and I really appreciate it.
Todo: * Add more tests to mapProps.test * Add tests for other queries * Potentially optimize for findBy queries
* Also updates onTimeout so now matches the behavior of React DOM where if a custom onTimeout is passed, the error that it receives does not have the element tree attached. Reference: https://github.com/testing-library/dom-testing-library/blob/1fc17bec5d28e5b58fcdd325d6d2caaff02dfb47/src/wait-for.js#L26 * Moves general element tree tests to new makeQueries.test.tsx instead of text.test.tsx
a791a91
to
139df37
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #1378 +/- ##
==========================================
+ Coverage 96.14% 96.31% +0.17%
==========================================
Files 50 51 +1
Lines 3317 3475 +158
Branches 492 519 +27
==========================================
+ Hits 3189 3347 +158
Misses 128 128
☔ View full report in Codecov by Sentry. |
This PR has been released in v12.1.0 🚀 |
….2 (#2445) [](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@testing-library/react-native](https://callstack.github.io/react-native-testing-library) ([source](https://togithub.com/callstack/react-native-testing-library)) | [`12.0.1` -> `12.1.2`](https://renovatebot.com/diffs/npm/@testing-library%2freact-native/12.0.1/12.1.2) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | [](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>callstack/react-native-testing-library</summary> ### [`v12.1.2`](https://togithub.com/callstack/react-native-testing-library/releases/tag/v12.1.2) [Compare Source](https://togithub.com/callstack/react-native-testing-library/compare/v12.1.1...v12.1.2) #### What's Changed #### Fixes - fix: pointer events evaluation by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1395](https://togithub.com/callstack/react-native-testing-library/pull/1395) - fix: non-editable wrapped TextInput events by [@​TMaszko](https://togithub.com/TMaszko) in [https://github.com/callstack/react-native-testing-library/pull/1385](https://togithub.com/callstack/react-native-testing-library/pull/1385) - fix: support `onXxx` event name for TextInput event checks by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1404](https://togithub.com/callstack/react-native-testing-library/pull/1404) #### Docs, Chores, etc - docs: add config example for pnpm by [@​yjose](https://togithub.com/yjose) in [https://github.com/callstack/react-native-testing-library/pull/1400](https://togithub.com/callstack/react-native-testing-library/pull/1400) - chore: move/remove deprecation functions by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1402](https://togithub.com/callstack/react-native-testing-library/pull/1402) - refactor: component tree dead code by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1403](https://togithub.com/callstack/react-native-testing-library/pull/1403) - refactor: `fireEvent` cleanup by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1401](https://togithub.com/callstack/react-native-testing-library/pull/1401) #### New Contributors - [@​yjose](https://togithub.com/yjose) made their first contribution in [https://github.com/callstack/react-native-testing-library/pull/1400](https://togithub.com/callstack/react-native-testing-library/pull/1400) 👏 - [@​TMaszko](https://togithub.com/TMaszko) made their first contribution in [https://github.com/callstack/react-native-testing-library/pull/1385](https://togithub.com/callstack/react-native-testing-library/pull/1385) 👏 **Full Changelog**: callstack/react-native-testing-library@v12.1.1...v12.1.2 ### [`v12.1.1`](https://togithub.com/callstack/react-native-testing-library/releases/tag/v12.1.1) [Compare Source](https://togithub.com/callstack/react-native-testing-library/compare/v12.1.0...v12.1.1) #### What's Changed #### Fixes - fix: remove incorrect dependencies by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1399](https://togithub.com/callstack/react-native-testing-library/pull/1399) **Full Changelog**: callstack/react-native-testing-library@v12.1.0...v12.1.1 ### [`v12.1.0`](https://togithub.com/callstack/react-native-testing-library/releases/tag/v12.1.0) [Compare Source](https://togithub.com/callstack/react-native-testing-library/compare/v12.0.1...v12.1.0) #### What's Changed ##### Improvements - feat: Render element tree in query error messages by [@​stevehanson](https://togithub.com/stevehanson) in [https://github.com/callstack/react-native-testing-library/pull/1378](https://togithub.com/callstack/react-native-testing-library/pull/1378) ##### Bugfixes - Proper stack trace for findBy\* and findAllBy\* queries by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1394](https://togithub.com/callstack/react-native-testing-library/pull/1394) #### New Contributors - [@​stevehanson](https://togithub.com/stevehanson) made their first contributions in [#​1377](https://togithub.com/callstack/react-native-testing-library/issues/1377), [#​1378](https://togithub.com/callstack/react-native-testing-library/issues/1378) and [#​1390](https://togithub.com/callstack/react-native-testing-library/issues/1390) 👏 ##### Chores, docs, deps, etc - Fix broken link in contributing.md by [@​stevehanson](https://togithub.com/stevehanson) in [https://github.com/callstack/react-native-testing-library/pull/1377](https://togithub.com/callstack/react-native-testing-library/pull/1377) - chore: update deps 2023-04-04 by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1380](https://togithub.com/callstack/react-native-testing-library/pull/1380) - Fix typo in "derived" in v12 migration guide by [@​CodingItWrong](https://togithub.com/CodingItWrong) in [https://github.com/callstack/react-native-testing-library/pull/1376](https://togithub.com/callstack/react-native-testing-library/pull/1376) - chore: fix migration guide role prop naming by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1382](https://togithub.com/callstack/react-native-testing-library/pull/1382) - fix: "Edit this Page" link in docs results in 404 by [@​stevehanson](https://togithub.com/stevehanson) in [https://github.com/callstack/react-native-testing-library/pull/1390](https://togithub.com/callstack/react-native-testing-library/pull/1390) - refactor: remove stale tests by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1392](https://togithub.com/callstack/react-native-testing-library/pull/1392) - chore: experiments app by [@​mdjastrzebski](https://togithub.com/mdjastrzebski) in [https://github.com/callstack/react-native-testing-library/pull/1391](https://togithub.com/callstack/react-native-testing-library/pull/1391) **Full Changelog**: callstack/react-native-testing-library@v12.0.1...v12.1.0 </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/dooboolab-community/react-native-iap). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS45OC40IiwidXBkYXRlZEluVmVyIjoiMzUuMTMxLjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIn0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Summary
Closes #1375.
This commit updates the error message to the
ByText
query so that it outputs the virtual DOM, which can help the developer pinpoint the error without having to rerun the test with adebug()
statement.Update: this PR now outputs the element tree for all query errors, not just
ByText
.Before
After
Screenshot
Here is an example of an error when an element does not match because it is hidden (in this case,
accessibilityElementsHidden: true
). The relevant prop is included. The virtual DOM also is formatted with syntax highlighting:Preserving visibility-related props
By default, the
byText
query only matches visible elements, so any props that hide an element are relevant to the test failure. For that reason, this PR preserves any props that hide an element. I followed the logic from isSubtreeInaccessible to determine how to do this:accessibilityElementsHidden=true
accessibilityViewIsModal=true
importantForAccessibility="no-hide-descendants"
style: { display: 'none' }
Test plan
I've added unit tests for the
mapVisibilityRelatedProps
function as well as snapshot tests for the output ofgetByText
with all of the above conditions. The snapshots are a little ugly since the formatted virtual DOM includes special characters for the syntax highlighting.