123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import { assertSelectedElements, render } from "./test-utils";
- import ExcalidrawApp from "../excalidraw-app";
- import { Keyboard, Pointer, UI } from "./helpers/ui";
- import { API } from "./helpers/api";
- import { getDefaultAppState } from "../appState";
- import { waitFor } from "@testing-library/react";
- import { createUndoAction, createRedoAction } from "../actions/actionHistory";
- import { EXPORT_DATA_TYPES } from "../constants";
- const { h } = window;
- const mouse = new Pointer("mouse");
- describe("history", () => {
- it("initializing scene should end up with single history entry", async () => {
- await render(<ExcalidrawApp />, {
- localStorageData: {
- elements: [API.createElement({ type: "rectangle", id: "A" })],
- appState: {
- zenModeEnabled: true,
- },
- },
- });
- await waitFor(() => expect(h.state.zenModeEnabled).toBe(true));
- await waitFor(() =>
- expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]),
- );
- const undoAction = createUndoAction(h.history);
- const redoAction = createRedoAction(h.history);
- h.app.actionManager.executeAction(undoAction);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A", isDeleted: false }),
- ]);
- const rectangle = UI.createElement("rectangle");
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A" }),
- expect.objectContaining({ id: rectangle.id }),
- ]);
- h.app.actionManager.executeAction(undoAction);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A", isDeleted: false }),
- expect.objectContaining({ id: rectangle.id, isDeleted: true }),
- ]);
- // noop
- h.app.actionManager.executeAction(undoAction);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A", isDeleted: false }),
- expect.objectContaining({ id: rectangle.id, isDeleted: true }),
- ]);
- expect(API.getStateHistory().length).toBe(1);
- h.app.actionManager.executeAction(redoAction);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A", isDeleted: false }),
- expect.objectContaining({ id: rectangle.id, isDeleted: false }),
- ]);
- expect(API.getStateHistory().length).toBe(2);
- });
- it("scene import via drag&drop should create new history entry", async () => {
- await render(<ExcalidrawApp />, {
- localStorageData: {
- elements: [API.createElement({ type: "rectangle", id: "A" })],
- appState: {
- viewBackgroundColor: "#FFF",
- },
- },
- });
- await waitFor(() => expect(h.state.viewBackgroundColor).toBe("#FFF"));
- await waitFor(() =>
- expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]),
- );
- API.drop(
- new Blob(
- [
- JSON.stringify({
- type: EXPORT_DATA_TYPES.excalidraw,
- appState: {
- ...getDefaultAppState(),
- viewBackgroundColor: "#000",
- },
- elements: [API.createElement({ type: "rectangle", id: "B" })],
- }),
- ],
- { type: "application/json" },
- ),
- );
- await waitFor(() => expect(API.getStateHistory().length).toBe(2));
- expect(h.state.viewBackgroundColor).toBe("#000");
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "B", isDeleted: false }),
- ]);
- const undoAction = createUndoAction(h.history);
- const redoAction = createRedoAction(h.history);
- h.app.actionManager.executeAction(undoAction);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "A", isDeleted: false }),
- expect.objectContaining({ id: "B", isDeleted: true }),
- ]);
- expect(h.state.viewBackgroundColor).toBe("#FFF");
- h.app.actionManager.executeAction(redoAction);
- expect(h.state.viewBackgroundColor).toBe("#000");
- expect(h.elements).toEqual([
- expect.objectContaining({ id: "B", isDeleted: false }),
- expect.objectContaining({ id: "A", isDeleted: true }),
- ]);
- });
- it("undo/redo works properly with groups", async () => {
- await render(<ExcalidrawApp />);
- const rect1 = API.createElement({ type: "rectangle", groupIds: ["A"] });
- const rect2 = API.createElement({ type: "rectangle", groupIds: ["A"] });
- h.elements = [rect1, rect2];
- mouse.select(rect1);
- assertSelectedElements([rect1, rect2]);
- expect(h.state.selectedGroupIds).toEqual({ A: true });
- Keyboard.withModifierKeys({ ctrl: true }, () => {
- Keyboard.keyPress("d");
- });
- expect(h.elements.length).toBe(4);
- assertSelectedElements([h.elements[2], h.elements[3]]);
- expect(h.state.selectedGroupIds).not.toEqual(
- expect.objectContaining({ A: true }),
- );
- Keyboard.withModifierKeys({ ctrl: true }, () => {
- Keyboard.keyPress("z");
- });
- expect(h.elements.length).toBe(4);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: rect1.id, isDeleted: false }),
- expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: true }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: true }),
- ]);
- expect(h.state.selectedGroupIds).toEqual({ A: true });
- Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
- Keyboard.keyPress("z");
- });
- expect(h.elements.length).toBe(4);
- expect(h.elements).toEqual([
- expect.objectContaining({ id: rect1.id, isDeleted: false }),
- expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: false }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: false }),
- ]);
- expect(h.state.selectedGroupIds).not.toEqual(
- expect.objectContaining({ A: true }),
- );
- // undo again, and duplicate once more
- // -------------------------------------------------------------------------
- Keyboard.withModifierKeys({ ctrl: true }, () => {
- Keyboard.keyPress("z");
- Keyboard.keyPress("d");
- });
- expect(h.elements.length).toBe(6);
- expect(h.elements).toEqual(
- expect.arrayContaining([
- expect.objectContaining({ id: rect1.id, isDeleted: false }),
- expect.objectContaining({ id: rect2.id, isDeleted: false }),
- expect.objectContaining({ id: `${rect1.id}_copy`, isDeleted: true }),
- expect.objectContaining({ id: `${rect2.id}_copy`, isDeleted: true }),
- expect.objectContaining({
- id: `${rect1.id}_copy_copy`,
- isDeleted: false,
- }),
- expect.objectContaining({
- id: `${rect2.id}_copy_copy`,
- isDeleted: false,
- }),
- ]),
- );
- expect(h.state.selectedGroupIds).not.toEqual(
- expect.objectContaining({ A: true }),
- );
- });
- });
|