refactor: move excalidraw-app outside src (#6987)

* refactor: move excalidraw-app outside src

* move some tests to excal app and fix some

* fix tests

* fix

* port remaining tests

* fix

* update snap

* move tests inside test folder

* fix

* fix
This commit is contained in:
Aakansha Doshi
2023-09-21 09:28:48 +05:30
committed by GitHub
parent 0a588a880b
commit 741d5f1a18
63 changed files with 638 additions and 415 deletions

46
src/tests/App.test.tsx Normal file
View File

@ -0,0 +1,46 @@
import ReactDOM from "react-dom";
import * as Renderer from "../renderer/renderScene";
import { reseed } from "../random";
import { render, queryByTestId } from "../tests/test-utils";
import { Excalidraw } from "../packages/excalidraw/index";
import { vi } from "vitest";
const renderStaticScene = vi.spyOn(Renderer, "renderStaticScene");
describe("Test <App/>", () => {
beforeEach(async () => {
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
localStorage.clear();
renderStaticScene.mockClear();
reseed(7);
});
it("should show error modal when using brave and measureText API is not working", async () => {
(global.navigator as any).brave = {
isBrave: {
name: "isBrave",
},
};
const originalContext = global.HTMLCanvasElement.prototype.getContext("2d");
//@ts-ignore
global.HTMLCanvasElement.prototype.getContext = (contextId) => {
return {
...originalContext,
measureText: () => ({
width: 0,
}),
};
};
await render(<Excalidraw />);
expect(
queryByTestId(
document.querySelector(".excalidraw-modal-container")!,
"brave-measure-text-error",
),
).toMatchSnapshot();
});
});

View File

@ -1,45 +0,0 @@
import ExcalidrawApp from "../excalidraw-app";
import {
mockBoundingClientRect,
render,
restoreOriginalGetBoundingClientRect,
} from "./test-utils";
import { UI } from "./helpers/ui";
describe("Test MobileMenu", () => {
const { h } = window;
const dimensions = { height: 400, width: 800 };
beforeAll(() => {
mockBoundingClientRect(dimensions);
});
beforeEach(async () => {
await render(<ExcalidrawApp />);
//@ts-ignore
h.app.refreshDeviceState(h.app.excalidrawContainerRef.current!);
});
afterAll(() => {
restoreOriginalGetBoundingClientRect();
});
it("should set device correctly", () => {
expect(h.app.device).toMatchInlineSnapshot(`
{
"canDeviceFitSidebar": false,
"isLandscape": true,
"isMobile": true,
"isSmScreen": false,
"isTouchScreen": false,
}
`);
});
it("should initialize with welcome screen and hide once user interacts", async () => {
expect(document.querySelector(".welcome-screen-center")).toMatchSnapshot();
UI.clickTool("rectangle");
expect(document.querySelector(".welcome-screen-center")).toBeNull();
});
});

View File

@ -0,0 +1,50 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Test <App/> > should show error modal when using brave and measureText API is not working 1`] = `
<div
data-testid="brave-measure-text-error"
>
<p>
Looks like you are using Brave browser with the
<span
style="font-weight: 600;"
>
Aggressively Block Fingerprinting
</span>
setting enabled.
</p>
<p>
This could result in breaking the
<span
style="font-weight: 600;"
>
Text Elements
</span>
in your drawings.
</p>
<p>
We strongly recommend disabling this setting. You can follow
<a
href="http://docs.excalidraw.com/docs/@excalidraw/excalidraw/faq#turning-off-aggresive-block-fingerprinting-in-brave-browser"
>
these steps
</a>
on how to do so.
</p>
<p>
If disabling this setting doesn't fix the display of text elements, please open an
<a
href="https://github.com/excalidraw/excalidraw/issues/new"
>
issue
</a>
on our GitHub, or write us on
<a
href="https://discord.gg/UexuTaE"
>
Discord
.
</a>
</p>
</div>
`;

View File

@ -13089,126 +13089,6 @@ exports[`regression tests > pinch-to-zoom works > [end of test] number of elemen
exports[`regression tests > pinch-to-zoom works > [end of test] number of renders 1`] = `7`;
exports[`regression tests > rerenders UI on language change > [end of test] appState 1`] = `
{
"activeEmbeddable": null,
"activeTool": {
"customType": null,
"lastActiveTool": null,
"locked": false,
"type": "rectangle",
},
"collaborators": Map {},
"contextMenu": null,
"currentChartType": "bar",
"currentItemBackgroundColor": "transparent",
"currentItemEndArrowhead": "arrow",
"currentItemFillStyle": "hachure",
"currentItemFontFamily": 1,
"currentItemFontSize": 20,
"currentItemOpacity": 100,
"currentItemRoughness": 1,
"currentItemRoundness": "round",
"currentItemStartArrowhead": null,
"currentItemStrokeColor": "#1e1e1e",
"currentItemStrokeStyle": "solid",
"currentItemStrokeWidth": 1,
"currentItemTextAlign": "left",
"cursorButton": "up",
"defaultSidebarDockedPreference": false,
"draggingElement": null,
"editingElement": null,
"editingFrame": null,
"editingGroupId": null,
"editingLinearElement": null,
"elementsToHighlight": null,
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"frameRendering": {
"clip": true,
"enabled": true,
"name": true,
"outline": true,
},
"frameToHighlight": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLoading": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
"multiElement": null,
"name": "Untitled-201933152653",
"offsetLeft": 0,
"offsetTop": 0,
"openDialog": null,
"openMenu": "canvas",
"openPopup": null,
"openSidebar": null,
"pasteDialog": {
"data": null,
"shown": false,
},
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": {},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": {},
"selectedLinearElement": null,
"selectionElement": null,
"shouldCacheIgnoreZoom": false,
"showHyperlinkPopup": false,
"showStats": false,
"showWelcomeScreen": true,
"startBoundElement": null,
"suggestedBindings": [],
"theme": "light",
"toast": null,
"viewBackgroundColor": "#ffffff",
"viewModeEnabled": false,
"width": 1024,
"zenModeEnabled": false,
"zoom": {
"value": 1,
},
}
`;
exports[`regression tests > rerenders UI on language change > [end of test] history 1`] = `
{
"recording": false,
"redoStack": [],
"stateHistory": [
{
"appState": {
"editingGroupId": null,
"editingLinearElement": null,
"name": "Untitled-201933152653",
"selectedElementIds": {},
"selectedGroupIds": {},
"viewBackgroundColor": "#ffffff",
},
"elements": [],
},
],
}
`;
exports[`regression tests > rerenders UI on language change > [end of test] number of elements 1`] = `0`;
exports[`regression tests > rerenders UI on language change > [end of test] number of renders 1`] = `5`;
exports[`regression tests > shift click on selected element should deselect it on pointer up > [end of test] appState 1`] = `
{
"activeEmbeddable": null,

View File

@ -0,0 +1,83 @@
import { Excalidraw } from "../packages/excalidraw/index";
import { CODES } from "../keys";
import { API } from "../tests/helpers/api";
import { Keyboard, Pointer, UI } from "../tests/helpers/ui";
import {
act,
fireEvent,
render,
screen,
togglePopover,
} from "../tests/test-utils";
import { copiedStyles } from "../actions/actionStyles";
const { h } = window;
const mouse = new Pointer("mouse");
describe("actionStyles", () => {
beforeEach(async () => {
await render(<Excalidraw handleKeyboardGlobally={true} />);
});
afterEach(async () => {
// https://github.com/floating-ui/floating-ui/issues/1908#issuecomment-1301553793
// affects node v16+
await act(async () => {});
});
it("should copy & paste styles via keyboard", async () => {
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
// Change some styles of second rectangle
togglePopover("Stroke");
UI.clickOnTestId("color-red");
togglePopover("Background");
UI.clickOnTestId("color-blue");
// Fill style
fireEvent.click(screen.getByTitle("Cross-hatch"));
// Stroke width
fireEvent.click(screen.getByTitle("Bold"));
// Stroke style
fireEvent.click(screen.getByTitle("Dotted"));
// Roughness
fireEvent.click(screen.getByTitle("Cartoonist"));
// Opacity
fireEvent.change(screen.getByLabelText("Opacity"), {
target: { value: "60" },
});
mouse.reset();
API.setSelectedElements([h.elements[1]]);
Keyboard.withModifierKeys({ ctrl: true, alt: true }, () => {
Keyboard.codeDown(CODES.C);
});
const secondRect = JSON.parse(copiedStyles)[0];
expect(secondRect.id).toBe(h.elements[1].id);
mouse.reset();
// Paste styles to first rectangle
API.setSelectedElements([h.elements[0]]);
Keyboard.withModifierKeys({ ctrl: true, alt: true }, () => {
Keyboard.codeDown(CODES.V);
});
const firstRect = API.getSelectedElement();
expect(firstRect.id).toBe(h.elements[0].id);
expect(firstRect.strokeColor).toBe("#e03131");
expect(firstRect.backgroundColor).toBe("#a5d8ff");
expect(firstRect.fillStyle).toBe("cross-hatch");
expect(firstRect.strokeWidth).toBe(2); // Bold: 2
expect(firstRect.strokeStyle).toBe("dotted");
expect(firstRect.roughness).toBe(2); // Cartoonist: 2
expect(firstRect.opacity).toBe(60);
});
});

View File

@ -1,6 +1,6 @@
import ReactDOM from "react-dom";
import { render } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../../src/packages/excalidraw/index";
import { defaultLang, setLanguage } from "../i18n";
import { UI, Pointer, Keyboard } from "./helpers/ui";
import { API } from "./helpers/api";
@ -60,7 +60,7 @@ describe("aligning", () => {
mouse.reset();
await setLanguage(defaultLang);
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
});
it("aligns two objects correctly to the top", () => {

View File

@ -1,6 +1,6 @@
import { queryByTestId, render, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api";
import { getDefaultAppState } from "../appState";
import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
@ -14,14 +14,17 @@ describe("appState", () => {
const defaultAppState = getDefaultAppState();
const exportBackground = !defaultAppState.exportBackground;
await render(<ExcalidrawApp />, {
localStorageData: {
appState: {
exportBackground,
viewBackgroundColor: "#F00",
},
},
});
await render(
<Excalidraw
initialData={{
appState: {
exportBackground,
viewBackgroundColor: "#F00",
},
}}
/>,
{},
);
await waitFor(() => {
expect(h.state.exportBackground).toBe(exportBackground);
@ -53,13 +56,15 @@ describe("appState", () => {
});
it("changing fontSize with text tool selected (no element created yet)", async () => {
const { container } = await render(<ExcalidrawApp />, {
localStorageData: {
appState: {
currentItemFontSize: 30,
},
},
});
const { container } = await render(
<Excalidraw
initialData={{
appState: {
currentItemFontSize: 30,
},
}}
/>,
);
UI.clickTool("text");

View File

@ -1,5 +1,5 @@
import { fireEvent, render } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../../src/packages/excalidraw/index";
import { UI, Pointer, Keyboard } from "./helpers/ui";
import { getTransformHandles } from "../element/transformHandles";
import { API } from "./helpers/api";
@ -12,7 +12,7 @@ const mouse = new Pointer("mouse");
describe("element binding", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
});
it("should create valid binding if duplicate start/end points", async () => {

View File

@ -7,7 +7,7 @@ import {
createPasteEvent,
} from "./test-utils";
import { Pointer, Keyboard } from "./helpers/ui";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { KEYS } from "../keys";
import {
getDefaultLineHeight,
@ -79,8 +79,13 @@ beforeEach(async () => {
mouse.reset();
await render(<ExcalidrawApp />);
h.app.setAppState({ zoom: { value: 1 as NormalizedZoomValue } });
await render(
<Excalidraw
autoFocus={true}
handleKeyboardGlobally={true}
initialData={{ appState: { zoom: { value: 1 as NormalizedZoomValue } } }}
/>,
);
setClipboardText("");
Object.assign(document, {
elementFromPoint: () => GlobalTestState.canvas,
@ -91,7 +96,6 @@ describe("general paste behavior", () => {
it("should randomize seed on paste", async () => {
const rectangle = API.createElement({ type: "rectangle" });
const clipboardJSON = (await copyToClipboard([rectangle], null))!;
pasteWithCtrlCmdV(clipboardJSON);
await waitFor(() => {

View File

@ -1,101 +0,0 @@
import { vi } from "vitest";
import { render, updateSceneData, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
import { createUndoAction } from "../actions/actionHistory";
const { h } = window;
Object.defineProperty(window, "crypto", {
value: {
getRandomValues: (arr: number[]) =>
arr.forEach((v, i) => (arr[i] = Math.floor(Math.random() * 256))),
subtle: {
generateKey: () => {},
exportKey: () => ({ k: "sTdLvMC_M3V8_vGa3UVRDg" }),
},
},
});
vi.mock("../excalidraw-app/data/index.ts", async (importActual) => {
const module = (await importActual()) as any;
return {
__esmodule: true,
...module,
getCollabServer: vi.fn(() => ({
url: /* doesn't really matter */ "http://localhost:3002",
})),
};
});
vi.mock("../excalidraw-app/data/firebase.ts", () => {
const loadFromFirebase = async () => null;
const saveToFirebase = () => {};
const isSavedToFirebase = () => true;
const loadFilesFromFirebase = async () => ({
loadedFiles: [],
erroredFiles: [],
});
const saveFilesToFirebase = async () => ({
savedFiles: new Map(),
erroredFiles: new Map(),
});
return {
loadFromFirebase,
saveToFirebase,
isSavedToFirebase,
loadFilesFromFirebase,
saveFilesToFirebase,
};
});
vi.mock("socket.io-client", () => {
return {
default: () => {
return {
close: () => {},
on: () => {},
once: () => {},
off: () => {},
emit: () => {},
};
},
};
});
describe("collaboration", () => {
it("creating room should reset deleted elements", async () => {
await render(<ExcalidrawApp />);
// To update the scene with deleted elements before starting collab
updateSceneData({
elements: [
API.createElement({ type: "rectangle", id: "A" }),
API.createElement({
type: "rectangle",
id: "B",
isDeleted: true,
}),
],
});
await waitFor(() => {
expect(h.elements).toEqual([
expect.objectContaining({ id: "A" }),
expect.objectContaining({ id: "B", isDeleted: true }),
]);
expect(API.getStateHistory().length).toBe(1);
});
window.collab.startCollaboration(null);
await waitFor(() => {
expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]);
expect(API.getStateHistory().length).toBe(1);
});
const undoAction = createUndoAction(h.history);
// noop
h.app.actionManager.executeAction(undoAction);
await waitFor(() => {
expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]);
expect(API.getStateHistory().length).toBe(1);
});
});
});

View File

@ -11,7 +11,7 @@ import {
waitFor,
togglePopover,
} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import * as Renderer from "../renderer/renderScene";
import { reseed } from "../random";
import { UI, Pointer, Keyboard } from "./helpers/ui";
@ -20,7 +20,6 @@ import { ShortcutName } from "../actions/shortcuts";
import { copiedStyles } from "../actions/actionStyles";
import { API } from "./helpers/api";
import { setDateTimeForTests } from "../utils";
import { LibraryItem } from "../types";
import { vi } from "vitest";
const checkpoint = (name: string) => {
@ -56,7 +55,7 @@ describe("contextMenu element", () => {
reseed(7);
setDateTimeForTests("201933152653");
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
});
beforeAll(() => {
@ -394,11 +393,9 @@ describe("contextMenu element", () => {
const contextMenu = UI.queryContextMenu();
fireEvent.click(queryByText(contextMenu!, "Add to library")!);
await waitFor(() => {
const library = localStorage.getItem("excalidraw-library");
expect(library).not.toBeNull();
const addedElement = JSON.parse(library!)[0] as LibraryItem;
expect(addedElement.elements[0]).toEqual(h.elements[0]);
await waitFor(async () => {
const libraryItems = await h.app.library.getLatestLibrary();
expect(libraryItems[0].elements[0]).toEqual(h.elements[0]);
});
});

View File

@ -1,5 +1,5 @@
import ReactDOM from "react-dom";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
import {
@ -30,7 +30,7 @@ const { h } = window;
describe("Test dragCreate", () => {
describe("add element to the scene when pointer dragging long enough", () => {
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("rectangle");
fireEvent.click(tool);
@ -62,7 +62,7 @@ describe("Test dragCreate", () => {
});
it("ellipse", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("ellipse");
fireEvent.click(tool);
@ -95,7 +95,7 @@ describe("Test dragCreate", () => {
});
it("diamond", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("diamond");
fireEvent.click(tool);
@ -127,7 +127,7 @@ describe("Test dragCreate", () => {
});
it("arrow", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("arrow");
fireEvent.click(tool);
@ -163,7 +163,7 @@ describe("Test dragCreate", () => {
});
it("line", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("line");
fireEvent.click(tool);
@ -207,7 +207,7 @@ describe("Test dragCreate", () => {
});
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("rectangle");
fireEvent.click(tool);
@ -227,7 +227,7 @@ describe("Test dragCreate", () => {
});
it("ellipse", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("ellipse");
fireEvent.click(tool);
@ -247,7 +247,7 @@ describe("Test dragCreate", () => {
});
it("diamond", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("diamond");
fireEvent.click(tool);
@ -267,7 +267,9 @@ describe("Test dragCreate", () => {
});
it("arrow", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
// select tool
const tool = getByToolName("arrow");
fireEvent.click(tool);
@ -292,7 +294,9 @@ describe("Test dragCreate", () => {
});
it("line", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
// select tool
const tool = getByToolName("line");
fireEvent.click(tool);

View File

@ -1,5 +1,5 @@
import ReactDOM from "react-dom";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { render } from "../tests/test-utils";
import { Keyboard, Pointer, UI } from "../tests/helpers/ui";
import { KEYS } from "../keys";
@ -15,7 +15,7 @@ const h = window.h;
describe("element locking", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
h.elements = [];
});

View File

@ -1,5 +1,5 @@
import { render, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api";
import {
encodePngMetadata,
@ -42,7 +42,7 @@ Object.defineProperty(window, "TextDecoder", {
describe("export", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("export embedded png and reimport", async () => {

View File

@ -1,14 +1,14 @@
import { render } from "./test-utils";
import { API } from "./helpers/api";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { vi } from "vitest";
const { h } = window;
describe("fitToContent", () => {
it("should zoom to fit the selected element", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
h.state.width = 10;
h.state.height = 10;
@ -30,7 +30,7 @@ describe("fitToContent", () => {
});
it("should zoom to fit multiple elements", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
const topLeft = API.createElement({
width: 20,
@ -61,7 +61,7 @@ describe("fitToContent", () => {
});
it("should scroll the viewport to the selected element", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
h.state.width = 10;
h.state.height = 10;
@ -106,7 +106,7 @@ describe("fitToContent animated", () => {
});
it("should ease scroll the viewport to the selected element", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
h.state.width = 10;
h.state.height = 10;
@ -142,7 +142,7 @@ describe("fitToContent animated", () => {
});
it("should animate the scroll but not the zoom", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
h.state.width = 50;
h.state.height = 50;

View File

@ -19,7 +19,7 @@ import {
FileId,
} from "../element/types";
import { newLinearElement } from "../element";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { mutateElement } from "../element/mutateElement";
import { NormalizedZoomValue } from "../types";
import { ROUNDNESS } from "../constants";
@ -52,7 +52,7 @@ beforeEach(async () => {
Object.assign(document, {
elementFromPoint: () => GlobalTestState.canvas,
});
await render(<ExcalidrawApp />);
await render(<Excalidraw autoFocus={true} handleKeyboardGlobally={true} />);
h.setState({
zoom: {
value: 1 as NormalizedZoomValue,

View File

@ -1,5 +1,5 @@
import { assertSelectedElements, render } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { Keyboard, Pointer, UI } from "./helpers/ui";
import { API } from "./helpers/api";
import { getDefaultAppState } from "../appState";
@ -13,14 +13,16 @@ 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 render(
<Excalidraw
initialData={{
elements: [API.createElement({ type: "rectangle", id: "A" })],
appState: {
zenModeEnabled: true,
},
}}
/>,
);
await waitFor(() => expect(h.state.zenModeEnabled).toBe(true));
await waitFor(() =>
@ -60,14 +62,16 @@ describe("history", () => {
});
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 render(
<Excalidraw
initialData={{
elements: [API.createElement({ type: "rectangle", id: "A" })],
appState: {
viewBackgroundColor: "#FFF",
},
}}
/>,
);
await waitFor(() => expect(h.state.viewBackgroundColor).toBe("#FFF"));
await waitFor(() =>
@ -113,7 +117,7 @@ describe("history", () => {
});
it("undo/redo works properly with groups", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
const rect1 = API.createElement({ type: "rectangle", groupIds: ["A"] });
const rect2 = API.createElement({ type: "rectangle", groupIds: ["A"] });

View File

@ -2,7 +2,7 @@ import { vi } from "vitest";
import { fireEvent, render, waitFor } from "./test-utils";
import { queryByTestId } from "@testing-library/react";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api";
import { MIME_TYPES } from "../constants";
import { LibraryItem, LibraryItems } from "../types";
@ -42,7 +42,7 @@ vi.mock("../data/filesystem.ts", async (importOriginal) => {
describe("library", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
h.app.library.resetLibrary();
});
@ -189,7 +189,7 @@ describe("library", () => {
describe("library menu", () => {
it("should load library from file picker", async () => {
const { container } = await render(<ExcalidrawApp />);
const { container } = await render(<Excalidraw />);
const latestLibrary = await h.app.library.getLatestLibrary();
expect(latestLibrary.length).toBe(0);

View File

@ -5,7 +5,7 @@ import {
ExcalidrawTextElementWithContainer,
FontString,
} from "../element/types";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { centerPoint } from "../math";
import { reseed } from "../random";
import * as Renderer from "../renderer/renderScene";
@ -43,7 +43,7 @@ describe("Test Linear Elements", () => {
renderInteractiveScene.mockClear();
renderStaticScene.mockClear();
reseed(7);
const comp = await render(<ExcalidrawApp />);
const comp = await render(<Excalidraw handleKeyboardGlobally={true} />);
h.state.width = 1000;
h.state.height = 1000;
container = comp.container;

View File

@ -1,7 +1,7 @@
import React from "react";
import ReactDOM from "react-dom";
import { render, fireEvent } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import * as Renderer from "../renderer/renderScene";
import { reseed } from "../random";
import { bindOrUnbindLinearElement } from "../element/binding";
@ -31,7 +31,7 @@ const { h } = window;
describe("move element", () => {
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
const canvas = container.querySelector("canvas.interactive")!;
{
@ -67,7 +67,7 @@ describe("move element", () => {
});
it("rectangles with binding arrow", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
// create elements
const rectA = UI.createElement("rectangle", { size: 100 });
@ -119,7 +119,7 @@ describe("move element", () => {
describe("duplicate element on move when ALT is clicked", () => {
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
const canvas = container.querySelector("canvas.interactive")!;
{

View File

@ -5,7 +5,7 @@ import {
mockBoundingClientRect,
restoreOriginalGetBoundingClientRect,
} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
import { ExcalidrawLinearElement } from "../element/types";
@ -29,7 +29,7 @@ const { h } = window;
describe("remove shape in non linear elements", () => {
beforeAll(() => {
mockBoundingClientRect();
mockBoundingClientRect({ width: 1000, height: 1000 });
});
afterAll(() => {
@ -37,12 +37,13 @@ describe("remove shape in non linear elements", () => {
});
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("rectangle");
fireEvent.click(tool);
const canvas = container.querySelector("canvas.interactive")!;
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
fireEvent.pointerUp(canvas, { clientX: 30, clientY: 30 });
@ -52,7 +53,7 @@ describe("remove shape in non linear elements", () => {
});
it("ellipse", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("ellipse");
fireEvent.click(tool);
@ -67,7 +68,7 @@ describe("remove shape in non linear elements", () => {
});
it("diamond", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("diamond");
fireEvent.click(tool);
@ -84,7 +85,7 @@ describe("remove shape in non linear elements", () => {
describe("multi point mode in linear elements", () => {
it("arrow", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("arrow");
fireEvent.click(tool);
@ -109,8 +110,8 @@ describe("multi point mode in linear elements", () => {
key: KEYS.ENTER,
});
expect(renderInteractiveScene).toHaveBeenCalledTimes(10);
expect(renderStaticScene).toHaveBeenCalledTimes(11);
expect(renderInteractiveScene).toHaveBeenCalledTimes(9);
expect(renderStaticScene).toHaveBeenCalledTimes(10);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;
@ -128,7 +129,7 @@ describe("multi point mode in linear elements", () => {
});
it("line", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("line");
fireEvent.click(tool);
@ -153,8 +154,8 @@ describe("multi point mode in linear elements", () => {
key: KEYS.ENTER,
});
expect(renderInteractiveScene).toHaveBeenCalledTimes(10);
expect(renderStaticScene).toHaveBeenCalledTimes(11);
expect(renderInteractiveScene).toHaveBeenCalledTimes(9);
expect(renderStaticScene).toHaveBeenCalledTimes(10);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;

View File

@ -1,422 +0,0 @@
import { expect } from "chai";
import { PRECEDING_ELEMENT_KEY } from "../constants";
import { ExcalidrawElement } from "../element/types";
import {
BroadcastedExcalidrawElement,
ReconciledElements,
reconcileElements,
} from "../excalidraw-app/collab/reconciliation";
import { randomInteger } from "../random";
import { AppState } from "../types";
type Id = string;
type ElementLike = {
id: string;
version: number;
versionNonce: number;
[PRECEDING_ELEMENT_KEY]?: string | null;
};
type Cache = Record<string, ExcalidrawElement | undefined>;
const createElement = (opts: { uid: string } | ElementLike) => {
let uid: string;
let id: string;
let version: number | null;
let parent: string | null = null;
let versionNonce: number | null = null;
if ("uid" in opts) {
const match = opts.uid.match(
/^(?:\((\^|\w+)\))?(\w+)(?::(\d+))?(?:\((\w+)\))?$/,
)!;
parent = match[1];
id = match[2];
version = match[3] ? parseInt(match[3]) : null;
uid = version ? `${id}:${version}` : id;
} else {
({ id, version, versionNonce } = opts);
parent = parent || null;
uid = id;
}
return {
uid,
id,
version,
versionNonce: versionNonce || randomInteger(),
[PRECEDING_ELEMENT_KEY]: parent || null,
};
};
const idsToElements = (
ids: (Id | ElementLike)[],
cache: Cache = {},
): readonly ExcalidrawElement[] => {
return ids.reduce((acc, _uid, idx) => {
const {
uid,
id,
version,
[PRECEDING_ELEMENT_KEY]: parent,
versionNonce,
} = createElement(typeof _uid === "string" ? { uid: _uid } : _uid);
const cached = cache[uid];
const elem = {
id,
version: version ?? 0,
versionNonce,
...cached,
[PRECEDING_ELEMENT_KEY]: parent,
} as BroadcastedExcalidrawElement;
// @ts-ignore
cache[uid] = elem;
acc.push(elem);
return acc;
}, [] as ExcalidrawElement[]);
};
const addParents = (elements: BroadcastedExcalidrawElement[]) => {
return elements.map((el, idx, els) => {
el[PRECEDING_ELEMENT_KEY] = els[idx - 1]?.id || "^";
return el;
});
};
const cleanElements = (elements: ReconciledElements) => {
return elements.map((el) => {
// @ts-ignore
delete el[PRECEDING_ELEMENT_KEY];
// @ts-ignore
delete el.next;
// @ts-ignore
delete el.prev;
return el;
});
};
const cloneDeep = (data: any) => JSON.parse(JSON.stringify(data));
const test = <U extends `${string}:${"L" | "R"}`>(
local: (Id | ElementLike)[],
remote: (Id | ElementLike)[],
target: U[],
bidirectional = true,
) => {
const cache: Cache = {};
const _local = idsToElements(local, cache);
const _remote = idsToElements(remote, cache);
const _target = target.map((uid) => {
const [, id, source] = uid.match(/^(\w+):([LR])$/)!;
return (source === "L" ? _local : _remote).find((e) => e.id === id)!;
}) as any as ReconciledElements;
const remoteReconciled = reconcileElements(_local, _remote, {} as AppState);
expect(target.length).equal(remoteReconciled.length);
expect(cleanElements(remoteReconciled)).deep.equal(
cleanElements(_target),
"remote reconciliation",
);
const __local = cleanElements(cloneDeep(_remote));
const __remote = addParents(cleanElements(cloneDeep(remoteReconciled)));
if (bidirectional) {
try {
expect(
cleanElements(
reconcileElements(
cloneDeep(__local),
cloneDeep(__remote),
{} as AppState,
),
),
).deep.equal(cleanElements(remoteReconciled), "local re-reconciliation");
} catch (error: any) {
console.error("local original", __local);
console.error("remote reconciled", __remote);
throw error;
}
}
};
export const findIndex = <T>(
array: readonly T[],
cb: (element: T, index: number, array: readonly T[]) => boolean,
fromIndex: number = 0,
) => {
if (fromIndex < 0) {
fromIndex = array.length + fromIndex;
}
fromIndex = Math.min(array.length, Math.max(fromIndex, 0));
let index = fromIndex - 1;
while (++index < array.length) {
if (cb(array[index], index, array)) {
return index;
}
}
return -1;
};
// -----------------------------------------------------------------------------
describe("elements reconciliation", () => {
it("reconcileElements()", () => {
// -------------------------------------------------------------------------
//
// in following tests, we pass:
// (1) an array of local elements and their version (:1, :2...)
// (2) an array of remote elements and their version (:1, :2...)
// (3) expected reconciled elements
//
// in the reconciled array:
// :L means local element was resolved
// :R means remote element was resolved
//
// if a remote element is prefixed with parentheses, the enclosed string:
// (^) means the element is the first element in the array
// (<id>) means the element is preceded by <id> element
//
// if versions are missing, it defaults to version 0
// -------------------------------------------------------------------------
// non-annotated elements
// -------------------------------------------------------------------------
// usually when we sync elements they should always be annotated with
// their (preceding elements) parents, but let's test a couple of cases when
// they're not for whatever reason (remote clients are on older version...),
// in which case the first synced element either replaces existing element
// or is pushed at the end of the array
test(["A:1", "B:1", "C:1"], ["B:2"], ["A:L", "B:R", "C:L"]);
test(["A:1", "B:1", "C"], ["B:2", "A:2"], ["B:R", "A:R", "C:L"]);
test(["A:2", "B:1", "C"], ["B:2", "A:1"], ["A:L", "B:R", "C:L"]);
test(["A:1", "B:1"], ["C:1"], ["A:L", "B:L", "C:R"]);
test(["A", "B"], ["A:1"], ["A:R", "B:L"]);
test(["A"], ["A", "B"], ["A:L", "B:R"]);
test(["A"], ["A:1", "B"], ["A:R", "B:R"]);
test(["A:2"], ["A:1", "B"], ["A:L", "B:R"]);
test(["A:2"], ["B", "A:1"], ["A:L", "B:R"]);
test(["A:1"], ["B", "A:2"], ["B:R", "A:R"]);
test(["A"], ["A:1"], ["A:R"]);
// C isn't added to the end because it follows B (even if B was resolved
// to local version)
test(["A", "B:1", "D"], ["B", "C:2", "A"], ["B:L", "C:R", "A:R", "D:L"]);
// some of the following tests are kinda arbitrary and they're less
// likely to happen in real-world cases
test(["A", "B"], ["B:1", "A:1"], ["B:R", "A:R"]);
test(["A:2", "B:2"], ["B:1", "A:1"], ["A:L", "B:L"]);
test(["A", "B", "C"], ["A", "B:2", "G", "C"], ["A:L", "B:R", "G:R", "C:L"]);
test(["A", "B", "C"], ["A", "B:2", "G"], ["A:L", "B:R", "G:R", "C:L"]);
test(["A", "B", "C"], ["A", "B:2", "G"], ["A:L", "B:R", "G:R", "C:L"]);
test(
["A:2", "B:2", "C"],
["D", "B:1", "A:3"],
["B:L", "A:R", "C:L", "D:R"],
);
test(
["A:2", "B:2", "C"],
["D", "B:2", "A:3", "C"],
["D:R", "B:L", "A:R", "C:L"],
);
test(
["A", "B", "C", "D", "E", "F"],
["A", "B:2", "X", "E:2", "F", "Y"],
["A:L", "B:R", "X:R", "E:R", "F:L", "Y:R", "C:L", "D:L"],
);
// annotated elements
// -------------------------------------------------------------------------
test(
["A", "B", "C"],
["(B)X", "(A)Y", "(Y)Z"],
["A:L", "B:L", "X:R", "Y:R", "Z:R", "C:L"],
);
test(["A"], ["(^)X", "Y"], ["X:R", "Y:R", "A:L"]);
test(["A"], ["(^)X", "Y", "Z"], ["X:R", "Y:R", "Z:R", "A:L"]);
test(
["A", "B"],
["(A)C", "(^)D", "F"],
["A:L", "C:R", "D:R", "F:R", "B:L"],
);
test(
["A", "B", "C", "D"],
["(B)C:1", "B", "D:1"],
["A:L", "C:R", "B:L", "D:R"],
);
test(
["A", "B", "C"],
["(^)X", "(A)Y", "(B)Z"],
["X:R", "A:L", "Y:R", "B:L", "Z:R", "C:L"],
);
test(
["B", "A", "C"],
["(^)X", "(A)Y", "(B)Z"],
["X:R", "B:L", "A:L", "Y:R", "Z:R", "C:L"],
);
test(["A", "B"], ["(A)X", "(A)Y"], ["A:L", "X:R", "Y:R", "B:L"]);
test(
["A", "B", "C", "D", "E"],
["(A)X", "(C)Y", "(D)Z"],
["A:L", "X:R", "B:L", "C:L", "Y:R", "D:L", "Z:R", "E:L"],
);
test(
["X", "Y", "Z"],
["(^)A", "(A)B", "(B)C", "(C)X", "(X)D", "(D)Y", "(Y)Z"],
["A:R", "B:R", "C:R", "X:L", "D:R", "Y:L", "Z:L"],
);
test(
["A", "B", "C", "D", "E"],
["(C)X", "(A)Y", "(D)E:1"],
["A:L", "B:L", "C:L", "X:R", "Y:R", "D:L", "E:R"],
);
test(
["C:1", "B", "D:1"],
["A", "B", "C:1", "D:1"],
["A:R", "B:L", "C:L", "D:L"],
);
test(
["A", "B", "C", "D"],
["(A)C:1", "(C)B", "(B)D:1"],
["A:L", "C:R", "B:L", "D:R"],
);
test(
["A", "B", "C", "D"],
["(A)C:1", "(C)B", "(B)D:1"],
["A:L", "C:R", "B:L", "D:R"],
);
test(
["C:1", "B", "D:1"],
["(^)A", "(A)B", "(B)C:2", "(C)D:1"],
["A:R", "B:L", "C:R", "D:L"],
);
test(
["A", "B", "C", "D"],
["(C)X", "(B)Y", "(A)Z"],
["A:L", "B:L", "C:L", "X:R", "Y:R", "Z:R", "D:L"],
);
test(["A", "B", "C", "D"], ["(A)B:1", "C:1"], ["A:L", "B:R", "C:R", "D:L"]);
test(["A", "B", "C", "D"], ["(A)C:1", "B:1"], ["A:L", "C:R", "B:R", "D:L"]);
test(
["A", "B", "C", "D"],
["(A)C:1", "B", "D:1"],
["A:L", "C:R", "B:L", "D:R"],
);
test(["A:1", "B:1", "C"], ["B:2"], ["A:L", "B:R", "C:L"]);
test(["A:1", "B:1", "C"], ["B:2", "C:2"], ["A:L", "B:R", "C:R"]);
test(["A", "B"], ["(A)C", "(B)D"], ["A:L", "C:R", "B:L", "D:R"]);
test(["A", "B"], ["(X)C", "(X)D"], ["A:L", "B:L", "C:R", "D:R"]);
test(["A", "B"], ["(X)C", "(A)D"], ["A:L", "D:R", "B:L", "C:R"]);
test(["A", "B"], ["(A)B:1"], ["A:L", "B:R"]);
test(["A:2", "B"], ["(A)B:1"], ["A:L", "B:R"]);
test(["A:2", "B:2"], ["B:1"], ["A:L", "B:L"]);
test(["A:2", "B:2"], ["B:1", "C"], ["A:L", "B:L", "C:R"]);
test(["A:2", "B:2"], ["(A)C", "B:1"], ["A:L", "C:R", "B:L"]);
test(["A:2", "B:2"], ["(A)C", "B:1"], ["A:L", "C:R", "B:L"]);
});
it("test identical elements reconciliation", () => {
const testIdentical = (
local: ElementLike[],
remote: ElementLike[],
expected: Id[],
) => {
const ret = reconcileElements(
local as any as ExcalidrawElement[],
remote as any as ExcalidrawElement[],
{} as AppState,
);
if (new Set(ret.map((x) => x.id)).size !== ret.length) {
throw new Error("reconcileElements: duplicate elements found");
}
expect(ret.map((x) => x.id)).to.deep.equal(expected);
};
// identical id/version/versionNonce
// -------------------------------------------------------------------------
testIdentical(
[{ id: "A", version: 1, versionNonce: 1 }],
[{ id: "A", version: 1, versionNonce: 1 }],
["A"],
);
testIdentical(
[
{ id: "A", version: 1, versionNonce: 1 },
{ id: "B", version: 1, versionNonce: 1 },
],
[
{ id: "B", version: 1, versionNonce: 1 },
{ id: "A", version: 1, versionNonce: 1 },
],
["B", "A"],
);
testIdentical(
[
{ id: "A", version: 1, versionNonce: 1 },
{ id: "B", version: 1, versionNonce: 1 },
],
[
{ id: "B", version: 1, versionNonce: 1 },
{ id: "A", version: 1, versionNonce: 1 },
],
["B", "A"],
);
// actually identical (arrays and element objects)
// -------------------------------------------------------------------------
const elements1 = [
{
id: "A",
version: 1,
versionNonce: 1,
[PRECEDING_ELEMENT_KEY]: null,
},
{
id: "B",
version: 1,
versionNonce: 1,
[PRECEDING_ELEMENT_KEY]: null,
},
];
testIdentical(elements1, elements1, ["A", "B"]);
testIdentical(elements1, elements1.slice(), ["A", "B"]);
testIdentical(elements1.slice(), elements1, ["A", "B"]);
testIdentical(elements1.slice(), elements1.slice(), ["A", "B"]);
const el1 = {
id: "A",
version: 1,
versionNonce: 1,
[PRECEDING_ELEMENT_KEY]: null,
};
const el2 = {
id: "B",
version: 1,
versionNonce: 1,
[PRECEDING_ELEMENT_KEY]: null,
};
testIdentical([el1, el2], [el2, el1], ["A", "B"]);
});
});

View File

@ -1,7 +1,7 @@
import ReactDOM from "react-dom";
import { ExcalidrawElement } from "../element/types";
import { CODES, KEYS } from "../keys";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { reseed } from "../random";
import * as Renderer from "../renderer/renderScene";
import { setDateTimeForTests } from "../utils";
@ -13,9 +13,7 @@ import {
render,
screen,
togglePopover,
waitFor,
} from "./test-utils";
import { defaultLang } from "../i18n";
import { FONT_FAMILY } from "../constants";
import { vi } from "vitest";
@ -56,7 +54,7 @@ beforeEach(async () => {
finger1.reset();
finger2.reset();
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
h.setState({ height: 768, width: 1024 });
});
@ -443,26 +441,6 @@ describe("regression tests", () => {
expect(h.state.zoom.value).toBe(1);
});
it("rerenders UI on language change", async () => {
// select rectangle tool to show properties menu
UI.clickTool("rectangle");
// english lang should display `thin` label
expect(screen.queryByTitle(/thin/i)).not.toBeNull();
fireEvent.click(document.querySelector(".dropdown-menu-button")!);
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
target: { value: "de-DE" },
});
// switching to german, `thin` label should no longer exist
await waitFor(() => expect(screen.queryByTitle(/thin/i)).toBeNull());
// reset language
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
target: { value: defaultLang.code },
});
// switching back to English
await waitFor(() => expect(screen.queryByTitle(/thin/i)).not.toBeNull());
});
it("make a group and duplicate it", () => {
UI.clickTool("rectangle");
mouse.down(10, 10);

View File

@ -6,7 +6,7 @@ import { reseed } from "../random";
import { UI, Keyboard } from "./helpers/ui";
import { resize } from "./utils";
import { ExcalidrawTextElement } from "../element/types";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api";
import { KEYS } from "../keys";
import { vi } from "vitest";
@ -126,7 +126,7 @@ describe("resize rectangle ellipses and diamond elements", () => {
describe("Test text element", () => {
it("should update font size via keyboard", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw handleKeyboardGlobally={true} />);
const textElement = API.createElement({
type: "text",

View File

@ -8,7 +8,6 @@ import { Excalidraw } from "../packages/excalidraw/index";
import { API } from "./helpers/api";
import { Keyboard } from "./helpers/ui";
import { KEYS } from "../keys";
import ExcalidrawApp from "../excalidraw-app";
const { h } = window;
@ -56,7 +55,7 @@ describe("appState", () => {
it("moving by page up/down/left/right", async () => {
mockBoundingClientRect();
await render(<ExcalidrawApp />, {});
await render(<Excalidraw handleKeyboardGlobally={true} />, {});
const scrollTest = () => {
const initialScrollY = h.state.scrollY;

View File

@ -6,7 +6,7 @@ import {
restoreOriginalGetBoundingClientRect,
assertSelectedElements,
} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
import { reseed } from "../random";
@ -34,7 +34,7 @@ const mouse = new Pointer("mouse");
describe("box-selection", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("should allow adding to selection via box-select when holding shift", async () => {
@ -102,7 +102,7 @@ describe("box-selection", () => {
describe("inner box-selection", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("selecting elements visually nested inside another", async () => {
const rect1 = API.createElement({
@ -218,7 +218,7 @@ describe("inner box-selection", () => {
describe("selection element", () => {
it("create selection element on pointer down", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("selection");
fireEvent.click(tool);
@ -239,7 +239,7 @@ describe("selection element", () => {
});
it("resize selection element on pointer move", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("selection");
fireEvent.click(tool);
@ -261,7 +261,7 @@ describe("selection element", () => {
});
it("remove selection element on pointer up", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(<Excalidraw />);
// select tool
const tool = getByToolName("selection");
fireEvent.click(tool);
@ -287,7 +287,9 @@ describe("select single element on the scene", () => {
});
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
const canvas = container.querySelector("canvas.interactive")!;
{
// create element
@ -317,7 +319,9 @@ describe("select single element on the scene", () => {
});
it("diamond", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
const canvas = container.querySelector("canvas.interactive")!;
{
// create element
@ -347,7 +351,9 @@ describe("select single element on the scene", () => {
});
it("ellipse", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
const canvas = container.querySelector("canvas.interactive")!;
{
// create element
@ -377,7 +383,9 @@ describe("select single element on the scene", () => {
});
it("arrow", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
const canvas = container.querySelector("canvas.interactive")!;
{
// create element
@ -419,7 +427,9 @@ describe("select single element on the scene", () => {
});
it("arrow escape", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const { getByToolName, container } = await render(
<Excalidraw handleKeyboardGlobally={true} />,
);
const canvas = container.querySelector("canvas.interactive")!;
{
// create element
@ -464,7 +474,7 @@ describe("select single element on the scene", () => {
describe("tool locking & selection", () => {
it("should not select newly created element while tool is locked", async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
UI.clickTool("lock");
expect(h.state.activeTool.locked).toBe(true);
@ -480,7 +490,7 @@ describe("tool locking & selection", () => {
describe("selectedElementIds stability", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("box-selection should be stable when not changing selection", () => {

View File

@ -11,7 +11,7 @@ import {
import * as toolQueries from "./queries/toolQueries";
import { ImportedDataState } from "../data/types";
import { STORAGE_KEYS } from "../excalidraw-app/app_constants";
import { STORAGE_KEYS } from "../../excalidraw-app/app_constants";
import { SceneData } from "../types";
import { getSelectedElements } from "../scene/selection";

View File

@ -1,5 +1,5 @@
import { render, GlobalTestState } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { KEYS } from "../keys";
import { Keyboard, Pointer, UI } from "./helpers/ui";
import { CURSOR_TYPE } from "../constants";
@ -12,7 +12,7 @@ const pointerTypes = [mouse, touch, pen];
describe("view mode", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("after switching to view mode cursor type should be pointer", async () => {

View File

@ -1,6 +1,6 @@
import ReactDOM from "react-dom";
import { render } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { Excalidraw } from "../packages/excalidraw/index";
import { reseed } from "../random";
import {
actionSendBackward,
@ -121,7 +121,6 @@ const assertZindex = ({
operations: [Actions, string[]][];
}) => {
const selectedElementIds = populateElements(elements, appState);
operations.forEach(([action, expected]) => {
h.app.actionManager.executeAction(action);
expect(h.elements.map((element) => element.id)).toEqual(expected);
@ -131,7 +130,7 @@ const assertZindex = ({
describe("z-index manipulation", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
await render(<Excalidraw />);
});
it("send back", () => {