question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] Click misses target due to animation

See original GitHub issue

Context:

  • Playwright Version: 1.22.1
  • Operating System: Linux
  • Node.js version: 16.13.2
  • Browser: Chromium

Code Snippet

import { expect, test } from '@playwright/test';

const html = `
<html>
<head>
  <style>
    .expanded .list {
      max-height: 100%;
    }
    .list {
      transition: max-height 0.25s cubic-bezier(0.82, 0, 0.1, 1);
      max-height: 0;
      overflow: auto;
    }
  </style>
  <script>
    function headerHandler() {
      const parent = document.getElementById('parent');
      if (parent.classList.contains('expanded')) {
        parent.classList.remove('expanded')
      } else {
        parent.classList.add('expanded')
      }
    }

    function optionHandler(i) {
      document.getElementById('result').innerHTML = 'O' + i;
    }
  </script>
</head>
<body>
<div id="parent">
  <div>
    <span onclick="headerHandler()">Header</span>
  </div>
  <ul class="list">
    <li onclick="optionHandler(1)">Option1</li>
    <li onclick="optionHandler(2)">Option2</li>
    <li onclick="optionHandler(3)">Option3</li>
    <li onclick="optionHandler(4)">Option4</li>
    <li onclick="optionHandler(5)">Option5</li>
    <li onclick="optionHandler(6)">Option6</li>
    <li onclick="optionHandler(7)">Option7</li>
  </ul>
</div>
<div id="result"></div>
</body>
</html>
`;

test('repro', async ({page}) => {
  await page.setContent(html);

  const testClick = async (i: number) => {
    await page.click('text=Header');
    await page.click(`text=Option${i}`);
    await page.click('text=Header');
    await expect(page.locator('#result')).toHaveText(`O${i}`);
    await page.waitForTimeout(260); // Some time for the menu to close, essentially resetting state.
  }

  for (let i = 0; i < 100; i++) {
    await testClick(6);
    await testClick(7);
  }
});

Describe the bug

Seems like PW is not waiting for animation to finish before trying to click an element, causing it to click wrong element. Due to the 100 iterations within the test it fails quite reliably, so this doesn’t happen very often in the above code, though with our real code it happens a bit more, maybe around 10% of the time.

The trace sometimes shows the red dot (where PW is going to click) quite a bit off from the intended element, while the intended element is highlighted in blue.

Video on the other hand shows that PW is clicking before animation has finished.

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:1
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
MarkusJLechnercommented, Oct 26, 2022

I updated the function to include the waitForAnimations snippet + renamed stableFrames to samePositionCount to make it more clear what it is. @skysantoroa Basically playwright looks for two iterations to have the same position, then it is called stable. With the samePositionCount you can increase the stable iterations to a higher number. Between one iteration there is a sleep for 15ms, i was reading something about a win webkit bug, for safety i leave it there

export async function waitStable(locator: Locator, waitForAnimations: boolean = false, samePositionCount: number = 20) {
  await locator.evaluate(
    async (element: SVGElement | HTMLElement, { waitForAnimations, samePositionCount }) => {
      // https://github.com/microsoft/playwright/blob/98215b4d74030dac7a98a37c67650fd9d0b82509/packages/playwright-core/src/server/injected/injectedScript.ts#L527-L586
      async function progressIsStable(
        element: SVGElement | HTMLElement,
        lastRect: { x: number; y: number; width: number; height: number } | null = null,
        samePositionCounter: number = 0,
        iteration: number = 0
      ): Promise<void> {
        // kill switch
        if (iteration > 500) {
          return
        }

        if (waitForAnimations) {
          await Promise.all(element.getAnimations().map((animation) => animation.finished))
        }

        await new Promise((resolve) => setTimeout(resolve, 15))

        const clientRect = element.getBoundingClientRect()
        const rect = { x: clientRect.left, y: clientRect.top, width: clientRect.width, height: clientRect.height }
        const samePosition =
          lastRect &&
          rect.x === lastRect.x &&
          rect.y === lastRect.y &&
          rect.width === lastRect.width &&
          rect.height === lastRect.height

        if (samePosition) {
          ++samePositionCounter
        } else {
          samePositionCounter = 0
        }

        const isStable = samePositionCounter >= samePositionCount

        lastRect = rect

        if (!isStable) {
          return progressIsStable(element, lastRect, samePositionCounter, ++iteration)
        }
      }

      return progressIsStable(element)
    },
    { waitForAnimations, samePositionCount }
  )

  return locator
}
1reaction
unlikelyzerocommented, Aug 4, 2022

@leotg130 you should bring up your findings on that RFE and give it a 👍 to raise the priority

Read more comments on GitHub >

github_iconTop Results From Across the Web

Animation targets keep getting disconnected - Forum | Webflow
After looking into the issue a bit more it appears this is expected behavior. The animation targets that are being disconnected are on...
Read more >
Animation event not triggering - Unity Forum
Currently in my game a lot of events are triggered from within an animation and occasionally they seem to be skipped over.
Read more >
Unity UI button not reacting to clicks or hovering - Stack Overflow
If anyone here ever runs into this problem and none of these fixes work, go to Edit > project settings > Input, then...
Read more >
Solve the FBX Import Error when importing new animations ...
This tutorial explains how to solve the "FBX Import Error " message that you could get when importing a new animation for a...
Read more >
Target Matching - Unity - Manual
You also need to find the place in the animation clip where the character is about to land on its feet, which in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found