Skip to content

Commit

Permalink
generation: add support for Matcher ids
Browse files Browse the repository at this point in the history
This allows us to transform the rest of the matchers and it is a
potential solution for other transformations regarding ids as well
  • Loading branch information
DanielMSchmidt committed Oct 4, 2017
1 parent 743b359 commit 0d7abb9
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 15 deletions.
102 changes: 102 additions & 0 deletions detox/src/ios/earlgreyapi/GREYMatchers+Detox.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,108 @@ class GREYMatchers {
};
}

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 {
Expand Down
93 changes: 93 additions & 0 deletions detox/src/ios/earlgreyapi/GREYMatchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,72 @@ is visible.</li>
};
}

/*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.
Expand Down Expand Up @@ -330,6 +396,33 @@ matched.
};
}

/*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.
Expand Down
15 changes: 6 additions & 9 deletions detox/src/ios/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,33 @@ 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;
}
}
Expand Down
6 changes: 6 additions & 0 deletions generation/__tests__/__snapshots__/earl-grey.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,9 @@ Object {
},
}
`;

exports[`earl-grey generation special case: id<GREYMatcher> 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<GREYMatcher> 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<GREYMatcher> should fail with wrongly formatted matchers 3`] = `"firstMatcher should be a GREYMatcher, but got {\\"type\\":\\"Invocation\\",\\"value\\":{\\"method\\":\\"matcherForAccessibilityID:\\",\\"args\\":[\\"Grandfather883\\"]}}"`;
Loading

0 comments on commit 0d7abb9

Please sign in to comment.