diff --git a/detox/ios/Detox/GREYMatchers+Detox.h b/detox/ios/Detox/GREYMatchers+Detox.h index 98e443bca8..4b18bc22ec 100644 --- a/detox/ios/Detox/GREYMatchers+Detox.h +++ b/detox/ios/Detox/GREYMatchers+Detox.h @@ -12,6 +12,8 @@ + (id)detoxMatcherForText:(NSString *)text; ++ (id)detox_matcherForAccessibilityLabel:(NSString *)label; + + (id)detoxMatcherForScrollChildOfMatcher:(id)matcher; + (id)detoxMatcherAvoidingProblematicReactNativeElements:(id)matcher; diff --git a/detox/src/ios/earlgreyapi/GREYActions.js b/detox/src/ios/earlgreyapi/GREYActions.js index 2ea832c6bd..35fe48c80d 100644 --- a/detox/src/ios/earlgreyapi/GREYActions.js +++ b/detox/src/ios/earlgreyapi/GREYActions.js @@ -39,6 +39,33 @@ function sanitize_greyContentEdge(action) { } } +function sanitize_uiAccessibilityTraits(value) { + let traits = 0; + for (let i = 0; i < value.length; i++) { + switch (value[i]) { + case 'button': traits |= 1; break; + case 'link': traits |= 2; break; + case 'header': traits |= 4; break; + case 'search': traits |= 8; break; + case 'image': traits |= 16; break; + case 'selected': traits |= 32; break; + case 'plays': traits |= 64; break; + case 'key': traits |= 128; break; + case 'text': traits |= 256; break; + case 'summary': traits |= 512; break; + case 'disabled': traits |= 1024; break; + case 'frequentUpdates': traits |= 2048; break; + case 'startsMedia': traits |= 4096; break; + case 'adjustable': traits |= 8192; break; + case 'allowsDirectInteraction': traits |= 16384; break; + case 'pageTurn': traits |= 32768; break; + default: throw new Error(`Unknown trait '${value[i]}', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios`); + } + } + + return traits; +} + class GREYActions { diff --git a/detox/src/ios/earlgreyapi/GREYMatchers+Detox.js b/detox/src/ios/earlgreyapi/GREYMatchers+Detox.js new file mode 100644 index 0000000000..2cb46e1fa6 --- /dev/null +++ b/detox/src/ios/earlgreyapi/GREYMatchers+Detox.js @@ -0,0 +1,221 @@ +/** + + This code is generated. + For more information see generation/README.md. +*/ + + +// Globally declared helpers + +function sanitize_greyDirection(action) { + switch (action) { + case "left": + return 1; + case "right": + return 2; + case "up": + return 3; + case "down": + return 4; + + default: + throw new Error(`GREYAction.GREYDirection must be a 'left'/'right'/'up'/'down', got ${action}`); + } +} + +function sanitize_greyContentEdge(action) { + switch (action) { + case "left": + return 0; + case "right": + return 1; + case "top": + return 2; + case "bottom": + return 3; + + default: + throw new Error(`GREYAction.GREYContentEdge must be a 'left'/'right'/'top'/'bottom', got ${action}`); + } +} + +function sanitize_uiAccessibilityTraits(value) { + let traits = 0; + for (let i = 0; i < value.length; i++) { + switch (value[i]) { + case 'button': traits |= 1; break; + case 'link': traits |= 2; break; + case 'header': traits |= 4; break; + case 'search': traits |= 8; break; + case 'image': traits |= 16; break; + case 'selected': traits |= 32; break; + case 'plays': traits |= 64; break; + case 'key': traits |= 128; break; + case 'text': traits |= 256; break; + case 'summary': traits |= 512; break; + case 'disabled': traits |= 1024; break; + case 'frequentUpdates': traits |= 2048; break; + case 'startsMedia': traits |= 4096; break; + case 'adjustable': traits |= 8192; break; + case 'allowsDirectInteraction': traits |= 16384; break; + case 'pageTurn': traits |= 32768; break; + default: throw new Error(`Unknown trait '${value[i]}', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios`); + } + } + + return traits; +} + + + +class GREYMatchers { + static detoxMatcherForText(text) { + if (typeof text !== "string") throw new Error("text should be a string, but got " + (text + (" (" + (typeof text + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForText:", + args: [{ + type: "NSString", + value: text + }] + }; + } + + static detox_matcherForAccessibilityLabel(label) { + if (typeof label !== "string") throw new Error("label should be a string, but got " + (label + (" (" + (typeof label + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detox_matcherForAccessibilityLabel:", + args: [{ + type: "NSString", + value: label + }] + }; + } + + static detoxMatcherForScrollChildOfMatcher(matcher) { + if (typeof matcher !== "object" || matcher.type !== "Invocation" || typeof matcher.value !== "object" || typeof matcher.value.target !== "object" || matcher.value.target.value !== "GREYMatchers") { + throw new Error('matcher should be a GREYMatcher, but got ' + JSON.stringify(matcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForScrollChildOfMatcher:", + args: [matcher] + }; + } + + static detoxMatcherAvoidingProblematicReactNativeElements(matcher) { + if (typeof matcher !== "object" || matcher.type !== "Invocation" || typeof matcher.value !== "object" || typeof matcher.value.target !== "object" || matcher.value.target.value !== "GREYMatchers") { + throw new Error('matcher should be a GREYMatcher, but got ' + JSON.stringify(matcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherAvoidingProblematicReactNativeElements:", + args: [matcher] + }; + } + + static detoxMatcherForBothAnd(firstMatcher, secondMatcher) { + if (typeof firstMatcher !== "object" || firstMatcher.type !== "Invocation" || typeof firstMatcher.value !== "object" || typeof firstMatcher.value.target !== "object" || firstMatcher.value.target.value !== "GREYMatchers") { + throw new Error('firstMatcher should be a GREYMatcher, but got ' + JSON.stringify(firstMatcher)); + } + + if (typeof secondMatcher !== "object" || secondMatcher.type !== "Invocation" || typeof secondMatcher.value !== "object" || typeof secondMatcher.value.target !== "object" || secondMatcher.value.target.value !== "GREYMatchers") { + throw new Error('secondMatcher should be a GREYMatcher, but got ' + JSON.stringify(secondMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForBoth:and:", + args: [firstMatcher, secondMatcher] + }; + } + + static detoxMatcherForBothAndAncestorMatcher(firstMatcher, ancestorMatcher) { + if (typeof firstMatcher !== "object" || firstMatcher.type !== "Invocation" || typeof firstMatcher.value !== "object" || typeof firstMatcher.value.target !== "object" || firstMatcher.value.target.value !== "GREYMatchers") { + throw new Error('firstMatcher should be a GREYMatcher, but got ' + JSON.stringify(firstMatcher)); + } + + if (typeof ancestorMatcher !== "object" || ancestorMatcher.type !== "Invocation" || typeof ancestorMatcher.value !== "object" || typeof ancestorMatcher.value.target !== "object" || ancestorMatcher.value.target.value !== "GREYMatchers") { + throw new Error('ancestorMatcher should be a GREYMatcher, but got ' + JSON.stringify(ancestorMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForBoth:andAncestorMatcher:", + args: [firstMatcher, ancestorMatcher] + }; + } + + static detoxMatcherForBothAndDescendantMatcher(firstMatcher, descendantMatcher) { + if (typeof firstMatcher !== "object" || firstMatcher.type !== "Invocation" || typeof firstMatcher.value !== "object" || typeof firstMatcher.value.target !== "object" || firstMatcher.value.target.value !== "GREYMatchers") { + throw new Error('firstMatcher should be a GREYMatcher, but got ' + JSON.stringify(firstMatcher)); + } + + if (typeof descendantMatcher !== "object" || descendantMatcher.type !== "Invocation" || typeof descendantMatcher.value !== "object" || typeof descendantMatcher.value.target !== "object" || descendantMatcher.value.target.value !== "GREYMatchers") { + throw new Error('descendantMatcher should be a GREYMatcher, but got ' + JSON.stringify(descendantMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForBoth:andDescendantMatcher:", + args: [firstMatcher, descendantMatcher] + }; + } + + static detoxMatcherForNot(matcher) { + if (typeof matcher !== "object" || matcher.type !== "Invocation" || typeof matcher.value !== "object" || typeof matcher.value.target !== "object" || matcher.value.target.value !== "GREYMatchers") { + throw new Error('matcher should be a GREYMatcher, but got ' + JSON.stringify(matcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForNot:", + args: [matcher] + }; + } + + static detoxMatcherForClass(aClassName) { + if (typeof aClassName !== "string") throw new Error("aClassName should be a string, but got " + (aClassName + (" (" + (typeof aClassName + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "detoxMatcherForClass:", + args: [{ + type: "NSString", + value: aClassName + }] + }; + } + +} + +module.exports = GREYMatchers; \ No newline at end of file diff --git a/detox/src/ios/earlgreyapi/GREYMatchers.js b/detox/src/ios/earlgreyapi/GREYMatchers.js new file mode 100644 index 0000000000..2b8ab723e5 --- /dev/null +++ b/detox/src/ios/earlgreyapi/GREYMatchers.js @@ -0,0 +1,628 @@ +/** + + This code is generated. + For more information see generation/README.md. +*/ + + +// Globally declared helpers + +function sanitize_greyDirection(action) { + switch (action) { + case "left": + return 1; + case "right": + return 2; + case "up": + return 3; + case "down": + return 4; + + default: + throw new Error(`GREYAction.GREYDirection must be a 'left'/'right'/'up'/'down', got ${action}`); + } +} + +function sanitize_greyContentEdge(action) { + switch (action) { + case "left": + return 0; + case "right": + return 1; + case "top": + return 2; + case "bottom": + return 3; + + default: + throw new Error(`GREYAction.GREYContentEdge must be a 'left'/'right'/'top'/'bottom', got ${action}`); + } +} + +function sanitize_uiAccessibilityTraits(value) { + let traits = 0; + for (let i = 0; i < value.length; i++) { + switch (value[i]) { + case 'button': traits |= 1; break; + case 'link': traits |= 2; break; + case 'header': traits |= 4; break; + case 'search': traits |= 8; break; + case 'image': traits |= 16; break; + case 'selected': traits |= 32; break; + case 'plays': traits |= 64; break; + case 'key': traits |= 128; break; + case 'text': traits |= 256; break; + case 'summary': traits |= 512; break; + case 'disabled': traits |= 1024; break; + case 'frequentUpdates': traits |= 2048; break; + case 'startsMedia': traits |= 4096; break; + case 'adjustable': traits |= 8192; break; + case 'allowsDirectInteraction': traits |= 16384; break; + case 'pageTurn': traits |= 32768; break; + default: throw new Error(`Unknown trait '${value[i]}', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios`); + } + } + + return traits; +} + + + +class GREYMatchers { + /*Matcher for application's key window. + +@return A matcher for the application's key window. +*/static matcherForKeyWindow() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForKeyWindow", + args: [] + }; + } + + /*Matcher for UI element with the provided accessibility @c label. + +@param label The accessibility label to be matched. + +@return A matcher for the accessibility label of an accessible element. +*/static matcherForAccessibilityLabel(label) { + if (typeof label !== "string") throw new Error("label should be a string, but got " + (label + (" (" + (typeof label + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityLabel:", + args: [{ + type: "NSString", + value: label + }] + }; + } + + /*Matcher for UI element with the provided accessibility ID @c accessibilityID. + +@param accessibilityID The accessibility ID to be matched. + +@return A matcher for the accessibility ID of an accessible element. +*/static matcherForAccessibilityID(accessibilityID) { + if (typeof accessibilityID !== "string") throw new Error("accessibilityID should be a string, but got " + (accessibilityID + (" (" + (typeof accessibilityID + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityID:", + args: [{ + type: "NSString", + value: accessibilityID + }] + }; + } + + /*Matcher for UI element with the provided accessibility @c value. + +@param value The accessibility value to be matched. + +@return A matcher for the accessibility value of an accessible element. +*/static matcherForAccessibilityValue(value) { + if (typeof value !== "string") throw new Error("value should be a string, but got " + (value + (" (" + (typeof value + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityValue:", + args: [{ + type: "NSString", + value: value + }] + }; + } + + /*Matcher for UI element with the provided accessibility @c traits. + +@param traits The accessibility traits to be matched. + +@return A matcher for the accessibility traits of an accessible element. + +*/static matcherForAccessibilityTraits(traits) { + if (typeof traits !== 'object' || !traits instanceof Array) { + throw new Error('TraitsMatcher ctor argument must be an array, got ' + typeof traits); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityTraits:", + args: [{ + type: "NSInteger", + value: sanitize_uiAccessibilityTraits(traits) + }] + }; + } + + /*Matcher for UI element with the provided accessiblity @c hint. + +@param hint The accessibility hint to be matched. + +@return A matcher for the accessibility hint of an accessible element. +*/static matcherForAccessibilityHint(hint) { + if (typeof hint !== "string") throw new Error("hint should be a string, but got " + (hint + (" (" + (typeof hint + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityHint:", + args: [{ + type: "NSString", + value: hint + }] + }; + } + + /*Matcher for UI element with accessiblity focus. + +@return A matcher for the accessibility focus of an accessible element. +*/static matcherForAccessibilityElementIsFocused() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityElementIsFocused", + args: [] + }; + } + + /*Matcher for UI elements of type UIButton, UILabel, UITextField or UITextView displaying the +provided @c inputText. + +@param text The text to be matched in the UI elements. + +@return A matcher to check for any UI elements with a text field containing the given text. +*/static matcherForText(text) { + if (typeof text !== "string") throw new Error("text should be a string, but got " + (text + (" (" + (typeof text + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForText:", + args: [{ + type: "NSString", + value: text + }] + }; + } + + /*Matcher for element that is the first responder. + +@return A matcher that verifies if a UI element is the first responder. +*/static matcherForFirstResponder() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForFirstResponder", + args: [] + }; + } + + /*Matcher to check if system alert view is shown. + +@return A matcher to check if a system alert view is being shown. +*/static matcherForSystemAlertViewShown() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForSystemAlertViewShown", + args: [] + }; + } + + /*Matcher for UI element whose percent visible area (of its accessibility frame) exceeds the +given @c percent. + +@param percent The percent visible area that the UI element being matched has to be visible. +Allowed values for @c percent are [0,1] inclusive. + +@return A matcher that checks if a UI element has a visible area at least equal +to a minimum value. +*/static matcherForMinimumVisiblePercent(percent) { + if (typeof percent !== "number") throw new Error("percent should be a number, but got " + (percent + (" (" + (typeof percent + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForMinimumVisiblePercent:", + args: [{ + type: "CGFloat", + value: percent + }] + }; + } + + /*Matcher for UI element that is sufficiently visible to the user. EarlGrey considers elements +with visible area percentage greater than @c kElementSufficientlyVisiblePercentage (0.75) +to be sufficiently visible. + +@return A matcher intialized with a visibility percentage that confirms an element is +sufficiently visible. +*/static matcherForSufficientlyVisible() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForSufficientlyVisible", + args: [] + }; + } + + /*Matcher for UI element that are not visible to the user i.e. has a zero visible area. + +@return A matcher for verifying if an element is not visible. +*/static matcherForNotVisible() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForNotVisible", + args: [] + }; + } + + /*Matcher for UI element that matches EarlGrey's criteria for user interaction currently it must +satisfy at least the following criteria: +
    +
  • At least a few pixels of the element's UI are visible.
  • +
  • The element's accessibility activation point OR the center of the element's visible area +is visible.
  • +
+ +@return A matcher that checks if a UI element is interactable. +*/static matcherForInteractable() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForInteractable", + args: [] + }; + } + + /*Matcher to check if a UI element is accessible. + +@return A matcher that checks if a UI element is accessible. +*/static matcherForAccessibilityElement() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAccessibilityElement", + args: [] + }; + } + + /*Matcher for matching UIProgressView's values. Use greaterThan, greaterThanOrEqualTo, +lessThan etc to create @c comparisonMatcher. For example, to match the UIProgressView +elements that have progress value greater than 50.2, use +@code [GREYMatchers matcherForProgress:grey_greaterThan(@(50.2))] @endcode. In case if an +unimplemented matcher is required, please implement it similar to @c grey_closeTo. + +@param comparisonMatcher The matcher with the value to check the progress against. + +@return A matcher for checking a UIProgessView's value. +*/static matcherForProgress(comparisonMatcher) { + if (typeof comparisonMatcher !== "object" || comparisonMatcher.type !== "Invocation" || typeof comparisonMatcher.value !== "object" || typeof comparisonMatcher.value.target !== "object" || comparisonMatcher.value.target.value !== "GREYMatchers") { + throw new Error('comparisonMatcher should be a GREYMatcher, but got ' + JSON.stringify(comparisonMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForProgress:", + args: [comparisonMatcher] + }; + } + + /*Matcher that matches UI element based on the presence of an ancestor in its hierarchy. +The given matcher is used to match decendants. + +@param ancestorMatcher The ancestor UI element whose descendants are to be matched. + +@return A matcher to check if a UI element is the descendant of another. +*/static matcherForAncestor(ancestorMatcher) { + if (typeof ancestorMatcher !== "object" || ancestorMatcher.type !== "Invocation" || typeof ancestorMatcher.value !== "object" || typeof ancestorMatcher.value.target !== "object" || ancestorMatcher.value.target.value !== "GREYMatchers") { + throw new Error('ancestorMatcher should be a GREYMatcher, but got ' + JSON.stringify(ancestorMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAncestor:", + args: [ancestorMatcher] + }; + } + + /*Matcher that matches any UI element with a descendant matching the given matcher. + +@param descendantMatcher A matcher being checked to be a descendant +of the UI element being checked. + +@return A matcher to check if a the specified element is in a descendant of another UI element. +*/static matcherForDescendant(descendantMatcher) { + if (typeof descendantMatcher !== "object" || descendantMatcher.type !== "Invocation" || typeof descendantMatcher.value !== "object" || typeof descendantMatcher.value.target !== "object" || descendantMatcher.value.target.value !== "GREYMatchers") { + throw new Error('descendantMatcher should be a GREYMatcher, but got ' + JSON.stringify(descendantMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForDescendant:", + args: [descendantMatcher] + }; + } + + /*Matcher that matches UIButton that has title label as @c text. + +@param title The title to be checked on the UIButtons being matched. + +@return A matcher to confirm UIButton titles. +*/static matcherForButtonTitle(title) { + if (typeof title !== "string") throw new Error("title should be a string, but got " + (title + (" (" + (typeof title + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForButtonTitle:", + args: [{ + type: "NSString", + value: title + }] + }; + } + + /*Matcher that matches UIScrollView that has contentOffset as @c offset. + +@param offset The content offset to be checked on the UIScrollView being +matched. + +@return A matcher to confirm UIScrollView content offset. +*/static matcherForScrollViewContentOffset(offset) { + if (typeof offset !== "object") throw new Error("offset should be a object, but got " + (offset + (" (" + (typeof offset + ")")))); + if (typeof offset.x !== "number") throw new Error("offset.x should be a number, but got " + (offset.x + (" (" + (typeof offset.x + ")")))); + if (typeof offset.y !== "number") throw new Error("offset.y should be a number, but got " + (offset.y + (" (" + (typeof offset.y + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForScrollViewContentOffset:", + args: [{ + type: "CGPoint", + value: offset + }] + }; + } + + /*Matcher that matches a UISlider's value. + +@param valueMatcher A matcher for the UISlider's value. You must provide a valid +@c valueMatcher for the floating point value comparison. The +@c valueMatcher should be of the type @c closeTo, @c greaterThan, +@c lessThan, @c lessThanOrEqualTo, @c greaterThanOrEqualTo. The +value matchers should account for any loss in precision for the given +floating point value. If you are using @c grey_closeTo, use delta diff as +@c kGREYAcceptableFloatDifference. In case if an unimplemented matcher +is required, please implement it similar to @c grey_closeTo. + +@return A matcher for checking a UISlider's value. +*/static matcherForSliderValueMatcher(valueMatcher) { + if (typeof valueMatcher !== "object" || valueMatcher.type !== "Invocation" || typeof valueMatcher.value !== "object" || typeof valueMatcher.value.target !== "object" || valueMatcher.value.target.value !== "GREYMatchers") { + throw new Error('valueMatcher should be a GREYMatcher, but got ' + JSON.stringify(valueMatcher)); + } + + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForSliderValueMatcher:", + args: [valueMatcher] + }; + } + + /*Matcher that matches UIPickerView that has a column set to @c value. + +@param column The column of the UIPickerView to be matched. +@param value The value that should be set in the column of the UIPickerView. + +@return A matcher to check the value in a particular column of a UIPickerView. +*/static matcherForPickerColumnSetToValue(column, value) { + if (typeof column !== "number") throw new Error("column should be a number, but got " + (column + (" (" + (typeof column + ")")))); + if (typeof value !== "string") throw new Error("value should be a string, but got " + (value + (" (" + (typeof value + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForPickerColumn:setToValue:", + args: [{ + type: "NSInteger", + value: column + }, { + type: "NSString", + value: value + }] + }; + } + + /*Matcher that verifies whether an element, that is a UIControl, is enabled. + +@return A matcher for checking whether a UI element is an enabled UIControl. +*/static matcherForEnabledElement() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForEnabledElement", + args: [] + }; + } + + /*Matcher that verifies whether an element, that is a UIControl, is selected. + +@return A matcher for checking whether a UI element is a selected UIControl. +*/static matcherForSelectedElement() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForSelectedElement", + args: [] + }; + } + + /*Matcher that verifies whether a view has its userInteractionEnabled property set to @c YES. + +@return A matcher for checking whether a view' userInteractionEnabled property is set to @c YES. +*/static matcherForUserInteractionEnabled() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForUserInteractionEnabled", + args: [] + }; + } + + /*Matcher primarily for asserting that the element is @c nil or not found. + +@return A matcher to check if a specified element is @c nil or not found. +*/static matcherForNil() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForNil", + args: [] + }; + } + + /*Matcher for asserting that the element exists in the UI hierarchy (i.e. not @c nil). + +@return A matcher to check if a specified element is not @c nil. +*/static matcherForNotNil() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForNotNil", + args: [] + }; + } + + /*A Matcher that matches against any object, including @c nils. + +@return A matcher that matches any object. +*/static matcherForAnything() { + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForAnything", + args: [] + }; + } + + /*Matcher that matches a UIScrollView scrolled to content @c edge. + +@param edge The content edge UIScrollView should be scrolled to. + +@return A matcher that matches a UIScrollView scrolled to content @c edge. +*/static matcherForScrolledToContentEdge(edge) { + if (!["left", "right", "top", "bottom"].some(option => option === edge)) throw new Error("edge should be one of [left, right, top, bottom], but got " + edge); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForScrolledToContentEdge:", + args: [{ + type: "NSInteger", + value: sanitize_greyContentEdge(edge) + }] + }; + } + + /*Matcher that matches a UITextField's content. + +@param value The text string contained inside the UITextField. + +@return A matcher that matches the value inside a UITextField. +*/static matcherForTextFieldValue(value) { + if (typeof value !== "string") throw new Error("value should be a string, but got " + (value + (" (" + (typeof value + ")")))); + return { + target: { + type: "Class", + value: "GREYMatchers" + }, + method: "matcherForTextFieldValue:", + args: [{ + type: "NSString", + value: value + }] + }; + } + +} + +module.exports = GREYMatchers; \ No newline at end of file diff --git a/detox/src/ios/matchers.js b/detox/src/ios/matchers.js index badfc113f6..aefc203176 100644 --- a/detox/src/ios/matchers.js +++ b/detox/src/ios/matchers.js @@ -1,37 +1,36 @@ const invoke = require('../invoke'); +const GreyMatchers = require('./earlgreyapi/GREYMatchers'); +const GreyMatchersDetox = require('./earlgreyapi/GREYMatchers+Detox'); class Matcher { withAncestor(matcher) { - if (!(matcher instanceof Matcher)) throw new Error(`Matcher withAncestor argument must be a valid Matcher, got ${typeof matcher}`); const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForBoth:andAncestorMatcher:', _originalMatcherCall, matcher._call); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForBothAndAncestorMatcher(_originalMatcherCall, matcher._call)); return this; } withDescendant(matcher) { - if (!(matcher instanceof Matcher)) throw new Error(`Matcher withDescendant argument must be a valid Matcher, got ${typeof matcher}`); const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForBoth:andDescendantMatcher:', _originalMatcherCall, matcher._call); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForBothAndDescendantMatcher(_originalMatcherCall, matcher._call)); return this; } and(matcher) { - if (!(matcher instanceof Matcher)) throw new Error(`Matcher and argument must be a valid Matcher, got ${typeof matcher}`); const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForBoth:and:', _originalMatcherCall, matcher._call); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForBothAnd(_originalMatcherCall, matcher._call)); return this; } not() { const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForNot:', _originalMatcherCall); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForNot(_originalMatcherCall)); return this; } _avoidProblematicReactNativeElements() { const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherAvoidingProblematicReactNativeElements:', _originalMatcherCall); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherAvoidingProblematicReactNativeElements(_originalMatcherCall)); return this; } _extendToDescendantScrollViews() { const _originalMatcherCall = this._call; - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForScrollChildOfMatcher:', _originalMatcherCall); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForScrollChildOfMatcher(_originalMatcherCall)); return this; } } @@ -39,98 +38,70 @@ class Matcher { class LabelMatcher extends Matcher { constructor(value) { super(); - if (typeof value !== 'string') throw new Error(`LabelMatcher ctor argument must be a string, got ${typeof value}`); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detox_matcherForAccessibilityLabel:', value); + this._call = invoke.callDirectly(GreyMatchersDetox.detox_matcherForAccessibilityLabel(value)); } } class IdMatcher extends Matcher { constructor(value) { super(); - if (typeof value !== 'string') throw new Error(`IdMatcher ctor argument must be a string, got ${typeof value}`); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForAccessibilityID:', value); + this._call = invoke.callDirectly(GreyMatchers.matcherForAccessibilityID(value)); } } class TypeMatcher extends Matcher { constructor(value) { super(); - if (typeof value !== 'string') throw new Error(`TypeMatcher ctor argument must be a string, got ${typeof value}`); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForClass:', value); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForClass(value)); } } class TraitsMatcher extends Matcher { constructor(value) { super(); - if ((typeof value !== 'object') || (!value instanceof Array)) throw new Error(`TraitsMatcher ctor argument must be an array, got ${typeof value}`); - let traits = 0; - for (let i = 0; i < value.length; i++) { - switch (value[i]) { - case 'button': traits |= 1; break; - case 'link': traits |= 2; break; - case 'header': traits |= 4; break; - case 'search': traits |= 8; break; - case 'image': traits |= 16; break; - case 'selected': traits |= 32; break; - case 'plays': traits |= 64; break; - case 'key': traits |= 128; break; - case 'text': traits |= 256; break; - case 'summary': traits |= 512; break; - case 'disabled': traits |= 1024; break; - case 'frequentUpdates': traits |= 2048; break; - case 'startsMedia': traits |= 4096; break; - case 'adjustable': traits |= 8192; break; - case 'allowsDirectInteraction': traits |= 16384; break; - case 'pageTurn': traits |= 32768; break; - default: throw new Error(`Unknown trait '${value[i]}', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios`); - } - } - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForAccessibilityTraits:', invoke.IOS.NSInteger(traits)); + this._call = invoke.callDirectly(GreyMatchers.matcherForAccessibilityTraits(value)); } } class VisibleMatcher extends Matcher { constructor() { super(); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForSufficientlyVisible'); + this._call = invoke.callDirectly(GreyMatchers.matcherForSufficientlyVisible()); } } class NotVisibleMatcher extends Matcher { constructor() { super(); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForNotVisible'); + this._call = invoke.callDirectly(GreyMatchers.matcherForNotVisible()); } } class ExistsMatcher extends Matcher { constructor() { super(); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForNotNil'); + this._call = invoke.callDirectly(GreyMatchers.matcherForNotNil()); } } class NotExistsMatcher extends Matcher { constructor() { super(); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForNil'); + this._call = invoke.callDirectly(GreyMatchers.matcherForNil()); } } class TextMatcher extends Matcher { constructor(value) { super(); - if (typeof value !== 'string') throw new Error(`TextMatcher ctor argument must be a string, got ${typeof value}`); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'detoxMatcherForText:', value); + this._call = invoke.callDirectly(GreyMatchersDetox.detoxMatcherForText(value)); } } class ValueMatcher extends Matcher { constructor(value) { super(); - if (typeof value !== 'string') throw new Error(`ValueMatcher ctor argument must be a string, got ${typeof value}`); - this._call = invoke.call(invoke.IOS.Class('GREYMatchers'), 'matcherForAccessibilityValue:', value); + this._call = invoke.callDirectly(GreyMatchers.matcherForAccessibilityValue(value)); } } diff --git a/detox/test/e2e/b-matchers.js b/detox/test/e2e/b-matchers.js index 81aef3fa4d..b169bae3c4 100644 --- a/detox/test/e2e/b-matchers.js +++ b/detox/test/e2e/b-matchers.js @@ -57,6 +57,7 @@ describe('Matchers', () => { await expect(element(by.id('UniqueId345').and(by.text('ID')))).toExist(); await expect(element(by.id('UniqueId345').and(by.text('RandomJunk')))).toNotExist(); await expect(element(by.id('UniqueId345').and(by.label('RandomJunk')))).toNotExist(); + await expect(element(by.id('UniqueId345').and(by.traits(['button'])))).toNotExist(); }); // waiting to upgrade EarlGrey version in order to test this (not supported in our current one) diff --git a/generation/__tests__/__snapshots__/earl-grey.js.snap b/generation/__tests__/__snapshots__/earl-grey.js.snap index a44881bf56..b5f323fff1 100644 --- a/generation/__tests__/__snapshots__/earl-grey.js.snap +++ b/generation/__tests__/__snapshots__/earl-grey.js.snap @@ -92,3 +92,9 @@ Object { }, } `; + +exports[`earl-grey generation special case: id should fail with wrongly formatted matchers 1`] = `"firstMatcher should be a GREYMatcher, but got {\\"type\\":\\"Invocation\\",\\"value\\":{\\"target\\":{\\"type\\":\\"Class\\",\\"value\\":\\"GREYAction\\"},\\"method\\":\\"matcherForAccessibilityID:\\",\\"args\\":[\\"Grandfather883\\"]}}"`; + +exports[`earl-grey generation special case: id should fail with wrongly formatted matchers 2`] = `"ancestorMatcher should be a GREYMatcher, but got {\\"type\\":\\"Invocation\\",\\"value\\":{\\"target\\":{\\"type\\":\\"Class\\",\\"value\\":\\"GREYAction\\"},\\"method\\":\\"matcherForAccessibilityID:\\",\\"args\\":[\\"Grandson883\\"]}}"`; + +exports[`earl-grey generation special case: id should fail with wrongly formatted matchers 3`] = `"firstMatcher should be a GREYMatcher, but got {\\"type\\":\\"Invocation\\",\\"value\\":{\\"method\\":\\"matcherForAccessibilityID:\\",\\"args\\":[\\"Grandfather883\\"]}}"`; diff --git a/generation/__tests__/__snapshots__/global-functions.js.snap b/generation/__tests__/__snapshots__/global-functions.js.snap index b34c8b5203..2c8fd64d54 100644 --- a/generation/__tests__/__snapshots__/global-functions.js.snap +++ b/generation/__tests__/__snapshots__/global-functions.js.snap @@ -3,3 +3,5 @@ exports[`globals sanitize_greyContentEdge should fail with unknown value 1`] = `"GREYAction.GREYContentEdge must be a 'left'/'right'/'top'/'bottom', got kittens"`; exports[`globals sanitize_greyDirection should fail with unknown value 1`] = `"GREYAction.GREYDirection must be a 'left'/'right'/'up'/'down', got kittens"`; + +exports[`globals sanitize_uiAccessibilityTraits should throw if unknown trait is accessed 1`] = `"Unknown trait 'unknown', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios"`; diff --git a/generation/__tests__/__snapshots__/helpers.js.snap b/generation/__tests__/__snapshots__/helpers.js.snap new file mode 100644 index 0000000000..41542647ab --- /dev/null +++ b/generation/__tests__/__snapshots__/helpers.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`helpers methodNameToSnakeCase should return the correct snake case method name 1`] = `"actionForScrollInDirectionAmountXOriginStartPercentageYOriginStartPercentage"`; diff --git a/generation/__tests__/earl-grey.js b/generation/__tests__/earl-grey.js index 7428516cd0..50f11fdbfe 100644 --- a/generation/__tests__/earl-grey.js +++ b/generation/__tests__/earl-grey.js @@ -166,6 +166,162 @@ describe("earl-grey generation", () => { }); }); + describe("special case: id", () => { + it("should not wrap the args in type id, but pass them in as they are", () => { + const ancestorMatcher = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandson883" + ] + } + }; + const currentMatcher = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandfather883" + ] + } + }; + const result = ExampleClass.detoxMatcherForBothAndAncestorMatcher(currentMatcher, ancestorMatcher); + + expect(result).toEqual({ + "target": { + "type": "Class", + "value": "GREYActions" + }, + "method": "detoxMatcherForBoth:andAncestorMatcher:", + "args": [ + { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandfather883" + ] + } + }, + { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandson883" + ] + } + } + ] + }); + }); + + it("should fail with wrongly formatted matchers", () => { + expect(() => { + const ancestorMatcher = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandson883" + ] + } + }; + const currentAction = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYAction" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandfather883" + ] + } + }; + ExampleClass.detoxMatcherForBothAndAncestorMatcher(currentAction, ancestorMatcher); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + const ancestorAction = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYAction" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandson883" + ] + } + }; + const currentMatcher = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYMatchers" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandfather883" + ] + } + }; + ExampleClass.detoxMatcherForBothAndAncestorMatcher(currentMatcher, ancestorAction); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + const ancestorMatcher = { + "type": "Invocation", + "value": { + "target": { + "type": "Class", + "value": "GREYAction" + }, + "method": "matcherForAccessibilityID:", + "args": [ + "Grandson883" + ] + } + }; + const currentMatcher = { + "type": "Invocation", + "value": { + "method": "matcherForAccessibilityID:", + "args": [ + "Grandfather883" + ] + } + }; + ExampleClass.detoxMatcherForBothAndAncestorMatcher(currentMatcher, ancestorMatcher); + }).toThrowErrorMatchingSnapshot(); + }); + }); + afterAll(() => { // Clean up remove.removeSync("./__tests__/generated"); diff --git a/generation/__tests__/global-functions.js b/generation/__tests__/global-functions.js index 34dd3900d2..71e49527f0 100644 --- a/generation/__tests__/global-functions.js +++ b/generation/__tests__/global-functions.js @@ -30,4 +30,15 @@ describe("globals", () => { }).toThrowErrorMatchingSnapshot(); }); }); + + describe("sanitize_uiAccessibilityTraits", () => { + it("should return numbers for traits", () => { + expect(globals.sanitize_uiAccessibilityTraits(["button", "link"])).toBe(3); + expect(globals.sanitize_uiAccessibilityTraits(["summary", "allowsDirectInteraction"])).toBe(16896); + }); + + it("should throw if unknown trait is accessed", () => { + expect(() => globals.sanitize_uiAccessibilityTraits(["unknown"])).toThrowErrorMatchingSnapshot(); + }); + }); }); \ No newline at end of file diff --git a/generation/__tests__/helpers.js b/generation/__tests__/helpers.js new file mode 100644 index 0000000000..04e5757829 --- /dev/null +++ b/generation/__tests__/helpers.js @@ -0,0 +1,13 @@ +const { methodNameToSnakeCase } = require('../helpers'); + +describe("helpers", () => { + describe("methodNameToSnakeCase", () => { + it("should not fail with empty string", () => { + expect(() => methodNameToSnakeCase("")).not.toThrow(); + }); + + it("should return the correct snake case method name", () => { + expect(methodNameToSnakeCase("actionForScrollInDirection:amount:xOriginStartPercentage:yOriginStartPercentage:")).toMatchSnapshot(); + }); + }); +}); diff --git a/generation/earl-grey/global-functions.js b/generation/earl-grey/global-functions.js index 5720d3ec1f..44f6360013 100644 --- a/generation/earl-grey/global-functions.js +++ b/generation/earl-grey/global-functions.js @@ -32,8 +32,36 @@ function sanitize_greyContentEdge(action) { } } +function sanitize_uiAccessibilityTraits(value) { + let traits = 0; + for (let i = 0; i < value.length; i++) { + switch (value[i]) { + case 'button': traits |= 1; break; + case 'link': traits |= 2; break; + case 'header': traits |= 4; break; + case 'search': traits |= 8; break; + case 'image': traits |= 16; break; + case 'selected': traits |= 32; break; + case 'plays': traits |= 64; break; + case 'key': traits |= 128; break; + case 'text': traits |= 256; break; + case 'summary': traits |= 512; break; + case 'disabled': traits |= 1024; break; + case 'frequentUpdates': traits |= 2048; break; + case 'startsMedia': traits |= 4096; break; + case 'adjustable': traits |= 8192; break; + case 'allowsDirectInteraction': traits |= 16384; break; + case 'pageTurn': traits |= 32768; break; + default: throw new Error(`Unknown trait '${value[i]}', see list in https://facebook.github.io/react-native/docs/accessibility.html#accessibilitytraits-ios`); + } + } + + return traits; +} + module.exports = { sanitize_greyDirection, sanitize_greyContentEdge, + sanitize_uiAccessibilityTraits, }; \ No newline at end of file diff --git a/generation/earl-grey/index.js b/generation/earl-grey/index.js index 5d31be4cfe..8c55c71296 100644 --- a/generation/earl-grey/index.js +++ b/generation/earl-grey/index.js @@ -1,4 +1,5 @@ const t = require("babel-types"); +const template = require("babel-template"); const objectiveCParser = require("objective-c-parser"); const generate = require("babel-generator").default; const fs = require("fs"); @@ -19,6 +20,30 @@ const isPoint = [ generateTypeCheck("number", { selector: "y" }) ]; const isOneOf = generateIsOneOfCheck; +const isGreyMatcher = ({ name }) => template(` + if ( + typeof ARG !== "object" || + ARG.type !== "Invocation" || + typeof ARG.value !== "object" || + typeof ARG.value.target !== "object" || + ARG.value.target.value !== "GREYMatchers" + ) { + throw new Error('${name} should be a GREYMatcher, but got ' + JSON.stringify(ARG)); + } +`)({ + ARG: t.identifier(name) + }); +const isArray = ({ name }) => template(` +if ( + (typeof ARG !== 'object') || + (!ARG instanceof Array) +) { + throw new Error('TraitsMatcher ctor argument must be an array, got ' + typeof ARG); + } +`)({ + ARG: t.identifier(name) + }); + // Constants const SUPPORTED_TYPES = [ @@ -30,6 +55,8 @@ const SUPPORTED_TYPES = [ "NSString *", "NSString", "NSUInteger", + "id", + "UIAccessibilityTraits" ]; /** @@ -155,6 +182,10 @@ const supportedContentSanitizersMap = { GREYContentEdge: { type: "NSInteger", value: callGlobal("sanitize_greyContentEdge") + }, + UIAccessibilityTraits: { + type: "NSInteger", + value: callGlobal("sanitize_uiAccessibilityTraits") } }; function addArgumentContentSanitizerCall(json) { @@ -172,8 +203,13 @@ function addArgumentTypeSanitizer(json) { return json.type; } +// These types need no wrapping with {type: ..., value: } +const plainArgumentTypes = ["id"]; +function shouldBeWrapped({ type }) { + return !plainArgumentTypes.includes(type); +} function createReturnStatement(className, json) { - const args = json.args.map(arg => + const args = json.args.map(arg => shouldBeWrapped(arg) ? t.objectExpression([ t.objectProperty( t.identifier("type"), @@ -183,7 +219,7 @@ function createReturnStatement(className, json) { t.identifier("value"), addArgumentContentSanitizerCall(arg) ) - ]) + ]) : addArgumentContentSanitizerCall(arg) ); return t.returnStatement( @@ -214,17 +250,14 @@ function createTypeCheck(json) { "NSDate *": isNumber, GREYDirection: isOneOf(["left", "right", "up", "down"]), GREYContentEdge: isOneOf(["left", "right", "top", "bottom"]), - GREYPinchDirection: isOneOf(["outward", "inward"]) + GREYPinchDirection: isOneOf(["outward", "inward"]), + "id": isGreyMatcher, + UIAccessibilityTraits: isArray, }; const typeCheckCreator = typeInterfaces[json.type]; const isListOfChecks = typeCheckCreator instanceof Array; - if (typeof typeCheckCreator !== "function" && !isListOfChecks) { - console.info("Could not find ", json); - return; - } - return isListOfChecks ? typeCheckCreator.map(singleCheck => singleCheck(json)) : typeCheckCreator(json); @@ -248,10 +281,13 @@ module.exports = function(files) { fs.writeFileSync(outputFile, code, "utf8"); // Output methods that were not created due to missing argument support - console.log(`Could not generate the following methods for ${json.name}`); - const unsupportedMethods = json.methods.filter(x => !filterMethodsWithUnsupportedParams(x)).forEach(method => { - const methodArgs = method.args.filter(methodArg => !SUPPORTED_TYPES.includes(methodArg.type)).map(methodArg => methodArg.type); - console.log(`\t ${method.name} misses ${methodArgs}`); - }); + const unsupportedMethods = json.methods.filter(x => !filterMethodsWithUnsupportedParams(x)); + if (unsupportedMethods.length) { + console.log(`Could not generate the following methods for ${json.name}`); + unsupportedMethods.forEach(method => { + const methodArgs = method.args.filter(methodArg => !SUPPORTED_TYPES.includes(methodArg.type)).map(methodArg => methodArg.type); + console.log(`\t ${method.name} misses ${methodArgs}`); + }); + } }); }; diff --git a/generation/fixtures/example.h b/generation/fixtures/example.h index 80b007678b..1ea0b904da 100644 --- a/generation/fixtures/example.h +++ b/generation/fixtures/example.h @@ -60,4 +60,5 @@ + (id)actionForScrollToContentEdge:(GREYContentEdge)edge; + (id)actionWithUnknownType:(WTFType *)wat; -+ (id)actionWithKnown:(NSUInteger)iknowdis andUnknownType:(WTFTypalike *)wat; \ No newline at end of file ++ (id)actionWithKnown:(NSUInteger)iknowdis andUnknownType:(WTFTypalike *)wat; ++ (id)detoxMatcherForBoth:(id)firstMatcher andAncestorMatcher:(id)ancestorMatcher; \ No newline at end of file diff --git a/generation/index.js b/generation/index.js index 404627dd50..16c8c46a61 100755 --- a/generation/index.js +++ b/generation/index.js @@ -1,7 +1,9 @@ #!/usr/bin/env node const generateEarlGreyAdapters = require("./earl-grey"); const files = { - "../detox/ios/EarlGrey/EarlGrey/Action/GREYActions.h": "../detox/src/ios/earlgreyapi/GREYActions.js" + "../detox/ios/EarlGrey/EarlGrey/Action/GREYActions.h": "../detox/src/ios/earlgreyapi/GREYActions.js", + "../detox/ios/Detox/GREYMatchers+Detox.h": "../detox/src/ios/earlgreyapi/GREYMatchers+Detox.js", + "../detox/ios/EarlGrey/EarlGrey/Matcher/GREYMatchers.h": "../detox/src/ios/earlgreyapi/GREYMatchers.js", }; generateEarlGreyAdapters(files); diff --git a/generation/package.json b/generation/package.json index 89b3dc777b..465365c106 100644 --- a/generation/package.json +++ b/generation/package.json @@ -15,7 +15,7 @@ "babel-types": "^6.25.0", "jest": "^20.0.4", "lerna": "2.0.0-rc.4", - "objective-c-parser": "1.0.4", + "objective-c-parser": "1.1.0", "remove": "^0.1.5" }, "jest": { @@ -34,6 +34,7 @@ } }, "dependencies": { - "babel-generate-guard-clauses": "^0.1.0" + "babel-generate-guard-clauses": "^2.0.0", + "babel-template": "^6.26.0" } -} +} \ No newline at end of file