mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
build: decouple package deps and introduce yarn workspaces (#7415)
* feat: decouple package deps and introduce yarn workspaces * update root directory * fix * fix scripts * fix lint * update path in scripts * remove yarn.lock files from packages * ignore workspace * dummy * dummy * remove comment check * revert workflow changes * ignore ws when installing gh actions * remove log * update path * fix * fix typo
This commit is contained in:
parent
b7d7ccc929
commit
d6cd8b78f1
567 changed files with 5066 additions and 8648 deletions
192
packages/excalidraw/tests/history.test.tsx
Normal file
192
packages/excalidraw/tests/history.test.tsx
Normal file
|
@ -0,0 +1,192 @@
|
|||
import { assertSelectedElements, render } from "./test-utils";
|
||||
import { Excalidraw } from "../index";
|
||||
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, MIME_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(
|
||||
<Excalidraw
|
||||
initialData={{
|
||||
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(
|
||||
<Excalidraw
|
||||
initialData={{
|
||||
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: MIME_TYPES.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(<Excalidraw handleKeyboardGlobally={true} />);
|
||||
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 }),
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue