-
-
Notifications
You must be signed in to change notification settings - Fork 764
Mykola mokhnach's actions params: The addition to the #756 #760
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
Mykola mokhnach's actions params: The addition to the #756 #760
Conversation
…ava-client into mykola-mokhnach-actions_params
- the draft of architectural improvements
- reversion of accidentally changed strings/javadocs. - javadocs were added to new-designed static methods - ability to take x&y offsets by RelativeOffsetOption
- old integration tests were updated
@mykola-mokhnach @SrinivasanTarget |
* @return this TouchAction, for chaining. | ||
*/ | ||
public TouchAction moveTo(WebElement el) { | ||
ActionParameter action = new ActionParameter("moveTo", (HasIdentity) el); | ||
public T moveTo(AbsoluteOffsetOption moveToOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moveTo does not support absolute offset.
return this; | ||
@Deprecated | ||
public T moveTo(int x, int y) { | ||
return moveTo(useAbsolute(x, y)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Override | ||
protected void verify() { | ||
ofNullable(absoluteOffset).orElseThrow(() -> new IllegalArgumentException( | ||
"Absolute offset must not be defined")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why must not?
|
||
protected void verify() { | ||
ofNullable(offsetOption).orElseThrow(() -> | ||
new IllegalArgumentException("Some relative or absolute offset should " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
->Either relative or absolute
} | ||
|
||
protected void verify() { | ||
ofNullable(offsetOption).orElseThrow(() -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should consider here, that useRelative(x,y) won't work as expected for press/tap/longTap. I think the best way to avoid it would be to extract element parameter from RelativeOffset into withElement like we discussed before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the main reason of this is that we anyway translate both relative and absolute offset into x,y
JSON keys, so there is no way for the server do distinguish them (unless we start using W3C JSON format to represent the stuff, which is already awaiting in the queue).
* @return a built option | ||
*/ | ||
public static RelativeOffsetOption useRelative(WebElement element) { | ||
return useRelative(element, 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0,0
should not be the case, but separate withElement should address this problem.
checkNotNull(element); | ||
this.elementId = ((HasIdentity) element).getId(); | ||
this.relativeOffset = new Point(xOffset, yOffset); | ||
//noinspection unchecked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this comment is not needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same below
result.put("x", point.x); | ||
result.put("y", point.y); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the empty line is not needed
|
||
@Override | ||
protected void verify() { | ||
ofNullable(duration).orElseThrow(() -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this verification is redundant, since we already have checkNotNull verification inside withDuration method
public void invalidAbsolutePositionOptionsShouldFailOnBuild() throws Exception { | ||
final List<ActionOptions> invalidOptions = new ArrayList<>(); | ||
invalidOptions.add(new AbsoluteOffsetOption()); | ||
for (ActionOptions opts : invalidOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it make sense to create an array with a single item?
public void invalidRelativePositionOptionsShouldFailOnBuild() throws Exception { | ||
final List<ActionOptions> invalidOptions = new ArrayList<>(); | ||
invalidOptions.add(new RelativeOffsetOption()); | ||
for (ActionOptions opts : invalidOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
- removal of redundant code from the WaitOptions. - code of test was optimized.
@@ -24,34 +25,28 @@ | |||
public class TouchOptionsTests { | |||
private static final WebElement DUMMY_ELEMENT = new DummyElement(); | |||
|
|||
@Test | |||
@Test(expected = IllegalArgumentException.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know about such possibility %)
- PositionOffsetOption and WebElementOption instead of AbsoluteOffsetOption and RelativeOffsetOption.
* @param longPressOptions see {@link LongPressOptions}. | ||
* @return this TouchAction, for chaining. | ||
*/ | ||
public T longPress(LongPressOptions longPressOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we still need this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mykola-mokhnach
Yes because it has the additional
withDuration
} | ||
|
||
/** | ||
* Sets the offset from the upper left corner of the screen/element or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have two separate methods to set relative and absolute offset, so then we can distinguish these in verify and show an error if the client wants, for example, to use absolute coordinates with moveTo call.
* @param yOffset is y-axis offset. | ||
* @return this instance for chaining. | ||
*/ | ||
public T withOffet(int xOffset, int yOffset) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Offet
import java.util.Map; | ||
|
||
public class PositionOffsetOption<T extends PositionOffsetOption<T>> extends ActionOptions<T> { | ||
private Point offset = new Point(0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like I mentioned before, the default value should not be set to 0,0
, but rather to null. When only element is set the the server code selects appropriate coordinates for touch and this is not always the top left corner of the element.
* @return the built option | ||
*/ | ||
public static WebElementOption elementOption(WebElement element) { | ||
return elementOption(element, 0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be no zeroes
checkNotNull(element); | ||
checkArgument(true, "Element should be an instance of the class which " | ||
+ "implements org.openqa.selenium.internal.HasIdentity", | ||
(HasIdentity.class.isAssignableFrom(element.getClass()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
element instanceof HasIdentity
is enough
|
||
public class WebElementOption extends PositionOffsetOption<WebElementOption> { | ||
|
||
private HasIdentity elementId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could save only String id value here instead of the whole element
TouchAction dragNDrop = | ||
new TouchAction(driver).longPress(dragDot1).moveTo(dragDot3).release(); | ||
TouchAction dragNDrop = new TouchAction(driver) | ||
.longPress(elementOption(dragDot1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps withOffset(elementOption(...))
* @param y is the y-offset from the upper left corner of the given element. | ||
* @return the built option | ||
*/ | ||
public static WebElementOption elementOption(WebElement element, int x, int y) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather rename these to simply element
, relative
and absolute
, so we can pass it like
withOffset(element(el, x, y)), withOffset(element(el)), withOffset(relative(x,y)), withOffset(absolute(x,y))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mykola-mokhnach
I think it would be better to have two static methods
element()
offset() //to not confuse user when he/she waits for an action with absolute offset but it acts with
//relative or does the opposite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there was a flag that could make action use offset as some relative/absolute value forcefully I think that it would have sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's easier to perform the verification on the client side and throw an exception then if we explicitly define which type of offset we have - relative or absolute.
Also issues whick were pointed out by @mykola-mokhnach were fixed out
@mykola-mokhnach |
The point is to make this implementation backward compatible to the existing one. And the existing one defines:
the meaning of "relative coordinates" in this context is that these coordinates are calculated as the offset from the most recent item in the chain, which precedes the current one |
- offsets were divided to absolute(PointOption) relative(OffsetOption) and relative to an element (ElementOption) - The PointOption which may take any position option - signature of TouchAction was changed
import org.openqa.selenium.Point; | ||
|
||
public abstract class AbstractPositionOption<T extends AbstractPositionOption<T>> extends ActionOptions<T> { | ||
protected Point offset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
= null
|
||
public class PointOption extends AbstractPositionOption<PointOption> { | ||
|
||
private static final String ERROR_MESSAGE_TEMPLATE = "%s coordinate value should be equal or greater than zero"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have it of type Function<String, String>
* @return self-reference | ||
*/ | ||
@Override | ||
public PointOption position(int xOffset, int yOffset) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not AbsoluteOffsetOption or just AbsoluteOffset?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is not completely correct. When we say absolute offset
it means coordinates
or 'point' on the screen.
.withDuration(ofSeconds(2))) | ||
.moveTo(offset(center2.x, center2.y)) | ||
.moveTo(position() | ||
.withPosition(coordinates(center2.x, center2.y))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how this expression visually helps to understand, that this position is calculated relatively to the previous element and the previous one is absolute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mykola-mokhnach
The signature of methods prevents the setting of the wrong offset. Like
public T tap(Position<PointOption> tapOptions)
It means that user may set only absolute offset (PointOption
, coordinates(x, y)
) or element-option there.
You said before
moveTo: relative coordinates (x, y) and partially absolute coordinates (element; element, x ,y)
It means that moveTo
is not bounded by absolute/relative/element offsets formally. So I desided to
public <S extends AbstractPositionOption> T moveTo(Position<S> moveToOptions) {
and it is possible to
moveTo(position().withOffset(coordinates()))
moveTo(position().withOffset(offset()))
moveTo(position().withOffset(element()))
I thought that one of ideas behind these changes was to minimize method count and exclude unnecessary overloading/code repeating. But, yes, it should be safe and it should exclude accidental user's actions.
If not everything is ok with this approach then I think that we could only split moveTo
to
moveTo(absolute)
moveTo(relative)
but it is still not clear when each method should be invoked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be there is some problem with the method naming
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea in general. What would you say if we don't split parameter signatures between move/non-moveTo, but just add a possibility to throw a runtime verification exception if withOffset(coordinates()
) is provided to moveTo
and/or withOffset(offset()))
is provided to non-moveTo
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mykola-mokhnach I think that the good way is to inform users by method signature and compilation errors. I suppose users won't have fun if compiled code will throw lots of runtime verification errors.
The Position
class has at least two purposes:
-
unify signature of methods. Most of touch actions (present and further) can use either coordinate position/offset or offset from some element. So the class has 2 methods
withOffset
andwithElement
which invocation replaces previously stored value. -
bound used coordinate strategy (absolute or relative) by generic parameter.
Currently most of actions use only points/elements except moveTo
which may consume offsets as well, but I think there will be new actions which will use only offsets/elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If something breaks the spec then it should throw an error. However I see there is a major issue in the server implementation. iOS threats moveTo arguments as relative and Android as absolute. That's why such expression
TouchAction dragNDrop = new TouchAction(driver)
.longPress(longPressOptions()
.withPosition(coordinates(center1.x, center1.y))
.withDuration(ofSeconds(2)))
.moveTo(position()
.withPosition(coordinates(center2.x, center2.y)))
.release();
works perfectly for Android, but causes an unexpected behaviour for iOS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mykola-mokhnach Yes i remember i had an issue open on this at server end reg. moveTo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I need to think a bit.
One of the possible ways could be the ovelloading of such methods by iOS/Androind/Windows (if it will be added), maybe the making TouchAction the abstract class with generic signature of such merhods, overloaded with bounds by sublasses .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AbstractPositionOption, OffsetOption, Position were removed. The set of class was simplified. Signature of TouchAction methods was changed and simplified.
@mykola-mokhnach @SrinivasanTarget ...Lets wait the new beta of appium with fixed appium/appium#7486 before the publishing of the 6.0.0-BETA. |
public class PointOption extends AbstractPositionOption<PointOption> { | ||
public class PointOption<T extends PointOption<T>> extends ActionOptions<T> { | ||
|
||
protected Point coordinates; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
= null
* @return self-reference | ||
*/ | ||
@Override | ||
public ElementOption coordinates(int xOffset, int yOffset) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be withCoordinates
to follow the common style?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor comments. I like the way it looks now.
public T coordinates(int xOffset, int yOffset) { | ||
checkArgument(xOffset >= 0, format(ERROR_MESSAGE_TEMPLATE, "X")); | ||
checkArgument(yOffset >= 0, format(ERROR_MESSAGE_TEMPLATE, "Y")); | ||
coordinates = new Point(xOffset, yOffset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
withCoordinates
as mykola suggested above.
|
||
public static <E extends Throwable> Matcher<Runnable> failsWith( | ||
final Class<E> throwableType, final Matcher<? super E> throwableMatcher) { | ||
return new FailsWithMatcher<>(allOf(instanceOf(throwableType), throwableMatcher)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also compilation error here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??? ок
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SrinivasanTarget
I can't reproduce it even after removal of the project from the disc and cloning it again.
Could you try again? If you are facing the same issue could you provide some text of the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SrinivasanTarget even Travis could compile the project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes travis wasn't able to catch it. Only IDE throws it on file level but still project builds fine. no instance of type variable exists so that E conforms to capture of ? super....
is error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks fine in latest IDE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you use Eclipse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intellij IDEA 2017.1.5 throws this error but no errors in Intellij IDEA 2017.2
- the `coordinates` method was renamed to `withCoordinates` - removal of unused imports from ElementOption
I am waiting for @SrinivasanTarget 's approve. |
Change list
The addition to the #756
Types of changes