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

changed moveTo to like it's been implemented in click #11529

Merged
merged 12 commits into from
Oct 28, 2023
Merged

changed moveTo to like it's been implemented in click #11529

merged 12 commits into from
Oct 28, 2023

Conversation

udarrr
Copy link
Member

@udarrr udarrr commented Oct 25, 2023

Proposed changes

change moveTo to click similarity, because of flaky moveTo

Types of changes

I'd like to suggest to change it to a way like it's been implemented in click method with 'origin: this' instead of calculation full offset. Even more i don't understand why we should calculate it instead of using 'origin: this' as click method perform particularly the same, with stable behavior in each cases, just with some additional actions like 'down' and 'up'.

@udarrr udarrr changed the title changed moveTo to like in has been implemented in click changed moveTo to like it's been implemented in click Oct 25, 2023
@erwinheitzman
Copy link
Member

Hi @udarrr

Could you provide examples of actual cases in which the command is not doing what you would expect it to do?
This way we can verify that this indeed fixes the problem(s)

@udarrr
Copy link
Member Author

udarrr commented Oct 25, 2023

Hi @udarrr

Could you provide examples of actual cases in which the command is not doing what you would expect it to do? This way we can verify that this indeed fixes the problem(s)

Hi @erwinheitzman unfortunately i can't because known reproduceable an example with no public resources, but from my perspective here something wrong. Let me explain

we have implementation of clicking, click https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/commands/element/click.ts already have an implementation of moveTo but with some extra actions

something like

if (this.isW3C) {
const browser = getBrowserObject(this)
await browser.action('pointer', {
parameters: { pointerType: 'mouse' }
})
.move({
origin: this,
x: xoffset,
y: yoffset
})
.down({ button })
.up({ button })

.perform(skipRelease)
return
}

it's moving pointer to element with scrolling and then down and up pointer, if we'd like to be moved to element without pressing pointer i believe we should just use 'move' without down and up. It's the same without useless calculations. And it works everywhere even with my apps.

@erwinheitzman
Copy link
Member

Hi @udarrr

Could you provide examples of actual cases in which the command is not doing what you would expect it to do? This way we can verify that this indeed fixes the problem(s)

Hi @erwinheitzman unfortunately i can't because known reproduceable an example with no public resources, but from my perspective here something wrong. Let me explain

we have implementation of clicking, click https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/commands/element/click.ts already have an implementation of moveTo but with some extra actions

something like

if (this.isW3C) {

    const browser = getBrowserObject(this)

    await browser.action('pointer', {

        parameters: { pointerType: 'mouse' }

    })

        .move({

            origin: this,

            x: xoffset,

            y: yoffset

        })

        **.down({ button })

        .up({ button })**

        .perform(skipRelease)

    return

}

it's moving pointer to element with scrolling and then down and up pointer, if we'd like to be moved to element without pressing pointer i believe we should just use 'move' without down and up. It's the same without useless calculations. And it works everywhere even with my apps.

I think I understand what you mean and I feel like we should create a utility function to retrieve the coordinates of an element with the passed offsets as right now we would have to maintain it in two places. Also the click implementation is still different from the changes you suggest so that will also be resolved by doing this.
Do you have a clear picture of what it is I am suggesting? 🙂

@erwinheitzman
Copy link
Member

Additionally there might be more commands that could benefit from such a utility function, like the scrollIntoView command might be able to use it (it does scroll of course)

@udarrr
Copy link
Member Author

udarrr commented Oct 25, 2023

Do you have a clear picture of what it is I am suggesting?

To be honest no :) it's still not clear for me. Could you clarify that ?

this is click implementation

const browser = getBrowserObject(this)
await browser.action('pointer', {
parameters: { pointerType: 'mouse' }
})
.move({
origin: this,
x: xoffset,
y: yoffset
})
.down({ button })
.up({ button })
.perform(skipRelease)

this is my implementation of moveTo

const browser = getBrowserObject(this)
await browser.action('pointer', {
parameters: { pointerType: 'mouse' }
})
.move({
origin: this,
x: xoffset,
y: yoffset
})
.perform()

@udarrr
Copy link
Member Author

udarrr commented Oct 25, 2023

Could you provide examples of actual cases in which the command is not doing what you would expect it to do? This way we can verify that this indeed fixes the problem(s)

Generally i was able to reproduce it. Here is description #11534 There are two issue.

  1. It can't be performed properly inside of iframe with current version of moveTo but can be resolved as i've done it above for both cases either none iframe element or iframe element

  2. My assumption that 'move' method can scroll it to element was wrong that's why if element not into view 'moveTo' method throw an Error 'move target out of bounds' Maybe it have sense to add scrolling to the method and timeout for scroll animation. Something like that

    const browser = getBrowserObject(this);
    await this.scrollIntoView(scrollOptions)
    await browser.pause(scrollAnimationTimeout)
    return browser.action('pointer', { parameters: { pointerType: 'mouse' } })
    .move({origin: this, x: 0, y: 0 })
    .perform();

or it could be responsibility of developer to add scrollIntoView and timeout to test. What do you think ?

@erwinheitzman
Copy link
Member

erwinheitzman commented Oct 25, 2023

The command adheres to the webdriver specs by throwing the error and has always acted like this and there are other options to do this (action command) so I would say that it is up to the user to scroll for now.

What I meant is that the whole part of the coordinates can be abstracted away into a function that can be shared between moveTo and click

@udarrr
Copy link
Member Author

udarrr commented Oct 26, 2023

What I meant is that the whole part of the coordinates can be abstracted away into a function that can be shared between moveTo.

I got it, but i'd like to suggest don't use it at all. moveTo can handle it to center by default for origin if i am not mistaken.
image

The same for clicking
image

I've updated the pr.

The command adheres to the webdriver specs by throwing the error and has always acted like this and there are other options to do this (action command) so I would say that it is up to the user to scroll for now.

Ok i'll create for that different discussion. Maybe it could be a good feature to add it.

Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

I think overall this makes sense. I wonder if there is an easy way to create some e2e tests for this similar to what we have /e2e/wdio/headless/test.e2e.ts#L123.

@erwinheitzman wdyt?

@erwinheitzman
Copy link
Member

erwinheitzman commented Oct 27, 2023

I think overall this makes sense. I wonder if there is an easy way to create some e2e tests for this similar to what we have /e2e/wdio/headless/test.e2e.ts#L123.

@erwinheitzman wdyt?

Yeah that's totally possible.

We can moveTo the same element with different offsets and also things like not passing and offsets, something along the lines of:

const inputs = [
  [undefined, { x: 9999, y: 9999 }],
  [{ xOffset: 10 }, { x: 9999, y: 9999 }],
  [{ yOffset: 10 }, { x: 9999, y: 9999 }],
  [{ xOffset: 25, yOffset: 25 }, { x: 9999, y: 9999 }],
  [{ xOffset: Number.MAX_SAFE_INTEGER, yOffset: Number.MAX_SAFE_INTEGER }, { x: 9999, y: 9999 }],
]

inputs.forEach(([input, output]) => {
  it(`moves to position x ${output.x} y ${output.y} when passing the arguments ${JSON.stringify(input)}`, () => {
    await browser.execute(() => {
      let mouse = { x:0, y:0 }
      document.onmousemove = function(e){ mouse.x = e.clientX, mouse.y = e.clientY }
    })
    
    await elem.moveTo(input);
    
    const { x, y } = await browser.execute(() => mouse)

    expect(x).toEqual(output.x)
    expect(y).toEqual(output.y)
  })
})

Note that this code is an example and I can by no means guarantee it works

@udarrr
Copy link
Member Author

udarrr commented Oct 27, 2023

Yeah that's totally possible.

We can moveTo the same element with different offsets and also things like not passing and offsets, something along the lines of:

const inputs = [
  [undefined, { x: 9999, y: 9999 }],
  [{ xOffset: 10 }, { x: 9999, y: 9999 }],
  [{ yOffset: 10 }, { x: 9999, y: 9999 }],
  [{ xOffset: 25, yOffset: 25 }, { x: 9999, y: 9999 }],
  [{ xOffset: Number.MAX_SAFE_INTEGER, yOffset: Number.MAX_SAFE_INTEGER }, { x: 9999, y: 9999 }],
]

inputs.forEach(([input, output]) => {
  it(`moves to position x ${output.x} y ${output.y} when passing the arguments ${JSON.stringify(input)}`, () => {
    await browser.execute(() => {
      let mouse = { x:0, y:0 }
      document.onmousemove = function(e){ mouse.x = e.clientX, mouse.y = e.clientY } 
    })
    
    await elem.moveTo(input);
    
    const { x, y } = await browser.execute(() => mouse)

    expect(x).toEqual(output.x)
    expect(y).toEqual(output.y)
  })
})

Note that this code is an example and I can by no means guarantee it works

That's great an example @erwinheitzman, It worth to add it. I've added some test cases related to your an example regarding position moving

@udarrr
Copy link
Member Author

udarrr commented Oct 28, 2023

Tests are failed don't related to last changes, because of

[headless chrome 120.0.6092.0 linux #1-0] Expected substring: "headless chrome"
[headless chrome 120.0.6092.0 linux #1-0] Received string: "mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko) headlesschrome/120.0.6092.0 safari/537.36"

there are 2 tests in different suites

@udarrr
Copy link
Member Author

udarrr commented Oct 28, 2023

I've fixed 2 test cases regarding message above and finally it can be merged)

e2e/wdio/headless/test.e2e.ts Outdated Show resolved Hide resolved
e2e/wdio/headless/test.e2e.ts Outdated Show resolved Hide resolved
e2e/wdio/headless/test.e2e.ts Outdated Show resolved Hide resolved
Copy link
Member

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Thanks a lot!

Copy link
Member

@erwinheitzman erwinheitzman left a comment

Choose a reason for hiding this comment

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

This is looking really good, thank you very much for your hard work 👏

@erwinheitzman erwinheitzman merged commit 233fbcd into webdriverio:main Oct 28, 2023
7 of 8 checks passed
@erwinheitzman erwinheitzman added the PR: Polish 💅 PRs that contain improvements on existing features label Oct 28, 2023
@vitaly-kiselev-qa
Copy link

vitaly-kiselev-qa commented Feb 3, 2024

@udarrr
Hello.

The comment states that xOffset and yOffset are relative to the top-left corner of the element:
https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/commands/element/moveTo.ts#L16

Looks like it is no longer true since they are passed unchanged to moveTo as x and y:
https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/commands/element/moveTo.ts#L44

And move (params: PointerActionMoveParams) expects x and y relative to the center of a specific element:
https://webdriver.io/docs/api/browser/action/#pointer-input-source

Is it possible to update the documentation?

@christian-bromann
Copy link
Member

@vitaly-kiselev-qa feel free to raise a PR if you have a concrete suggestions in mind.

@vitaly-kiselev-qa
Copy link

@christian-bromann thank you for your answer.

My comment was more a question not a suggestion.

It looks like, the behaviour has been changed and the documention has not.
The question is — was the change intentional or not?

For my code this was a breaking change and I had to rewrite it (calculate coordinates in a new way).

@christian-bromann
Copy link
Member

@vitaly-kiselev-qa mind raising an issue with a description of this, I think this should be fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: Polish 💅 PRs that contain improvements on existing features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants