aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/tests/fitToContent.test.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/excalidraw/tests/fitToContent.test.tsx')
-rw-r--r--packages/excalidraw/tests/fitToContent.test.tsx191
1 files changed, 191 insertions, 0 deletions
diff --git a/packages/excalidraw/tests/fitToContent.test.tsx b/packages/excalidraw/tests/fitToContent.test.tsx
new file mode 100644
index 0000000..a4f0391
--- /dev/null
+++ b/packages/excalidraw/tests/fitToContent.test.tsx
@@ -0,0 +1,191 @@
+import React from "react";
+import { act, render } from "./test-utils";
+import { API } from "./helpers/api";
+
+import { Excalidraw } from "../index";
+import { vi } from "vitest";
+
+const { h } = window;
+
+const waitForNextAnimationFrame = () => {
+ return act(
+ () =>
+ new Promise((resolve) => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ }),
+ );
+};
+
+describe("fitToContent", () => {
+ it("should zoom to fit the selected element", async () => {
+ await render(<Excalidraw />);
+
+ h.state.width = 10;
+ h.state.height = 10;
+
+ const rectElement = API.createElement({
+ width: 50,
+ height: 100,
+ x: 50,
+ y: 100,
+ });
+
+ expect(h.state.zoom.value).toBe(1);
+
+ act(() => {
+ h.app.scrollToContent(rectElement, { fitToContent: true });
+ });
+
+ // element is 10x taller than the viewport size,
+ // zoom should be at least 1/10
+ expect(h.state.zoom.value).toBeLessThanOrEqual(0.1);
+ });
+
+ it("should zoom to fit multiple elements", async () => {
+ await render(<Excalidraw />);
+
+ const topLeft = API.createElement({
+ width: 20,
+ height: 20,
+ x: 0,
+ y: 0,
+ });
+
+ const bottomRight = API.createElement({
+ width: 20,
+ height: 20,
+ x: 80,
+ y: 80,
+ });
+
+ h.state.width = 10;
+ h.state.height = 10;
+
+ expect(h.state.zoom.value).toBe(1);
+
+ act(() => {
+ h.app.scrollToContent([topLeft, bottomRight], {
+ fitToContent: true,
+ });
+ });
+
+ // elements take 100x100, which is 10x bigger than the viewport size,
+ // zoom should be at least 1/10
+ expect(h.state.zoom.value).toBeLessThanOrEqual(0.1);
+ });
+
+ it("should scroll the viewport to the selected element", async () => {
+ await render(<Excalidraw />);
+
+ h.state.width = 10;
+ h.state.height = 10;
+
+ const rectElement = API.createElement({
+ width: 100,
+ height: 100,
+ x: 100,
+ y: 100,
+ });
+
+ expect(h.state.zoom.value).toBe(1);
+ expect(h.state.scrollX).toBe(0);
+ expect(h.state.scrollY).toBe(0);
+
+ act(() => {
+ h.app.scrollToContent(rectElement);
+ });
+
+ // zoom level should stay the same
+ expect(h.state.zoom.value).toBe(1);
+
+ // state should reflect some scrolling
+ expect(h.state.scrollX).not.toBe(0);
+ expect(h.state.scrollY).not.toBe(0);
+ });
+});
+
+describe("fitToContent animated", () => {
+ beforeEach(() => {
+ vi.spyOn(window, "requestAnimationFrame");
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it("should ease scroll the viewport to the selected element", async () => {
+ await render(<Excalidraw />);
+
+ h.state.width = 10;
+ h.state.height = 10;
+
+ const rectElement = API.createElement({
+ width: 100,
+ height: 100,
+ x: -100,
+ y: -100,
+ });
+
+ act(() => {
+ h.app.scrollToContent(rectElement, { animate: true });
+ });
+
+ expect(window.requestAnimationFrame).toHaveBeenCalled();
+
+ // Since this is an animation, we expect values to change through time.
+ // We'll verify that the scroll values change at 50ms and 100ms
+ expect(h.state.scrollX).toBe(0);
+ expect(h.state.scrollY).toBe(0);
+
+ await waitForNextAnimationFrame();
+
+ const prevScrollX = h.state.scrollX;
+ const prevScrollY = h.state.scrollY;
+
+ expect(h.state.scrollX).not.toBe(0);
+ expect(h.state.scrollY).not.toBe(0);
+
+ await waitForNextAnimationFrame();
+
+ expect(h.state.scrollX).not.toBe(prevScrollX);
+ expect(h.state.scrollY).not.toBe(prevScrollY);
+ });
+
+ it("should animate the scroll but not the zoom", async () => {
+ await render(<Excalidraw />);
+
+ h.state.width = 50;
+ h.state.height = 50;
+
+ const rectElement = API.createElement({
+ width: 100,
+ height: 100,
+ x: 100,
+ y: 100,
+ });
+
+ expect(h.state.scrollX).toBe(0);
+ expect(h.state.scrollY).toBe(0);
+
+ act(() => {
+ h.app.scrollToContent(rectElement, { animate: true, fitToContent: true });
+ });
+
+ expect(window.requestAnimationFrame).toHaveBeenCalled();
+
+ await waitForNextAnimationFrame();
+
+ const prevScrollX = h.state.scrollX;
+ const prevScrollY = h.state.scrollY;
+
+ expect(h.state.scrollX).not.toBe(0);
+ expect(h.state.scrollY).not.toBe(0);
+
+ await waitForNextAnimationFrame();
+
+ expect(h.state.scrollX).not.toBe(prevScrollX);
+ expect(h.state.scrollY).not.toBe(prevScrollY);
+ });
+});