-
Notifications
You must be signed in to change notification settings - Fork 274
feat(breaking): Return host component for all queries #1234
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
Merged
mdjastrzebski
merged 45 commits into
callstack:main
from
pierrezimmermannbam:poc/hostComponentQueries
Jan 15, 2023
Merged
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
5b214bb
BREAKING CHANGE : make placeholdertext queries return host elements
88c7f93
BREAKING CHANGE : make displayValue queries return host elements
0b229b6
BREAKING CHANGE: make text queries return host elements
5624984
refactor: reset config to default before each test
6bbf105
feat: use component names from config and add api to configure them
80e1139
feat: remove legacy option from queries
pierrezimmermannbam 8ee6995
refactor: reorganize legacy and breaking tests for queries
pierrezimmermannbam bdcbdf2
feat: change error messages for mismatching host component names
pierrezimmermannbam f34d77d
fix: do not update screen when running auto detection
pierrezimmermannbam 5795c41
fix: typo in comment
pierrezimmermannbam 6648c5c
refactor: use an object for matchTextContent params
pierrezimmermannbam 6314f59
tests: rewrite some tests to match repo patterns
pierrezimmermannbam 34eb57a
fix: use legacy queries by default to prevent breaking changes
pierrezimmermannbam d09a738
fix: remove wrong uppercase in flow typings
pierrezimmermannbam fd5c342
docs: remove section on useLegacy queries as it wont be a public api
pierrezimmermannbam 17e42a6
docs: small improvements based on review
pierrezimmermannbam 568981e
fix: remove typo in error message
46c70a4
refactor: rename getReactNativeHostComponentNames to detectHostCompon…
eb30f96
refactor: use useBreakingChange from internal config instead of useLe…
2245bd8
fix: use fake timers in test so it doesnt take too much time
c19a7db
refactor: replace type assertions by real type checking
78724f3
fix: revert change due to rebase
e911932
fix: improvements after review
f627ad4
refactor: define type to match inside matchTextContent function
9eb4dc4
refactor: rename function to get error message
57b704e
feat: display error when auto detect of host component names throw
751af46
fix: remove unused ts-expect-error after ts version bump
366f08f
refactor: run auto detection on render if it hasnt been run yet
e83c806
refactor: reuse type imports that had been converted to regular ones
8377660
refactor: add comments to distinguish breaking and legacy
3c8fd8c
refactor: move config logic into detectComponentHostNames function
b454746
refactor: rename function to detectComponentHostNamesIfNeeded
8145557
tests: add test on detectHostComponentNamesIfNeeded
6f1fdd0
fix: use host component name from config for textinput in fireEvent
097465c
refactor: run autodetection in text, displayvalue and placeholder que…
0568bc0
refactor: remove useless async declaration
820d164
refactor: improvements for readability from review
ceed10e
Update src/__tests__/host-component-names.test.tsx
mdjastrzebski 6b18bbe
refactor: declare variable for host component names in detection func…
a7ae9e1
refactor: rename detectHostComponentNamesIfNeeded to getHostComponent…
b25abeb
feat: use getHostComponentNames in fireEvent for textinput host name
80788c2
refactor: revert refactor on matchTextContent
def1cc4
refactor: add comment to indicate impossible path
7557fa3
chore: simplify jest-setup files
mdjastrzebski 880b7e8
refactor: code review changes
mdjastrzebski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,7 @@ | ||
import { resetToDefaults } from './src/pure'; | ||
|
||
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); | ||
|
||
beforeEach(() => { | ||
resetToDefaults(); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 hidden or 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 hidden or 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,48 @@ | ||
import { View } from 'react-native'; | ||
import TestRenderer from 'react-test-renderer'; | ||
import { configureInternal, getConfig } from '../config'; | ||
import { getHostComponentNames } from '../helpers/host-component-names'; | ||
|
||
const mockCreate = jest.spyOn(TestRenderer, 'create') as jest.Mock; | ||
|
||
describe('getHostComponentNames', () => { | ||
test('updates internal config with host component names when they are not defined', () => { | ||
expect(getConfig().hostComponentNames).toBeUndefined(); | ||
|
||
getHostComponentNames(); | ||
|
||
expect(getConfig().hostComponentNames).toEqual({ | ||
text: 'Text', | ||
textInput: 'TextInput', | ||
}); | ||
}); | ||
|
||
test('does not update internal config when host component names are already configured', () => { | ||
configureInternal({ | ||
hostComponentNames: { text: 'banana', textInput: 'banana' }, | ||
}); | ||
|
||
getHostComponentNames(); | ||
|
||
expect(getConfig().hostComponentNames).toEqual({ | ||
text: 'banana', | ||
textInput: 'banana', | ||
}); | ||
}); | ||
|
||
test('throw an error when autodetection fails', () => { | ||
mockCreate.mockReturnValue({ | ||
root: { type: View, children: [], props: {} }, | ||
}); | ||
|
||
expect(() => getHostComponentNames()).toThrowErrorMatchingInlineSnapshot(` | ||
"Trying to detect host component names triggered the following error: | ||
|
||
Unable to find an element with testID: text | ||
|
||
There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly. | ||
Please check if you are using compatible versions of React Native and React Native Testing Library. | ||
" | ||
`); | ||
}); | ||
}); |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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,50 @@ | ||
import React from 'react'; | ||
import { Text, TextInput, View } from 'react-native'; | ||
import TestRenderer from 'react-test-renderer'; | ||
import { configureInternal, getConfig, HostComponentNames } from '../config'; | ||
import { getQueriesForElement } from '../within'; | ||
|
||
const defaultErrorMessage = `There seems to be an issue with your configuration that prevents React Native Testing Library from working correctly. | ||
Please check if you are using compatible versions of React Native and React Native Testing Library.`; | ||
|
||
export function getHostComponentNames(): HostComponentNames { | ||
const configHostComponentNames = getConfig().hostComponentNames; | ||
if (configHostComponentNames) { | ||
return configHostComponentNames; | ||
} | ||
|
||
try { | ||
const renderer = TestRenderer.create( | ||
<View> | ||
<Text testID="text">Hello</Text> | ||
<TextInput testID="textInput" /> | ||
</View> | ||
); | ||
const { getByTestId } = getQueriesForElement(renderer.root); | ||
const textHostName = getByTestId('text').type; | ||
const textInputHostName = getByTestId('textInput').type; | ||
|
||
// This code path should not happen as getByTestId always returns host elements. | ||
if ( | ||
typeof textHostName !== 'string' || | ||
typeof textInputHostName !== 'string' | ||
) { | ||
throw new Error(defaultErrorMessage); | ||
pierrezimmermannbam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
const hostComponentNames = { | ||
text: textHostName, | ||
textInput: textInputHostName, | ||
}; | ||
configureInternal({ hostComponentNames }); | ||
return hostComponentNames; | ||
} catch (error) { | ||
const errorMessage = | ||
error && typeof error === 'object' && 'message' in error | ||
? error.message | ||
: null; | ||
|
||
throw new Error(`Trying to detect host component names triggered the following error:\n\n${errorMessage}\n\n${defaultErrorMessage} | ||
`); | ||
} | ||
} |
This file contains hidden or 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 hidden or 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,136 @@ | ||
import * as React from 'react'; | ||
pierrezimmermannbam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { View, TextInput } from 'react-native'; | ||
|
||
import { render } from '../..'; | ||
import { configureInternal } from '../../config'; | ||
|
||
const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; | ||
const PLACEHOLDER_CHEF = 'Who inspected freshness?'; | ||
const INPUT_FRESHNESS = 'Custom Freshie'; | ||
const INPUT_CHEF = 'I inspected freshie'; | ||
const DEFAULT_INPUT_CHEF = 'What did you inspect?'; | ||
const DEFAULT_INPUT_CUSTOMER = 'What banana?'; | ||
|
||
beforeEach(() => configureInternal({ useBreakingChanges: true })); | ||
|
||
const Banana = () => ( | ||
<View> | ||
<TextInput | ||
testID="bananaCustomFreshness" | ||
placeholder={PLACEHOLDER_FRESHNESS} | ||
value={INPUT_FRESHNESS} | ||
/> | ||
<TextInput | ||
testID="bananaChef" | ||
placeholder={PLACEHOLDER_CHEF} | ||
value={INPUT_CHEF} | ||
defaultValue={DEFAULT_INPUT_CHEF} | ||
/> | ||
<TextInput defaultValue={DEFAULT_INPUT_CUSTOMER} /> | ||
<TextInput defaultValue={'hello'} value="" /> | ||
</View> | ||
); | ||
|
||
test('getByDisplayValue, queryByDisplayValue', () => { | ||
const { getByDisplayValue, queryByDisplayValue } = render(<Banana />); | ||
const input = getByDisplayValue(/custom/i); | ||
|
||
expect(input.props.value).toBe(INPUT_FRESHNESS); | ||
|
||
const sameInput = getByDisplayValue(INPUT_FRESHNESS); | ||
|
||
expect(sameInput.props.value).toBe(INPUT_FRESHNESS); | ||
expect(() => getByDisplayValue('no value')).toThrow( | ||
'Unable to find an element with displayValue: no value' | ||
); | ||
|
||
expect(queryByDisplayValue(/custom/i)).toBe(input); | ||
expect(queryByDisplayValue('no value')).toBeNull(); | ||
expect(() => queryByDisplayValue(/fresh/i)).toThrow( | ||
'Found multiple elements with display value: /fresh/i' | ||
); | ||
}); | ||
|
||
test('getByDisplayValue, queryByDisplayValue get element by default value only when value is undefined', () => { | ||
const { getByDisplayValue, queryByDisplayValue } = render(<Banana />); | ||
expect(() => | ||
getByDisplayValue(DEFAULT_INPUT_CHEF) | ||
).toThrowErrorMatchingInlineSnapshot( | ||
`"Unable to find an element with displayValue: What did you inspect?"` | ||
); | ||
expect(queryByDisplayValue(DEFAULT_INPUT_CHEF)).toBeNull(); | ||
|
||
expect(() => getByDisplayValue('hello')).toThrowErrorMatchingInlineSnapshot( | ||
`"Unable to find an element with displayValue: hello"` | ||
); | ||
expect(queryByDisplayValue('hello')).toBeNull(); | ||
|
||
expect(getByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy(); | ||
expect(queryByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy(); | ||
}); | ||
|
||
test('getAllByDisplayValue, queryAllByDisplayValue', () => { | ||
const { getAllByDisplayValue, queryAllByDisplayValue } = render(<Banana />); | ||
const inputs = getAllByDisplayValue(/fresh/i); | ||
|
||
expect(inputs).toHaveLength(2); | ||
expect(() => getAllByDisplayValue('no value')).toThrow( | ||
'Unable to find an element with displayValue: no value' | ||
); | ||
|
||
expect(queryAllByDisplayValue(/fresh/i)).toEqual(inputs); | ||
expect(queryAllByDisplayValue('no value')).toHaveLength(0); | ||
}); | ||
|
||
test('findBy queries work asynchronously', async () => { | ||
const options = { timeout: 10 }; // Short timeout so that this test runs quickly | ||
const { rerender, findByDisplayValue, findAllByDisplayValue } = render( | ||
<View /> | ||
); | ||
|
||
await expect( | ||
findByDisplayValue('Display Value', {}, options) | ||
).rejects.toBeTruthy(); | ||
await expect( | ||
findAllByDisplayValue('Display Value', {}, options) | ||
).rejects.toBeTruthy(); | ||
|
||
setTimeout( | ||
() => | ||
rerender( | ||
<View> | ||
<TextInput value="Display Value" /> | ||
</View> | ||
), | ||
20 | ||
); | ||
|
||
await expect(findByDisplayValue('Display Value')).resolves.toBeTruthy(); | ||
await expect(findAllByDisplayValue('Display Value')).resolves.toHaveLength(1); | ||
}, 20000); | ||
|
||
test('byDisplayValue queries support hidden option', () => { | ||
const { getByDisplayValue, queryByDisplayValue } = render( | ||
<TextInput value="hidden" style={{ display: 'none' }} /> | ||
); | ||
|
||
expect(getByDisplayValue('hidden')).toBeTruthy(); | ||
expect( | ||
getByDisplayValue('hidden', { includeHiddenElements: true }) | ||
).toBeTruthy(); | ||
|
||
expect( | ||
queryByDisplayValue('hidden', { includeHiddenElements: false }) | ||
).toBeFalsy(); | ||
expect(() => | ||
getByDisplayValue('hidden', { includeHiddenElements: false }) | ||
).toThrowErrorMatchingInlineSnapshot( | ||
`"Unable to find an element with displayValue: hidden"` | ||
); | ||
}); | ||
|
||
test('byDisplayValue should return host component', () => { | ||
const { getByDisplayValue } = render(<TextInput value="value" />); | ||
|
||
expect(getByDisplayValue('value').type).toBe('TextInput'); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.