Or press ESC to close.

Automating Animation Testing with Playwright: A Practical Guide

Aug 24th 2025 12 min read
medium
javascriptES6
playwright1.55.0
ui

Animations are an essential part of modern web applications because they guide users, provide feedback, and make interfaces feel more dynamic. While they enhance the user experience, they also introduce challenges when it comes to testing. Manual checks for timing, transitions, and accessibility are often unreliable. In this post, we explore how to use Playwright to automate animation testing with a demo site that includes examples such as fade ins, spinners, slide panels, and accessibility aware animations.

Demo Setup

The demo website is designed to showcase a variety of animations that can be tested in different ways. It includes a fade in effect, a loading spinner, a sliding panel, a modal with backdrop, a transforming box, a hover effect, a respectful animation that adapts to reduced motion preferences, and a more complex animation sequence.

demo app with different animations

The demo application showcasing an animation

Accessibility is also considered in the setup, since some animations respond to the user's operating system preference for reduced motion. This allows us to verify not only visual transitions but also inclusive design practices.

For the test environment, Playwright is used as the automation framework. The test suite interacts with the demo site, triggers animations, and validates their behavior through style checks, visibility assertions, and timing validations.

Testing Common Animations with Playwright

With the demo site in place, the next step is to write tests that confirm each animation works as intended. Playwright makes this easier by providing methods to check CSS values, wait for transitions, and simulate user interactions. Below are examples of how common animations can be validated.

Fade In and Fade Out

To verify a fade in animation, the test first checks that the element starts with zero opacity. After triggering the animation, the test waits until the opacity reaches full visibility. Resetting the animation is then confirmed by checking that opacity returns to zero.

                
await expect(animatedElement).toHaveCSS("opacity", "0");
await page.click("#trigger-animation");
await expect(animatedElement).toHaveClass(/animate-fade-in/);
await page.waitForFunction(() => {
  const el = document.querySelector(".fade-in-element");
  return parseFloat(getComputedStyle(el).opacity) > 0.99;
});
                
Loading Spinner

A spinner test confirms that it becomes visible and animates after a button click. Once loading is complete, the spinner disappears and the success message is displayed. This ensures the animation and its state transition both work.

                
await submitButton.click();
await expect(spinner).toHaveClass(/spinning/);
await expect(successMessage).toHaveCSS("display", "block", { timeout: 4000 });
                
Slide Panel and Modal

For the slide panel, the test validates the element's transform property, confirming it starts off-screen and moves into view after interaction. For the modal, both the modal and its backdrop are checked for visibility and opacity changes to confirm that the fade in and fade out transitions complete correctly.

                
await expect(slidePanel).toHaveCSS("transform", "matrix(1, 0, 0, 1, -400, 0)");
await toggleButton.click();
await expect(slidePanel).toHaveCSS("transform", "matrix(1, 0, 0, 1, 0, 0)");
                
Hover Effects

Hover animations can be tested by simulating a mouse hover and checking that the background color changes. After moving the mouse away, the element should return to its original color.

                
await hoverElement.hover();
const hoverBg = await hoverElement.evaluate(el => getComputedStyle(el).backgroundColor);
                
Animated Box

For transitions that change shape or size, the test measures both the class change and the time it takes to complete. Playwright can record the time before and after the interaction, allowing us to validate that the duration is consistent with what is defined in the CSS.

                
const startTime = Date.now();
await animateBtn.click();
await page.waitForTimeout(600);
const duration = Date.now() - startTime;
expect(duration).toBeGreaterThan(400);
                

These examples illustrate how animations can be validated with a mix of CSS property checks, visibility assertions, and timing validations, giving confidence that they behave as expected.

Advanced Scenarios

Beyond the common animations, there are situations where testing needs to go further. Playwright provides flexibility to handle accessibility preferences, chained keyframes, performance checks, and even faster execution by disabling animations.

Respecting Reduced Motion

Accessibility often requires animations to respect the user's system settings. With Playwright, the emulateMedia method can simulate a reduced motion environment. Instead of moving across the screen, the element changes color, and the test validates that behavior.

                
await page.emulateMedia({ reducedMotion: "reduce" });
await triggerBtn.click();
const newBg = await respectfulElement.evaluate(el => getComputedStyle(el).backgroundColor);
expect(newBg).not.toBe(initialBg);
                
Complex Animations

Some animations involve multiple steps with chained keyframes, such as moving, rotating, and scaling in sequence. In this case, the test confirms that the animation class is applied and waits long enough for the cycle to complete. The element should end in its original state while still holding the animation class.

                
await startBtn.click();
await expect(complexElement).toHaveClass(/start-complex/);
await page.waitForTimeout(3200);
                
Disabling Animations for Faster Tests

For situations where speed is more important than visual confirmation, animations can be disabled by injecting a style tag. This shortens execution time while still allowing tests to validate functionality without waiting for transitions.

                
await page.addStyleTag({
  content: `*, *::before, *::after {
    animation-duration: 1ms !important;
    transition-duration: 0s !important;
  }`
});
                
Performance Testing

When multiple animations are triggered at once, it is useful to measure overall performance. By recording the start time and verifying that all animations complete within an acceptable duration, the test can confirm that performance stays within expected limits.

                
const startTime = Date.now();
await Promise.all([
  page.click("#trigger-animation"),
  page.click("#animate-box"),
  page.click("#trigger-respectful-animation")
]);
const duration = Date.now() - startTime;
expect(duration).toBeLessThan(3000);
                

These advanced scenarios demonstrate how animation testing can go beyond visual checks to include accessibility, efficiency, and performance, ensuring that animations enhance the user experience without creating issues.

Best Practices and Lessons Learned

Working with animations in automated tests requires both precision and flexibility. A few practices can make the process more reliable and maintainable.

Always combine CSS property checks with appropriate timeouts so that tests wait for animations to finish before making assertions. Methods such as waitForFunction and toHaveCSS are especially useful for verifying transitions because they account for timing.

Consider when to keep animations enabled and when to disable them. Running with full animations provides a realistic view of user experience, while disabling them helps reduce execution time for larger suites. Both approaches can be valuable depending on the goal of the test run.

Finally, remember to test accessibility aspects. By simulating reduced motion preferences, tests ensure that users who cannot tolerate motion-heavy animations still receive a consistent and usable experience.

Conclusion

Animations bring web applications to life, but they also add complexity when it comes to testing. With Playwright, it becomes possible to validate everything from simple fade effects to complex keyframe sequences while also considering accessibility and performance. By combining CSS checks, timing validations, and smart use of reduced motion or disabled animations, tests can stay both accurate and efficient.

This approach ensures that animations do not just look good but also behave consistently across different environments and user preferences. The complete code example used in this post can be found on our GitHub page, making it easy to explore the full setup and adapt it to your own projects.