返回 DeepSeek-Reasonix
resize-drag.test.ts
根目录 / desktop / frontend / src / __tests__ / resize-drag.test.ts
1 // Run: tsx src/__tests__/resize-drag.test.ts
2
3 import { createRafResizeUpdater } from "../lib/resizeDrag";
4
5 let passed = 0;
6 let failed = 0;
7
8 function eq(a: unknown, b: unknown, label: string) {
9 if (a === b) {
10 process.stdout.write(` PASS ${label}\n`);
11 passed += 1;
12 } else {
13 process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`);
14 failed += 1;
15 }
16 }
17
18 console.log("\nresizeDrag");
19
20 const frames: FrameRequestCallback[] = [];
21 const originalRequestAnimationFrame = globalThis.requestAnimationFrame;
22 const originalCancelAnimationFrame = globalThis.cancelAnimationFrame;
23 globalThis.requestAnimationFrame = ((callback: FrameRequestCallback) => {
24 frames.push(callback);
25 return frames.length;
26 }) as typeof requestAnimationFrame;
27 globalThis.cancelAnimationFrame = ((id: number) => {
28 frames[id - 1] = () => undefined;
29 }) as typeof cancelAnimationFrame;
30
31 try {
32 const applied: Array<[string, string]> = [];
33 const attrs: Record<string, string> = {};
34 const updater = createRafResizeUpdater({
35 target: {
36 style: {
37 setProperty(name: string, value: string) {
38 applied.push([name, value]);
39 },
40 },
41 },
42 separator: {
43 setAttribute(name: string, value: string) {
44 attrs[name] = value;
45 },
46 },
47 cssVar: "--pane-width",
48 });
49
50 updater.schedule(101.2);
51 updater.schedule(148.7);
52 eq(applied.length, 0, "live resize waits for animation frame");
53 eq(frames.length, 1, "multiple pointer moves share one animation frame");
54 frames[0](0);
55 eq(applied.length, 1, "one frame applies one live resize");
56 eq(applied[0]?.[0], "--pane-width", "live resize writes the configured CSS variable");
57 eq(applied[0]?.[1], "149px", "live resize applies the latest rounded pixel value");
58 eq(attrs["aria-valuenow"], "149", "live resize keeps separator ARIA in sync");
59
60 frames.length = 0;
61 const flushed: Array<[string, string]> = [];
62 const flushUpdater = createRafResizeUpdater({
63 target: {
64 style: {
65 setProperty(name: string, value: string) {
66 flushed.push([name, value]);
67 },
68 },
69 },
70 cssVar: "--pane-width",
71 });
72 flushUpdater.schedule(600);
73 frames[0](0);
74 flushUpdater.schedule(420);
75 eq(flushed[flushed.length - 1]?.[1], "600px", "pending final resize waits for animation frame before flush");
76 flushUpdater.flush();
77 eq(flushed[flushed.length - 1]?.[1], "420px", "flush applies the final resize before React state commits");
78
79 frames.length = 0;
80 const appliedValues: number[] = [];
81 const callbackUpdater = createRafResizeUpdater({
82 target: {
83 style: {
84 setProperty() {
85 // CSS write is covered above; this block checks the synchronized callback contract.
86 },
87 },
88 },
89 cssVar: "--pane-width",
90 onApply(value) {
91 appliedValues.push(value);
92 },
93 });
94 callbackUpdater.schedule(279.7);
95 callbackUpdater.schedule(300.2);
96 eq(appliedValues.length, 0, "live resize callback waits for animation frame");
97 frames[0](0);
98 eq(appliedValues.join(","), "300", "live resize callback receives the applied rounded value");
99 } finally {
100 globalThis.requestAnimationFrame = originalRequestAnimationFrame;
101 globalThis.cancelAnimationFrame = originalCancelAnimationFrame;
102 }
103
104 console.log(`\n${passed} passed, ${failed} failed, ${passed + failed} total`);
105 if (failed > 0) process.exit(1);
106
106 lines TYPESCRIPT