Skip to content

Commit c910597

Browse files
add rollwright spec to cover motion+spring
1 parent 084e785 commit c910597

5 files changed

+144
-19
lines changed

motion.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const MS_PER_FRAME = 1000 / 60;
2+
const MAX_SKIP_FRAME = 10;
3+
14
export function motion(defs, callback, options) {
25
let updt = updateOf(defs);
36
let intp = interpolateOf(defs);

oscillation.spec.js

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { test as base } from "rollwright";
2+
import { expect } from "@playwright/test";
3+
4+
let test = base.extend({
5+
advance: async ({ execute }, use) => {
6+
let advance = await execute(() => {
7+
let timestamp = 0;
8+
window.performance = { now: () => timestamp };
9+
10+
let callbacks = {};
11+
let id = 0;
12+
window.requestAnimationFrame = (cb) => ((callbacks[id++] = cb), id);
13+
window.cancelAnimationFrame = (id) => delete callbacks[id];
14+
15+
function step(ms) {
16+
let cbs = callbacks;
17+
callbacks = {};
18+
timestamp += ms;
19+
for (let k in cbs) cbs[k](timestamp);
20+
}
21+
22+
return (steps, ms) => {
23+
for (let i = 0; i < steps; i++) step(ms);
24+
};
25+
});
26+
27+
await use((steps, ms) =>
28+
execute((advance, steps, ms) => advance(steps, ms), advance, steps, ms),
29+
);
30+
},
31+
});
32+
33+
test("interpolation with ideal timer condition", async ({ execute, advance }) => {
34+
let snapshots = await execute(async () => {
35+
let { motion } = await import("./motion.js");
36+
let { spring } = await import("./spring.js");
37+
let snapshots = [];
38+
let ctl = new AbortController();
39+
motion(spring(0, 10), (x) => snapshots.push(x), { signal: ctl.signal });
40+
return snapshots;
41+
});
42+
43+
await advance(1, 0);
44+
await advance(5, 1000 / 60);
45+
46+
expect(await snapshots.jsonValue()).toEqual([
47+
0, 0.47241496285986795, 1.1901835641718526, 2.0130615470443205, 2.8566207208531145,
48+
3.6720468997166984,
49+
]);
50+
});
51+
52+
test("eventual interpolation completeness", async ({ execute, advance }) => {
53+
let snapshots = await execute(async () => {
54+
let { motion } = await import("./motion.js");
55+
let { spring } = await import("./spring.js");
56+
let snapshots = [];
57+
let ctl = new AbortController();
58+
motion(spring(0, 10), (x) => snapshots.push(x), { signal: ctl.signal });
59+
return snapshots;
60+
});
61+
62+
await advance(1, 0);
63+
await advance(69, 1000 / 60);
64+
65+
expect((await snapshots.jsonValue()).at(-1)).toBe(10);
66+
});
67+
68+
test("interpolation with inconsistent timer condition", async ({ execute, advance }) => {
69+
let snapshots = await execute(async () => {
70+
let { motion } = await import("./motion.js");
71+
let { spring } = await import("./spring.js");
72+
let snapshots = [];
73+
let ctl = new AbortController();
74+
motion(spring(0, 10), (x) => snapshots.push(x), { signal: ctl.signal });
75+
return snapshots;
76+
});
77+
78+
await advance(1, 0);
79+
expect(await snapshots.jsonValue()).toEqual([0]);
80+
81+
await advance(1, (1000 / 60) * 11);
82+
expect(await snapshots.jsonValue()).toEqual([0]);
83+
84+
await advance(3, 0.1);
85+
expect(await snapshots.jsonValue()).toEqual([
86+
0, 0.0028344897771590463, 0.0056689795543180925, 0.008503469331477139,
87+
]);
88+
89+
await advance(10, 999);
90+
expect(await snapshots.jsonValue()).toEqual([
91+
0, 0.0028344897771590463, 0.0056689795543180925, 0.008503469331477139,
92+
]);
93+
94+
await advance(2, 36);
95+
expect(await snapshots.jsonValue()).toEqual([
96+
0, 0.0028344897771590463, 0.0056689795543180925, 0.008503469331477139, 1.3218440414314472,
97+
3.117557098089461,
98+
]);
99+
});
100+
101+
test("explicit motion cancellation", async ({ execute, advance }) => {
102+
let ctl = await execute(() => new AbortController());
103+
104+
let snapshots = await execute(async (ctl) => {
105+
let { motion } = await import("./motion.js");
106+
let { spring } = await import("./spring.js");
107+
let snapshots = [];
108+
motion(spring(-10, -100), (x) => snapshots.push(x), { signal: ctl.signal });
109+
return snapshots;
110+
}, ctl);
111+
112+
await advance(1, 0);
113+
await advance(3, 1000 / 60);
114+
115+
await execute((ctl) => ctl.abort(), ctl);
116+
await advance(2, 1000 / 60);
117+
118+
expect(await snapshots.jsonValue()).toEqual([
119+
-10, -14.251734665738812, -20.711652077546674, -28.117553923398887,
120+
]);
121+
});

package.json

+7-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
{
2-
"workspaces": [
3-
"packages/*"
4-
],
5-
"devDependencies": {
6-
"@babel/preset-env": "^7.18.6",
7-
"@babel/preset-react": "^7.18.6",
8-
"@rollup/plugin-multi-entry": "^4.1.0",
9-
"babel-jest": "^28.1.1",
10-
"jest": "^28.1.1",
11-
"prettier": "^2.7.1",
12-
"rollup": "^2.55.1",
13-
"rollup-plugin-auto-external": "^2.0.0",
14-
"rollup-plugin-bundle-size": "^1.0.3",
15-
"rollup-plugin-copy": "^3.4.0"
16-
},
2+
"type": "module",
173
"prettier": {
184
"printWidth": 100,
195
"trailingComma": "all",
206
"proseWrap": "always"
7+
},
8+
"devDependencies": {
9+
"@playwright/test": "^1.48.1",
10+
"prettier": "^3.3.3",
11+
"rollup": "^4.24.0",
12+
"rollwright": "^0.0.6"
2113
}
2214
}

playwright.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from "@playwright/test";
2+
3+
export default defineConfig({
4+
use: { headless: false },
5+
testMatch: /.*\.spec\.js/,
6+
reporter: [["list"], ["rollwright/coverage-reporter"]],
7+
});

spring.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const SEC_PER_FRAME = 1 / 60;
2+
13
export let springs = {
24
noWobble: { damping: 0.997, frequency: 0.4818, precision: 0.01 },
35
gentle: { damping: 0.639, frequency: 0.5735, precision: 0.01 },
@@ -34,8 +36,8 @@ function singlespring(current, destination, damping, stiffness, precision) {
3436
return true;
3537
},
3638
interpolate(t) {
37-
let next = step(current, velocity, destination, damping, stiffness, precision);
38-
return current + (next[0] - current) * t;
39+
let tuple = step(current, velocity, destination, damping, stiffness, precision);
40+
return current + (tuple[0] - current) * t;
3941
},
4042
};
4143
}
@@ -58,11 +60,11 @@ function multispring(current, destination, damping, stiffness, precision) {
5860
}
5961
return true;
6062
},
61-
interpolate() {
63+
interpolate(t) {
6264
let tuple;
6365
for (let i = 0; i < current.length; i++) {
6466
tuple = step(current[i], velocity[i], destination[i], damping, stiffness, precision);
65-
interpolated[i] = tuple[0];
67+
interpolated[i] = current[i] + (tuple[0] - current[i]) * t;
6668
}
6769
return interpolated;
6870
},

0 commit comments

Comments
 (0)