Skip to content

Motion e2e tests#131

Open
marine-bre wants to merge 22 commits intomasterfrom
motion-e2e
Open

Motion e2e tests#131
marine-bre wants to merge 22 commits intomasterfrom
motion-e2e

Conversation

@marine-bre
Copy link
Contributor

Description

Related Issue

Checklist

  • I have read the Contributing Guide
  • I have added/updated tests for my changes (if applicable)
  • I have updated documentation/rules/skills (if applicable)

Screenshots / Demos

Additional Notes

@marine-bre marine-bre marked this pull request as ready for review February 25, 2026 09:53
@marine-bre marine-bre changed the title initial commit Motion e2e tests Feb 25, 2026
Copy link
Collaborator

@ydaniv ydaniv left a comment

Choose a reason for hiding this comment

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

So far reviewed animation-group, effects, and pointer-animations.
Let's fix those and continue

"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.28.6":
version: 7.28.6
resolution: "@babel/code-frame@npm:7.28.6"
"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like we have misaligned versions in the different package.json files.
I can see one is @vitejs/plugin-react.
Please see if you can dedupe those.

) as HTMLElement;

// X-axis animation: translateX driven by horizontal mouse position
const xAxisGroup = getWebAnimation(xAxisTarget, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

All of the pointer fixtures use getWebAnimation(), none actually use getScrubScene() which is what we actually use for all pointer-move animations.
So none actually check the correct code path for pointer animations.
Let's correct that, so it's actually passing the correct trigger data, with different axes, using all the type of effects, etc.

return Math.max(0, Math.min(1, progress));
}

const animationGroup = getWebAnimation(target, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like none of the examples in here actually uses native scroll-driven animations.
Try asking cursor to test both native SDAs and using getScrubScene which uses a polyfill.

await scrollPage.goto();
});

test.describe('View Progress Trigger', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This suite should attempt testing native SDAs if supported, and also using getScrubScene

});
});

test.describe('Scrub Scene', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Motion allows calling getScrubScene both when ViewTimeline is supported natively and when not.
Then native use-case is for customEffects. Could be nice to test that.
The non-native support is also for the simple polyfill flow of simple named/keyframe effects, should be tested as well.

Comment on lines +21 to +22
const progressAfterScroll = await scrollPage.getScrollProgress();
expect(progressAfterScroll).toBeGreaterThan(0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not testing the animation in any way. It's just checking scroll progress which it gets from measuring stuff from the page.

Comment on lines +156 to +157
(window as ScrollFixtureWindow).getScrubSceneMode = () =>
Array.isArray(rangeSceneResult) ? 'polyfill' : 'native';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could easily break if we change implementation. Let's change it to check having scrub scenes and supporting ViewTimeline or not. Should be enough

Comment on lines +32 to +33
expect(sceneStart).toBeNull();
expect(sceneEnd).toBeNull();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is also silly, probably because the scene is an array

test.skip(!supportsNative, 'Native ViewTimeline is required for this customEffect flow.');

await scrollPage.scrollTo(0);
await scrollPage.wait(60);
Copy link
Collaborator

Choose a reason for hiding this comment

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

See if we can generally avoid calls to wait and ask Cursor to scroll and check ASAP. All those waits add up to slow tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, it suggested to use playwright's toPass(), which is a retrying assertion. Seems to do the trick!

expect(after.shift).toBeGreaterThan(before.shift);
});

test('should expose getScrubScene range offsets in fallback metadata', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what this test checks... any idea?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not really e2e material, I removed it


test('should handle destroy cleanup properly', async ({ page }) => {
await scrollPage.cancelScrubScene();
await waitForWindowPlayState(page, 'scrubScene', ['idle', 'paused'], 5000);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we want to only allow for idle there. Please check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

paused was allowed because there was a race condition between setting idle state via animationGroup.cancel() and the scroll listener callback that was still setting progress. I added a flag scrubSceneDestroyed to avoid this and only enable idle.

@marine-bre marine-bre requested a review from ydaniv March 15, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants