Skip to content
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

Merged
merged 15 commits into from
Nov 12, 2017
Merged

Mykola mokhnach's actions params: The addition to the #756 #760

merged 15 commits into from
Nov 12, 2017

Conversation

TikhomirovSergey
Copy link
Contributor

Change list

The addition to the #756

Types of changes

  • No changes in production code.
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Mykola Mokhnach and others added 8 commits October 16, 2017 10:43
- 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
@TikhomirovSergey
Copy link
Contributor Author

@mykola-mokhnach @SrinivasanTarget
I finished. Could you take a look at this one?
@SrinivasanTarget could you run iOS tests?

* @return this TouchAction, for chaining.
*/
public TouchAction moveTo(WebElement el) {
ActionParameter action = new ActionParameter("moveTo", (HasIdentity) el);
public T moveTo(AbsoluteOffsetOption moveToOptions) {
Copy link
Contributor

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));
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Nov 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • @param x change in x coordinate to move through.
  • @param y change in y coordinate to move through.

@Override
protected void verify() {
ofNullable(absoluteOffset).orElseThrow(() -> new IllegalArgumentException(
"Absolute offset must not be defined"));
Copy link
Contributor

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 "
Copy link
Contributor

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(() ->
Copy link
Contributor

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

Copy link
Contributor

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);
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor

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);
});

Copy link
Contributor

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(() ->
Copy link
Contributor

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) {
Copy link
Contributor

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) {
Copy link
Contributor

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)
Copy link
Contributor

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.
@appium appium deleted a comment Nov 5, 2017
* @param longPressOptions see {@link LongPressOptions}.
* @return this TouchAction, for chaining.
*/
public T longPress(LongPressOptions longPressOptions) {
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Nov 6, 2017

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) {
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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())));
Copy link
Contributor

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;
Copy link
Contributor

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))
Copy link
Contributor

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) {
Copy link
Contributor

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))

Copy link
Contributor Author

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. 

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@TikhomirovSergey
Copy link
Contributor Author

TikhomirovSergey commented Nov 6, 2017

@mykola-mokhnach
It seems I still don't understand something.
First of all it would be good to understand the action flow and how do actions use offsets accordingly their number in the action sequence.
Also which actions may use only absolute/relative offsets.

@mykola-mokhnach
Copy link
Contributor

mykola-mokhnach commented Nov 6, 2017

The point is to make this implementation backward compatible to the existing one. And the existing one defines:

  • tap, longPress, press: absolute coordinates (x, y; element; element, x, y)
  • moveTo: relative coordinates (x, y) and partially absolute coordinates (element; element, x ,y)

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

@appium appium deleted a comment Nov 6, 2017
- 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
@appium appium deleted a comment Nov 8, 2017
import org.openqa.selenium.Point;

public abstract class AbstractPositionOption<T extends AbstractPositionOption<T>> extends ActionOptions<T> {
protected Point offset;
Copy link
Contributor

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";
Copy link
Contributor

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) {
Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Nov 9, 2017

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?

Copy link
Contributor Author

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)))
Copy link
Contributor

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?

Copy link
Contributor Author

@TikhomirovSergey TikhomirovSergey Nov 9, 2017

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.

Copy link
Contributor Author

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

Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Nov 9, 2017

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?

Copy link
Contributor Author

@TikhomirovSergey TikhomirovSergey Nov 9, 2017

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 and withElement 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.

Copy link
Contributor

@mykola-mokhnach mykola-mokhnach Nov 9, 2017

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

Copy link
Member

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

Copy link
Contributor Author

@TikhomirovSergey TikhomirovSergey Nov 9, 2017

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 .

Copy link
Member

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.
@TikhomirovSergey
Copy link
Contributor Author

TikhomirovSergey commented Nov 10, 2017

@mykola-mokhnach @SrinivasanTarget
There is one more change. It was done according our decision to use only absolute coordinates or an offset from an element. Please review the last change.

...Lets wait the new beta of appium with fixed appium/appium#7486 before the publishing of the 6.0.0-BETA.

@appium appium deleted a comment Nov 10, 2017
public class PointOption extends AbstractPositionOption<PointOption> {
public class PointOption<T extends PointOption<T>> extends ActionOptions<T> {

protected Point coordinates;
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor

@mykola-mokhnach mykola-mokhnach left a 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);
Copy link
Member

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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also compilation error here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??? ок

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Member

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you use Eclipse?

Copy link
Member

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
@TikhomirovSergey
Copy link
Contributor Author

I am waiting for @SrinivasanTarget 's approve.

@appium appium deleted a comment Nov 11, 2017
@TikhomirovSergey TikhomirovSergey merged commit a433896 into appium:master Nov 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants