Skip to content

Commit

Permalink
refactor(android): pass matcher to perform() method.
Browse files Browse the repository at this point in the history
Instead of passing `ViewInteraction`.
  • Loading branch information
asafkorem committed Sep 11, 2023
1 parent 335f133 commit 64880f1
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
public class EspressoDetox {
private static final String LOG_TAG = "detox";

public static Object perform(ViewInteraction interaction, ViewAction action) {
public static Object perform(Matcher<View> matcher, ViewAction action) {
ViewInteraction interaction = onView(matcher);
interaction.perform(action);

if (action instanceof ViewActionWithResult) {
Expand Down
54 changes: 26 additions & 28 deletions detox/src/android/core/NativeElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,37 @@ class NativeElement {
constructor(invocationManager, emitter, matcher) {
this._invocationManager = invocationManager;
this._emitter = emitter;
this._originalMatcher = matcher;
this._selectElementWithMatcher(this._originalMatcher);
this._matcher = matcher;
}

_selectElementWithMatcher(matcher) {
this._call = invoke.call(invoke.Espresso, 'onView', matcher._call);
get _call() {
return invoke.call(invoke.Espresso, 'onView', this._matcher._call);
}

atIndex(index) {
if (typeof index !== 'number') throw new DetoxRuntimeError({ message: `Element atIndex argument must be a number, got ${typeof index}` });
const matcher = this._originalMatcher;
this._originalMatcher._call = invoke.callDirectly(DetoxMatcherApi.matcherForAtIndex(index, matcher._call.value));
const matcher = this._matcher;
this._matcher._call = invoke.callDirectly(DetoxMatcherApi.matcherForAtIndex(index, matcher._call.value));

this._selectElementWithMatcher(this._originalMatcher);
return this;
}

async tap(value) {
const action = new actions.TapAction(value);
const traceDescription = actionDescription.tapAtPoint(value);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async tapAtPoint(value) {
const action = new actions.TapAtPointAction(value);
const traceDescription = actionDescription.tapAtPoint(value);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async longPress() {
const action = new actions.LongPressAction();
const traceDescription = actionDescription.longPress();
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async multiTap(times) {
Expand All @@ -56,61 +54,61 @@ class NativeElement {

const action = new actions.MultiClickAction(times);
const traceDescription = actionDescription.multiTap(times);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async tapBackspaceKey() {
const action = new actions.PressKeyAction(67);
const traceDescription = actionDescription.tapBackspaceKey();
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async tapReturnKey() {
const action = new actions.TypeTextAction('\n');
const traceDescription = actionDescription.tapReturnKey();
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async typeText(value) {
const action = new actions.TypeTextAction(value);
const traceDescription = actionDescription.typeText(value);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async replaceText(value) {
const action = new actions.ReplaceTextAction(value);
const traceDescription = actionDescription.replaceText(value);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async clearText() {
const action = new actions.ClearTextAction();
const traceDescription = actionDescription.clearText();
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async scroll(amount, direction = 'down', startPositionX, startPositionY) {
const action = new actions.ScrollAmountAction(direction, amount, startPositionX, startPositionY);
const traceDescription = actionDescription.scroll(amount, direction, startPositionX, startPositionY);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async scrollTo(edge) {
// override the user's element selection with an extended matcher that looks for UIScrollView children
this._selectElementWithMatcher(this._originalMatcher._extendToDescendantScrollViews());
this._matcher = this._matcher._extendToDescendantScrollViews();

const action = new actions.ScrollEdgeAction(edge);
const traceDescription = actionDescription.scrollTo(edge);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async scrollToIndex(index) {
// override the user's element selection with an extended matcher that looks for UIScrollView children
this._selectElementWithMatcher(this._originalMatcher._extendToDescendantScrollViews());
this._matcher = this._matcher._extendToDescendantScrollViews();

const action = new actions.ScrollToIndex(index);
const traceDescription = actionDescription.scrollToIndex(index);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async setDatePickerDate(rawDateString, formatString) {
Expand All @@ -120,7 +118,7 @@ class NativeElement {

const action = new actions.SetDatePickerDateAction(dateString, formatString);
const traceDescription = actionDescription.setDatePickerDate(dateString, formatString);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

/**
Expand All @@ -134,18 +132,18 @@ class NativeElement {
normalizedSwipeOffset = Number.isNaN(normalizedSwipeOffset) ? 0.75 : normalizedSwipeOffset;

// override the user's element selection with an extended matcher that avoids RN issues with RCTScrollView
this._selectElementWithMatcher(this._originalMatcher._avoidProblematicReactNativeElements());
this._matcher = this._matcher._avoidProblematicReactNativeElements();

const action = new actions.SwipeAction(direction, speed, normalizedSwipeOffset, normalizedStartingPointX, normalizedStartingPointY);
const traceDescription = actionDescription.swipe(direction, speed, normalizedSwipeOffset, normalizedStartingPointX, normalizedStartingPointY);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async takeScreenshot(screenshotName) {
// TODO this should be moved to a lower-layer handler of this use-case
const action = new actions.TakeElementScreenshot();
const traceDescription = actionDescription.takeScreenshot(screenshotName);
const resultBase64 = await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
const resultBase64 = await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
const filePath = tempfile('detox.element-screenshot.png');
await fs.writeFile(filePath, resultBase64, 'base64');

Expand All @@ -160,19 +158,19 @@ class NativeElement {
async getAttributes() {
const action = new actions.GetAttributes();
const traceDescription = actionDescription.getAttributes();
const result = await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
const result = await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
return JSON.parse(result);
}

async adjustSliderToPosition(newPosition) {
const action = new actions.AdjustSliderToPosition(newPosition);
const traceDescription = actionDescription.adjustSliderToPosition(newPosition);
return await new ActionInteraction(this._invocationManager, this, action, traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
}

async performAccessibilityAction(actionName) {
const traceDescription = actionDescription.performAccessibilityAction(actionName);
return await new ActionInteraction(this._invocationManager, this, new actions.AccessibilityActionAction(actionName), traceDescription).execute();
return await new ActionInteraction(this._invocationManager, this._matcher, new actions.AccessibilityActionAction(actionName), traceDescription).execute();
}
}

Expand Down
11 changes: 9 additions & 2 deletions detox/src/android/espressoapi/EspressoDetox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@
*/


function sanitize_matcher(matcher) {
if (!matcher._call) {
return matcher;
}

const originalMatcher = typeof matcher._call === 'function' ? matcher._call() : matcher._call;
return originalMatcher.type ? originalMatcher.value : originalMatcher;
}
class EspressoDetox {
static perform(interaction, action) {
static perform(matcher, action) {
return {
target: {
type: "Class",
Expand All @@ -16,7 +23,7 @@ class EspressoDetox {
method: "perform",
args: [{
type: "Invocation",
value: interaction
value: sanitize_matcher(matcher)
}, action]
};
}
Expand Down
5 changes: 2 additions & 3 deletions detox/src/android/interactions/native.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class Interaction {
}

class ActionInteraction extends Interaction {
constructor(invocationManager, element, action, traceDescription) {
constructor(invocationManager, matcher, action, traceDescription) {
super(invocationManager, traceDescription);
this._call = EspressoDetoxApi.perform(call(element._call), action._call);
this._call = EspressoDetoxApi.perform(matcher, action._call);
// TODO: move this.execute() here from the caller
}
}
Expand All @@ -48,7 +48,6 @@ class WaitForInteraction extends Interaction {
super(invocationManager, expectTraceDescription);
this._element = element;
this._assertionMatcher = assertionMatcher;
this._element._selectElementWithMatcher(this._element._originalMatcher);
}

async withTimeout(timeout) {
Expand Down

0 comments on commit 64880f1

Please sign in to comment.