After the RN upgrade, the
by.traits(['selected'])stopped working. It seems that Detox doesn't fully support 0.61 and I'm open to helping with this if some sort of guidance can be given to me (where should I change stuff to identify why it fails)
The same error is described at Stackoverflow but I think it may not be just a
help request
The suite that is failing has basically two tests:
[PASS] element(by.id('FirstName').withAncestor(by.id('SignUpScreen'));
[FAIL] element(by.id('FirstName').withAncestor(by.id('SignUpScreen')).and(by.traits(['selected'])));
The failing one shows the following "query":
(
(
!(kindOfClass('RCTScrollView')) &&
(
(
(
(respondsToSelector(accessibilityIdentifier) && accessibilityID('FirstName'))
&& (
(kindOfClass('UIView') || respondsToSelector(accessibilityContainer))
&& ancestorThatMatches((respondsToSelector(accessibilityIdentifier) && accessibilityID('SignUpScreen')))
)
) && ((respondsToSelector(isAccessibilityElement) && isAccessibilityElement) && accessibilityTraits: UIAccessibilityTraitSelected))
&& !(kindOfClass('UIAccessibilityTextFieldElement'))
)
) ||
(
(
(kindOfClass('UIView') || respondsToSelector(accessibilityContainer))
&& parentThatMatches(kindOfClass('RCTScrollView'))
)
&& (
(kindOfClass('UIView') || respondsToSelector(accessibilityContainer))
&& parentThatMatches(((
(
(respondsToSelector(accessibilityIdentifier) && accessibilityID('FirstName')) &&
(
(kindOfClass('UIView') || respondsToSelector(accessibilityContainer))
&& ancestorThatMatches((respondsToSelector(accessibilityIdentifier) && accessibilityID('SignUpScreen')))
)
) && ((respondsToSelector(isAccessibilityElement) && isAccessibilityElement) && accessibilityTraits: UIAccessibilityTraitSelected)
) && !(kindOfClass('UIAccessibilityTextFieldElement'))))
)
)
)
And the following hierarchy (cleaned up a bit):
|--<RCTScrollView:0x7f92ab6ee4d0; AX=N; AX.id='SignUpScreen';>
| | |--<RCTView>
| | | |--<RCTSinglelineTextInputView>
| | | | |--<RCTUITextField AX.id='LastName'; AX.traits='UIAccessibilityTraitSelected'>
| | | | | |--<_UITextFieldCanvasView>
| | | | | |--<UIAccessibilityTextFieldElement AX.id='LastName'; AX.traits='UIAccessibilityTraitSelected'>
| | |--<RCTTextView AX.id='Typography'; AX.label='Last name'; AX.traits='UIAccessibilityTraitStaticText'>
| |--<RCTView AX.label='First name';>
| | |--<RCTView>
| | | |--<RCTSinglelineTextInputView>
| | | | |--<RCTUITextField AX.id='FirstName'; AX.traits='UIAccessibilityTraitSelected'>
| | | | | |--<UIFieldEditor>
| | | | | | |--<_UITextFieldCanvasView>
| | | | | | | |--<UITextSelectionView>
| | | | | | | | |--<UIView>
| | | | | |--<UIAccessibilityTextFieldElement AX.id='FirstName'; AX.traits='UIAccessibilityTraitSelected'>
Now, it's weird to me that the whole problem is around the .and(by.traits(['selected']))) part, which used to work at 0.59.10.
And, as far as I understand, both query and hierarchy have the UIAccessibilityTraitSelected... so... why exactly it is failing?
What am I missing?
Some other tests I did:
[FAIL] element(by.id('FirstName').and(by.traits(['selected'])))
[FAIL] element(by.traits(['selected']).withAncestor(by.id('SignUpScreen')))
[PASS] element(by.id('FirstName'))
[PASS] element(by.id('FirstName').withAncestor(by.id('SignUpScreen')))
I did an isolated test with the following:
<>
<View
style={{flex: 1, backgroundColor: 'cyan', padding: 60}}
testID="Ancestor">
<TextInput testID="Input" value="Input" style={style} />
<TextInput
testID="InputSelectedStates"
value="InputSelectedStates"
accessibilityStates={['selected']}
style={style}
/>
<TextInput
testID="InputSelectedState"
value="InputSelectedState"
accessibilityState={{selected: true}}
style={style}
/>
</View>
</>
[PASS]
it('A', async () => {
await expect(element(by.id('Ancestor'))).toExist();
});
[PASS]
it('B', async () => {
await expect(element(by.id('Input'))).toExist();
});
[PASS]
it('C', async () => {
await expect(element(by.id('InputSelectedStates'))).toExist();
});
[PASS]
it('D', async () => {
await expect(element(by.id('InputSelectedState'))).toExist();
});
[FAIL]
it('E', async () => {
await expect(element(by.traits(['selected']))).toExist();
});
[PASS]
it('AB', async () => {
await expect(
element(by.id('Input').withAncestor(by.id('Ancestor'))),
).toExist();
});
[PASS]
it('AC', async () => {
await expect(
element(by.id('InputSelectedStates').withAncestor(by.id('Ancestor'))),
).toExist();
});
[PASS]
it('AD', async () => {
await expect(
element(by.id('InputSelectedState').withAncestor(by.id('Ancestor'))),
).toExist();
});
[PASS]
it('AE', async () => {
await expect(
element(by.id('Input').and(by.traits(['selected']))),
).toNotExist();
});
[FAIL]
it('BE', async () => {
await expect(
element(by.id('InputSelectedStates').and(by.traits(['selected']))),
).toExist();
});
[FAIL]
it('CE', async () => {
await expect(
element(by.id('InputSelectedState').and(by.traits(['selected']))),
).toExist();
});
Hierarchy:
<UIWindow AX.traits='UIAccessibilityTraitNone'>
|--<UITransitionView AX.traits='UIAccessibilityTraitNone'>
| |--<UIDropShadowView AX.traits='UIAccessibilityTraitNone'>
| | |--<RCTRootView AX.traits='UIAccessibilityTraitNone'>
| | | |--<RCTRootContentView AX.traits='UIAccessibilityTraitNone'>
| | | | |--<RCTView AX.traits='UIAccessibilityTraitNone'>
| | | | | |--<RCTView AX.traits='UIAccessibilityTraitNone'>
| | | | | | |--<RCTView AX.id='Ancestor'; AX.traits='UIAccessibilityTraitNone'>
| | | | | | | |--<RCTSinglelineTextInputView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | |--<RCTUITextField
| | | | | | | | | AX.id='InputSelectedState';
| | | | | | | | | AX.value='InputSelectedState';
| | | | | | | | | AX.traits='UIAccessibilityTraitSelected';
| | | | | | | | | text='InputSelectedState'>
| | | | | | | | | |--<_UITextFieldCanvasView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | | |--<UIAccessibilityTextFieldElement
| | | | | | | | | AX.id='InputSelectedState';
| | | | | | | | | AX.value='InputSelectedState';
| | | | | | | | | AX.traits='UIAccessibilityTraitSelected'>
| | | | | | | |--<RCTSinglelineTextInputView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | |--<RCTUITextField
| | | | | | | | | AX.id='InputSelectedStates';
| | | | | | | | | AX.value='InputSelectedStates';
| | | | | | | | | AX.traits='UIAccessibilityTraitSelected';
| | | | | | | | | text='InputSelectedStates'>
| | | | | | | | | |--<_UITextFieldCanvasView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | | |--<UIAccessibilityTextFieldElement
| | | | | | | | | AX.id='InputSelectedStates';
| | | | | | | | | AX.value='InputSelectedStates';
| | | | | | | | | AX.traits='UIAccessibilityTraitSelected'>
| | | | | | | |--<RCTSinglelineTextInputView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | |--<RCTUITextField
| | | | | | | | | AX.id='Input';
| | | | | | | | | AX.value='Input';
| | | | | | | | | AX.traits='UIAccessibilityTraitNone';
| | | | | | | | | text='Input'>
| | | | | | | | | |--<_UITextFieldCanvasView AX.traits='UIAccessibilityTraitNone'>
| | | | | | | | | |--<UIAccessibilityTextFieldElement
| | | | | | | | | AX.id='Input';
| | | | | | | | | AX.value='Input';
| | | | | | | | | AX.traits='UIAccessibilityTraitNone'>
Query for element(by.traits(['selected'])):
(
(
!(kindOfClass('RCTScrollView'))
&&
(
(
(
respondsToSelector(isAccessibilityElement)
&&
isAccessibilityElement
)
&&
accessibilityTraits: UIAccessibilityTraitSelected
)
&&
!(
kindOfClass('UIAccessibilityTextFieldElement')
)
)
)
||
(
(
(
kindOfClass('UIView')
||
respondsToSelector(accessibilityContainer)
)
&&
parentThatMatches(kindOfClass('RCTScrollView'))
)
&&
(
(
kindOfClass('UIView')
||
respondsToSelector(accessibilityContainer)
)
&&
parentThatMatches(
(
(
(
respondsToSelector(isAccessibilityElement)
&&
isAccessibilityElement
)
&&
accessibilityTraits: UIAccessibilityTraitSelected
)
&&
!(kindOfClass('UIAccessibilityTextFieldElement'))
)
)
)
)
)
Seems that the problem is the accessibilityTraits: UIAccessibilityTraitSelected part...
Hello,
We are currently working on a new underlying system to replace Earl Grey, so until that arrives, I don't think we'll invest much time in Earl Grey issues (which this looks like).
If you'd like to assist with debugging, please follow the Xcode integration guide and investigate why Earl Grey fails to match this combination.
Thanks
Is there any timeline for this new tool?
When it鈥檚 done. There is a lot of work still, and also it will take a while to stabilize.
Also, hard for me to see why it would work in .59 but not in .61 if the trait is set correctly by RN.
@LeoNatan After further tests, it seems that there's some type of delay happening. Some times it works and some times they don't. I'll try to change to selected for now... Maybe it's an RN-related issue.
But if the hierarchy after failure shows that the trait is there, it doesn't make sense that the matcher system in EG would fail to find it...
@LeoNatan The Accessibility Inspector doesn't show anything like "selected" on them. Now, if I change to checked instead, it shows the value as checked. I'll try to dig a bit more over the weekend.
Thanks for the help so far.
Great, looking forward to your findings.
@LeoNatan Seems that the whole problem is around https://github.com/wix/Detox/commit/fd02f1f98f5a2f0789b43aaccf47bb0ce1ba1e4e#diff-63bcd6e377115ee1fcf2d9deec677a51 .
More exactly:
matcher = grey_allOf(matcher, grey_not(grey_kindOfClass(NSClassFromString(@"UIAccessibilityTextFieldElement"))), nil);
I did a test with the same selectors but Text instead of TextInput and they worked.
You can check a bit about the tests here:
https://gist.github.com/sidferreira/ea637de661c710468fc57a1eb997cff9
This issue made me think about changing some of my tests for Focused instead of Selected but it seems that Detox matchers are incomplete.
I found https://github.com/wix/Detox/blob/bd96780505ebcdabde45c285bebd4644e46d0091/detox/src/ios/earlgreyapi/GREYMatchers.js#L209 it is not in use.
Anyways, WIP: https://github.com/sidferreira/Detox/tree/toBeFocused
I don't know what "focused" means. No such accessibility trait exists in iOS:
In general, "focus" as a term on iOS does not exist. It exists in tvOS, where what Apple calls the "focus engine" is used for the cursor system. That's not in use in iOS.
@LeoNatan https://google.github.io/EarlGrey/Classes/GREYMatchers.html#/c:objc(cs)GREYMatchers(cm)matcherForAccessibilityElementIsFocused
I see that it uses this method to determine selection:
https://developer.apple.com/documentation/objectivec/nsobject/1615190-accessibilityelementisfocused?language=objc
I am not aware what this means. "YES if an assistive technology is virtually focused on the element" tells me nothing. Apple docs are terrible.
Regarding UIAccessibilityTextFieldElement, could you please explain why you think this is the problem?
Even in your example, you can see that this element exists, but is not the one you want:
|--<RCTScrollView:0x7f92ab6ee4d0; AX=N; AX.id='SignUpScreen';>
| | |--<RCTView>
| | | |--<RCTSinglelineTextInputView>
| | | | |--<RCTUITextField AX.id='LastName'; AX.traits='UIAccessibilityTraitSelected'>
| | | | | |--<_UITextFieldCanvasView>
| | | | | |--<UIAccessibilityTextFieldElement AX.id='LastName'; AX.traits='UIAccessibilityTraitSelected'>
The element you wish to match is RCTUITextField, rather than UIAccessibilityTextFieldElement.
Looking at what is possible to query using the new system we are working on, there is both selected and hasFocus:
https://developer.apple.com/documentation/xctest/xcuielementattributes?language=objc
If you can, could you use the accessibility inspector on your views, and see when focused and selected change?
toBeFocused (or hasFocus, whatever :) )
This is a test we need on a screen. We want to be sure the inputs lose the focus in some cases. I reviewed EarGrey method and seems to be when VoiceOver or something is focusing on that element, not if an input has the focus or not (what I'm looking for).
The query issue
I dug a bit more and I noticed one small detail I missed previously:
|--<RCTUITextField:0x7f9f0600fe00;
AX=N;
AX.id='InputSelectedState';
AX.value='InputSelectedState';
AX.frame={{65, 119}, {284, 17}};
AX.activationPoint={207, 127.5};
AX.traits='UIAccessibilityTraitSelected';
AX.focused='N';
frame={{5, 5}, {284, 17}};
alpha=1;
text='InputSelectedState'>
| |--<_UITextFieldCanvasView:0x7f9f05909fb0;
AX=N;
AX.frame={{65, 119}, {284, 17}};
AX.activationPoint={207, 127.5};
AX.traits='UIAccessibilityTraitNone';
AX.focused='N';
frame={{0, 0}, {284, 17}};
alpha=1;
UIE=N>
| |--<UIAccessibilityTextFieldElement:0x6000017def00;
AX=Y;
AX.id='InputSelectedState';
AX.value='InputSelectedState';
AX.frame={{65, 119}, {284, 17}};
AX.activationPoint={207, 127.5};
AX.traits='UIAccessibilityTraitSelected';
AX.focused='N'>
RCTUITextField has AX=N... Does that mean that (respondsToSelector(isAccessibilityElement) && isAccessibilityElement) returns false?
About your other tests, I'll run them ASAP... But it

seems that the fields with selected doesn't show as selected...
Do you happen to have the same project before upgrading to RN61? Could and probably is a bug on their end.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back.
Thank you for your contributions!
For more information on bots in this reporsitory, read this discussion.
The issue has been closed for inactivity.