Skip to content

Commit 025a10a

Browse files
committed
test(regressions): add regression test suite for 46 reported bugs
Deterministic test harness with uniform interpolation for velocity, 9 regression spec files covering all testable open bugs. Results: 21 FAILING (active bugs), 25 PASSING (fixed in v4). 11 bugs remain N/A (not testable in jsdom). Closes #168 Closes #241 Closes #250 Closes #286 Closes #323 Closes #364 Closes #392 Closes #404 Closes #406 Closes #408 Closes #423 Closes #427 Closes #431 Closes #432 Closes #463 Closes #479 Closes #483 Closes #487 Closes #495 Closes #498 Closes #524 Closes #547 Ref #112 Ref #283 Ref #316 Ref #363 Ref #396 Ref #418 Ref #434 Ref #437 Ref #438 Ref #439 Ref #443 Ref #460 Ref #462 Ref #467 Ref #506 Ref #508 Ref #516 Ref #529 Ref #538 Ref #545 Ref #553 Made-with: Cursor
1 parent 72e72f7 commit 025a10a

67 files changed

Lines changed: 1345 additions & 156 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/testing-regressions.mdc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ alwaysApply: false
3030
- Prefer **moveEventCount** (or the project-standard name) over vague steps in
3131
public test helper types.
3232

33+
## Running tests
34+
35+
The project uses **jest-watch-typeahead** (`jest.config.ts` → `watchPlugins`).
36+
In watch mode (`yarn test --watch`) press:
37+
38+
- **`p`** — filter by **filename** (e.g. type `pan.velocity` to run only that spec)
39+
- **`t`** — filter by **test name** (e.g. type `should trigger velocity`)
40+
41+
Use these to quickly iterate on a single spec without running the full suite.
42+
43+
To run a single file directly: `yarn test __tests__/features/pan/pan.velocity.spec.tsx`
44+
3345
## General
3446

3547
- Match existing test layout under **tests**/features/ for broad coverage; use

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ yarn-error.log*
4848
Thumbs.db
4949

5050
# testing
51-
coverage
51+
coverage
52+
jest-reg.txtjest-reg.txt
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { waitFor, fireEvent } from "@testing-library/react";
2+
3+
import { renderApp } from "../utils";
4+
5+
const NativeResizeObserver = global.ResizeObserver;
6+
7+
beforeAll(() => {
8+
global.ResizeObserver = class {
9+
observe() {}
10+
disconnect() {}
11+
unobserve() {}
12+
} as unknown as typeof ResizeObserver;
13+
});
14+
15+
afterAll(() => {
16+
global.ResizeObserver = NativeResizeObserver;
17+
});
18+
19+
describe("bounds and centering regressions", () => {
20+
it("maxPositionX clamps horizontal pan when set (Ref #250)", () => {
21+
const { pan, ref } = renderApp({
22+
maxPositionX: 50,
23+
limitToBounds: true,
24+
disablePadding: true,
25+
});
26+
ref.current!.setTransform(0, 0, 2);
27+
pan({ x: 200, y: 0 });
28+
expect(ref.current!.instance.state.positionX).toBeLessThanOrEqual(50);
29+
});
30+
31+
it("touchpad zoom-out respects minScale / limitToBounds (Ref #396)", () => {
32+
const { content, ref } = renderApp({
33+
minScale: 0.5,
34+
limitToBounds: true,
35+
smooth: false,
36+
});
37+
38+
for (let i = 0; i < 50; i++) {
39+
fireEvent(
40+
content,
41+
new WheelEvent("wheel", {
42+
bubbles: true,
43+
deltaY: 30,
44+
ctrlKey: true,
45+
deltaMode: 0,
46+
}),
47+
);
48+
}
49+
50+
expect(ref.current!.instance.state.scale).toBeGreaterThanOrEqual(0.5);
51+
});
52+
53+
it("tall zoomed content can pan far enough upward (negative positionY) (Ref #524)", () => {
54+
const { pan, ref } = renderApp({
55+
contentHeight: "2000px",
56+
wrapperHeight: "500px",
57+
limitToBounds: true,
58+
disablePadding: true,
59+
});
60+
ref.current!.setTransform(0, 0, 2);
61+
pan({ x: 0, y: -600 });
62+
expect(ref.current!.instance.state.positionY).toBeLessThan(-1);
63+
});
64+
65+
it("centerOnInit yields centered translation after mount (Ref #392)", async () => {
66+
const { ref } = renderApp({
67+
centerOnInit: true,
68+
contentHeight: "2000px",
69+
wrapperHeight: "500px",
70+
limitToBounds: false,
71+
});
72+
await waitFor(() => {
73+
const { positionX, positionY } = ref.current!.instance.state;
74+
expect(positionX !== 0 || positionY !== 0).toBe(true);
75+
});
76+
});
77+
78+
it("centering accounts for wrapper offset from viewport (Ref #462)", () => {
79+
const { ref, wrapper } = renderApp({
80+
centerOnInit: true,
81+
limitToBounds: false,
82+
});
83+
84+
jest.spyOn(wrapper, "getBoundingClientRect").mockReturnValue({
85+
width: 500,
86+
height: 500,
87+
top: 200,
88+
left: 150,
89+
bottom: 700,
90+
right: 650,
91+
x: 150,
92+
y: 200,
93+
toJSON: () => ({}),
94+
} as DOMRect);
95+
96+
ref.current!.centerView(1, 0);
97+
98+
const { positionX, positionY } = ref.current!.instance.state;
99+
expect(positionX !== 0 || positionY !== 0).toBe(true);
100+
});
101+
102+
it("panning resumes after hitting bounds and reversing direction (Ref #316)", () => {
103+
const { pan, ref } = renderApp({
104+
limitToBounds: true,
105+
disablePadding: true,
106+
});
107+
ref.current!.setTransform(0, 0, 2);
108+
pan({ x: 1000, y: 0 });
109+
const posAfterRight = ref.current!.instance.state.positionX;
110+
111+
pan({ x: -500, y: 0, from: { clientX: 250, clientY: 250 } });
112+
const posAfterLeft = ref.current!.instance.state.positionX;
113+
114+
expect(posAfterLeft).toBeLessThan(posAfterRight);
115+
});
116+
117+
it("initialPositionX is applied on first paint (Ref #483)", async () => {
118+
const { ref } = renderApp({
119+
initialPositionX: 100,
120+
limitToBounds: false,
121+
});
122+
await waitFor(() => {
123+
expect(ref.current!.instance.state.positionX).toBe(100);
124+
});
125+
});
126+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
4+
describe("build and packaging regressions", () => {
5+
it("package.json files field includes src/ for source map resolution (Ref #529)", () => {
6+
const pkgPath = path.resolve(__dirname, "../../package.json");
7+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
8+
const files: string[] = pkg.files || [];
9+
const includesSrc = files.some(
10+
(f: string) => f === "src" || f.startsWith("src/"),
11+
);
12+
expect(includesSrc).toBe(true);
13+
});
14+
});

0 commit comments

Comments
 (0)