diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.ts b/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.ts
deleted file mode 100644
index 7c3f60b9a2841..0000000000000
--- a/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { expectPresenceMotionFunction, expectPresenceMotionObject } from '../../testing/testUtils';
-import { Fade } from './Fade';
-
-describe('Fade', () => {
- it('stores its motion definition as a static function', () => {
- expectPresenceMotionFunction(Fade);
- });
-
- it('generates a motion definition from the static function', () => {
- expectPresenceMotionObject(Fade);
- });
-});
diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.tsx b/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.tsx
new file mode 100644
index 0000000000000..3070be848acb2
--- /dev/null
+++ b/packages/react-components/react-motion-components-preview/library/src/components/Fade/Fade.test.tsx
@@ -0,0 +1,69 @@
+import * as React from 'react';
+import { Fade, FadeExaggerated, FadeSnappy } from './Fade';
+import { render } from '@testing-library/react';
+import { motionTokens } from '@fluentui/react-motion';
+import { mockAnimation } from '../../testing/testUtils';
+
+describe('Fade motion component', () => {
+ let originalAnimate: typeof HTMLElement.prototype.animate;
+ let animateSpy: jest.SpyInstance;
+ const testElement =
Test
;
+
+ // JSDOM does not support the Web Animations API, so create a mock animate() before spying on it
+ beforeAll(() => {
+ originalAnimate = HTMLElement.prototype.animate;
+ HTMLElement.prototype.animate = () => mockAnimation();
+ });
+
+ beforeEach(() => {
+ animateSpy = jest.spyOn(HTMLElement.prototype, 'animate');
+ });
+
+ afterEach(() => {
+ animateSpy.mockRestore();
+ });
+
+ afterAll(() => {
+ HTMLElement.prototype.animate = originalAnimate;
+ });
+
+ it('should render Fade with correct opacity keyframes, duration and easing (visible=false -> true -> false)', () => {
+ const { rerender } = render({testElement});
+
+ // Testing fade in motion
+ rerender({testElement});
+ expect(animateSpy).toHaveBeenCalledWith(
+ [{ opacity: 0 }, { opacity: 1 }],
+ expect.objectContaining({ duration: motionTokens.durationNormal, easing: motionTokens.curveEasyEase }),
+ );
+
+ // Testing fade out motion
+ rerender({testElement});
+ expect(animateSpy).toHaveBeenCalledWith(
+ [{ opacity: 1 }, { opacity: 0 }],
+ expect.objectContaining({ duration: motionTokens.durationNormal, easing: motionTokens.curveEasyEase }),
+ );
+ });
+
+ it('should render Snappy variant of Fade component with correct opacity keyframes, duration and easing', () => {
+ const { rerender } = render({testElement});
+
+ rerender({testElement});
+
+ expect(animateSpy).toHaveBeenCalledWith(
+ [{ opacity: 0 }, { opacity: 1 }],
+ expect.objectContaining({ duration: motionTokens.durationFast, easing: motionTokens.curveEasyEase }),
+ );
+ });
+
+ it('should render Exaggerated variant of Fade component with correct opacity keyframes, duration and easing', () => {
+ const { rerender } = render({testElement});
+
+ rerender({testElement});
+
+ expect(animateSpy).toHaveBeenCalledWith(
+ [{ opacity: 0 }, { opacity: 1 }],
+ expect.objectContaining({ duration: motionTokens.durationGentle, easing: motionTokens.curveEasyEase }),
+ );
+ });
+});
diff --git a/packages/react-components/react-motion-components-preview/library/src/testing/testUtils.ts b/packages/react-components/react-motion-components-preview/library/src/testing/testUtils.ts
index e80006cd176b2..27c00673f66f4 100644
--- a/packages/react-components/react-motion-components-preview/library/src/testing/testUtils.ts
+++ b/packages/react-components/react-motion-components-preview/library/src/testing/testUtils.ts
@@ -63,3 +63,31 @@ export function expectPresenceMotionFunction(PresenceComponent: PresenceComponen
expect(presenceMotionFn).toBeInstanceOf(Function);
}
+
+export const mockAnimation: () => Animation = () => ({
+ finish: jest.fn(),
+ cancel: jest.fn(),
+ persist: jest.fn(),
+ currentTime: null,
+ effect: null,
+ finished: Promise.resolve({} as Animation),
+ id: '',
+ play: jest.fn(),
+ pause: jest.fn(),
+ updatePlaybackRate: jest.fn(),
+ reverse: jest.fn(),
+ playState: 'running',
+ playbackRate: 1,
+ startTime: null,
+ timeline: null,
+ oncancel: null,
+ onfinish: null,
+ ready: Promise.resolve({} as Animation),
+ removeEventListener: jest.fn(),
+ addEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ onremove: null,
+ pending: false,
+ replaceState: 'active',
+ commitStyles: jest.fn(),
+});