-
Notifications
You must be signed in to change notification settings - Fork 264
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Render element tree in query error messages (#1378)
Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com>
- Loading branch information
1 parent
5f770cb
commit f7c8400
Showing
22 changed files
with
1,321 additions
and
196 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { ReactTestRendererJSON } from 'react-test-renderer'; | ||
import { defaultMapProps } from '../format-default'; | ||
|
||
const node: ReactTestRendererJSON = { | ||
type: 'View', | ||
props: {}, | ||
children: null, | ||
}; | ||
|
||
describe('mapPropsForQueryError', () => { | ||
test('preserves props that are helpful for debugging', () => { | ||
const props = { | ||
accessibilityElementsHidden: true, | ||
accessibilityViewIsModal: true, | ||
importantForAccessibility: 'yes', | ||
testID: 'TEST_ID', | ||
nativeID: 'NATIVE_ID', | ||
accessibilityLabel: 'LABEL', | ||
accessibilityLabelledBy: 'LABELLED_BY', | ||
accessibilityRole: 'ROLE', | ||
accessibilityHint: 'HINT', | ||
placeholder: 'PLACEHOLDER', | ||
value: 'VALUE', | ||
defaultValue: 'DEFAULT_VALUE', | ||
}; | ||
|
||
const result = defaultMapProps(props, node); | ||
|
||
expect(result).toStrictEqual(props); | ||
}); | ||
|
||
test('does not preserve less helpful props', () => { | ||
const result = defaultMapProps( | ||
{ | ||
style: [{ flex: 1 }, { display: 'flex' }], | ||
onPress: () => null, | ||
key: 'foo', | ||
}, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({}); | ||
}); | ||
|
||
test('preserves "display: none" style but no other style', () => { | ||
const result = defaultMapProps( | ||
{ style: [{ flex: 1 }, { display: 'none', flex: 2 }] }, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({ | ||
style: { display: 'none' }, | ||
}); | ||
}); | ||
|
||
test('removes undefined keys from accessibilityState', () => { | ||
const result = defaultMapProps( | ||
{ accessibilityState: { checked: undefined, selected: false } }, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({ | ||
accessibilityState: { selected: false }, | ||
}); | ||
}); | ||
|
||
test('removes accessibilityState if all keys are undefined', () => { | ||
const result = defaultMapProps( | ||
{ accessibilityState: { checked: undefined, selected: undefined } }, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({}); | ||
}); | ||
|
||
test('does not fail if accessibilityState is a string, passes through', () => { | ||
const result = defaultMapProps({ accessibilityState: 'foo' }, node); | ||
expect(result).toStrictEqual({ accessibilityState: 'foo' }); | ||
}); | ||
|
||
test('does not fail if accessibilityState is an array, passes through', () => { | ||
const result = defaultMapProps({ accessibilityState: [1] }, node); | ||
expect(result).toStrictEqual({ accessibilityState: [1] }); | ||
}); | ||
|
||
test('does not fail if accessibilityState is null, passes through', () => { | ||
const result = defaultMapProps({ accessibilityState: null }, node); | ||
expect(result).toStrictEqual({ accessibilityState: null }); | ||
}); | ||
|
||
test('does not fail if accessibilityState is nested object, passes through', () => { | ||
const accessibilityState = { 1: { 2: 3 }, 2: undefined }; | ||
const result = defaultMapProps({ accessibilityState }, node); | ||
expect(result).toStrictEqual({ accessibilityState: { 1: { 2: 3 } } }); | ||
}); | ||
|
||
test('removes undefined keys from accessibilityValue', () => { | ||
const result = defaultMapProps( | ||
{ accessibilityValue: { min: 1, max: undefined } }, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({ accessibilityValue: { min: 1 } }); | ||
}); | ||
|
||
test('removes accessibilityValue if all keys are undefined', () => { | ||
const result = defaultMapProps( | ||
{ accessibilityValue: { min: undefined } }, | ||
node | ||
); | ||
|
||
expect(result).toStrictEqual({}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { StyleSheet, ViewStyle } from 'react-native'; | ||
import { MapPropsFunction } from './format'; | ||
|
||
const propsToDisplay = [ | ||
'testID', | ||
'nativeID', | ||
'accessibilityElementsHidden', | ||
'accessibilityViewIsModal', | ||
'importantForAccessibility', | ||
'accessibilityRole', | ||
'accessibilityLabel', | ||
'accessibilityLabelledBy', | ||
'accessibilityHint', | ||
'placeholder', | ||
'value', | ||
'defaultValue', | ||
'title', | ||
]; | ||
|
||
/** | ||
* Preserve props that are helpful in diagnosing test failures, while stripping rest | ||
*/ | ||
export const defaultMapProps: MapPropsFunction = (props) => { | ||
const result: Record<string, unknown> = {}; | ||
|
||
const styles = StyleSheet.flatten(props.style as ViewStyle); | ||
if (styles?.display === 'none') { | ||
result.style = { display: 'none' }; | ||
} | ||
|
||
const accessibilityState = removeUndefinedKeys(props.accessibilityState); | ||
if (accessibilityState !== undefined) { | ||
result.accessibilityState = accessibilityState; | ||
} | ||
|
||
const accessibilityValue = removeUndefinedKeys(props.accessibilityValue); | ||
if (accessibilityValue !== undefined) { | ||
result.accessibilityValue = accessibilityValue; | ||
} | ||
|
||
propsToDisplay.forEach((propName) => { | ||
if (propName in props) { | ||
result[propName] = props[propName]; | ||
} | ||
}); | ||
|
||
return result; | ||
}; | ||
|
||
function isObject(value: unknown): value is Record<string, unknown> { | ||
return typeof value === 'object' && value !== null && !Array.isArray(value); | ||
} | ||
|
||
function removeUndefinedKeys(prop: unknown) { | ||
if (!isObject(prop)) { | ||
return prop; | ||
} | ||
|
||
const result: Record<string, unknown> = {}; | ||
Object.entries(prop).forEach(([key, value]) => { | ||
if (value !== undefined) { | ||
result[key] = value; | ||
} | ||
}); | ||
|
||
// If object does not have any props we will ignore it. | ||
if (Object.keys(result).length === 0) { | ||
return undefined; | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.