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

[Feature] Add waitForAnimation #4055

Closed
dwood023 opened this issue Oct 5, 2020 · 23 comments
Closed

[Feature] Add waitForAnimation #4055

dwood023 opened this issue Oct 5, 2020 · 23 comments

Comments

@dwood023
Copy link

dwood023 commented Oct 5, 2020

Perhaps I have misunderstood the existing API, but I've found it tricky to wait reliably for elements with animations (e.g. a fade-in using opacity).

I saw a solution to this problem using Puppeteer on Stack Overflow and successfully applied the same pattern in my Playwright tests (see code below).

export const waitForAnimation = async (frame, selector) => {
  return frame.$eval(selector, el => new Promise((res, _) => {
    const onEnd = () => {
      el.removeEventListener('animationend', onEnd)
      res()
    }
    el.addEventListener("animationend", onEnd)
  }))
}
// Usage
await waitForAnimation(frame, 'div >> "Look at this div's fade-in animation!"')

But ideally there could be an API resembling waitForSelector:

frame.waitForAnimation('div >> "Look at this div's fade-in animation!"')

ElementHandle.waitForElementState("stable") looked like it could work for this purpose, but on closer reading it sounds like this will only work for animations which affect the element's bounding box (so not for opacity)

Again, pardon my ignorance if the API already handles animations/transitions with another method, but otherwise this seemed like a fairly common use case that is reliably solved with event listeners.

@dgozman
Copy link
Contributor

dgozman commented Oct 6, 2020

Thank you for the issue! Could you please clarify why do you wait for the fade-in to finish? Do you perform some action after waiting for fade-in, e.g. clicking? I'd like to better understand the usecase to provide a good API.

@dwood023
Copy link
Author

dwood023 commented Oct 10, 2020

In my case, I simply take a screenshot for review at a later date - this is the only reason I've had so far for needing to wait for animations to finish, as navigation/clicking has worked flawlessly regardless of animations.

@bashess
Copy link

bashess commented Oct 10, 2020

I had a case where i wait for transition end, to check if a hover image in a shop product list is visible. I don't get it worked with waitForState.

@evanleck
Copy link

I'd like something like this as well. I have forms that transition between fieldsets using an animation and waitForSelector catches those fieldset elements immediately so they're not fully animated in. As part of our tests we capture screenshots and having to dance around the animations is getting kinda tedious.

@dgozman
Copy link
Contributor

dgozman commented Feb 11, 2021

@evanleck Did you try waitForElementState('stable')? It should wait for any motion animations to finish. However, if the element stays in place, like when animation only touches opacity, it won't work.

@evanleck
Copy link

@dgozman yeah, I counted that out since the only thing I'm animating is opacity. Thank you though!

@yauri-io
Copy link

Yeah this one would be a nice addition if the feature can be added.
For now there are a couple of ways for handling this though.

@sahmeepee
Copy link

We have a similar issue with a date picker that transitions from display: none to visible, and then animates from zero to full opacity so we only want to screenshot at the end of that sequence. Right now our screenshots always fail comparison. Currently we would have to build something slightly messy where we check the styles to await full opacity, but it would be good if we could easily detect that the opacity was stable for more than X milliseconds.

I wouldn't have a problem with that being incorporated into waitForElementState('stable'), but perhaps that would break existing tests in rare situations for others.

@sergioariveros
Copy link

We really need this, one of our application is completely animated with WebGL and some videos, and we have to take some screenshots to do some visual testing

@thewilkybarkid
Copy link

I had a race condition between an opacity transition and taking a screenshot; investigated various options but there doesn't seem to be a way to detect if an element is currently transitioning (or what the current opacity is). So, I've had to just disable them: PREreview/prereview@c4d06c4

@arukiidou
Copy link
Contributor

If the animation that keeps adding opacity until "style="opacity: 1;"
You can solve this problem as follows.

await page.waitForSelector('#nav1[style="opacity: 1;"]')

@p01
Copy link
Contributor

p01 commented Nov 2, 2021

For CSS animations and transitions, I usually inject some CSS to force all animation-duration and transition-duration to be 1ms. I favor this over disabling CSS animations and transitions as some UI code can depend on the animation-end and transition-end event which will never fire if they are disabled.

To deal with JavaScript based animations (mostly due to fallback code in our case), in our Component tests, I added a method to check if the page is "busy" with any pending network activity, Promises, setTimeout ( >N seconds as those are not UI related ), or recent DOM mutations. It is a bit heavy handed, but it works well for Component tests.

@aslushnikov
Copy link
Contributor

aslushnikov commented Feb 15, 2022

Everybody: the disableAnimations option has landed on @playwright/test@next for page.screenshot, locator.screenshot and elementHandle.screenshot. This should stop all the CSS animations and transitions; please give it a try!

@aslushnikov
Copy link
Contributor

I'll close this for now in favor of the disableAnimations option for screenshots. It's scheduled to be shipped in 1.20. Feel free to open a new issue if you think that's not sufficient!

@janosh
Copy link

janosh commented Apr 7, 2022

@aslushnikov I think this issue should remain open for now. What about e2e testing? The OP was not concerned with screenshots it seems to me. In my case, I need to test computedStyles on an element after its transition (triggered by user input) has finished.

@ghost
Copy link

ghost commented Apr 6, 2023

this animation disabled property not working

@aslushnikov
Copy link
Contributor

@janosh could you please file separately with your usecase? This way we can keep it on the radar and gauge community interest

@janosh
Copy link

janosh commented Apr 6, 2023

@aslushnikov Wouldn't it be easier to re-open this issue? I would essentially just copy the OP text from here into a new issue and then everyone who upvoted would have to go find the new issue and upvote again.

@aslushnikov
Copy link
Contributor

aslushnikov commented Apr 10, 2023

@janosh fair. However, I don't understand the use case outside of the VRT. Why would you want to explicitly wait for certain animation to finish? This should already be handled with our auto-waiting logic.

You clearly have a usecase in mind; so filing a new issue with a clear-cut use case would help us in discussing the API and gauging community interest.

@janosh
Copy link

janosh commented Apr 10, 2023

I have several tests where current auto-waiting doesn't do the trick. See here and here. The tests fail without custom waiting logic because I'm checking that a select dropdown auto-closes when reaching max number of selectable items at which point an animation plays and only after that is the DOM node unmounted which I'm testing to be gone.

@aslushnikov
Copy link
Contributor

@janosh So in the first test:

https://github.com/janosh/svelte-multiselect/blob/2b2eb0dbc3974b53d5fad7c176d4c03b21d46e92/tests/MultiSelect.test.ts#L471

You have to await the expect, not the locator:

await expect(page.locator(ul_selector)).toBeHidden();

See https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-hidden for details on web-first assertions.

I didn't check the second test; please, raise a new issue so that we can discuss your issues without spamming all the people here.

@janosh
Copy link

janosh commented Apr 11, 2023

Oh... you're exactly right. Please disregard my prior comments. No new issue necessary on my end. Auto-waiting ftw. 😅

@FoHoOV
Copy link

FoHoOV commented Mar 1, 2024

For future readers, I think this solution might be the cleanest and easiest one to do:

export async function waitForAnimationEnd(locator: Locator) {
	const handle = await locator.elementHandle();
	await handle?.waitForElementState('stable');
	handle?.dispose();
}

this link is the reference to what element states are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests