Merge branch 'master' into arnost/scroll-in-read-only-links

This commit is contained in:
Arnost Pleskot 2023-07-31 09:26:14 +02:00 committed by GitHub
commit af6e64ffc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
171 changed files with 9637 additions and 13847 deletions

View file

@ -11,23 +11,23 @@ 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!);
});
beforeAll(() => {
mockBoundingClientRect(dimensions);
});
afterAll(() => {
restoreOriginalGetBoundingClientRect();
});
it("should set device correctly", () => {
expect(h.app.device).toMatchInlineSnapshot(`
Object {
{
"canDeviceFitSidebar": false,
"isLandscape": true,
"isMobile": true,
@ -39,7 +39,6 @@ describe("Test MobileMenu", () => {
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

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Test MobileMenu should initialize with welcome screen and hide once user interacts 1`] = `
exports[`Test MobileMenu > should initialize with welcome screen and hide once user interacts 1`] = `
<div
class="welcome-screen-center"
>

View file

@ -1,15 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`tryParseSpreadsheet works for numbers with comma in them 1`] = `
Object {
"spreadsheet": Object {
"labels": Array [
exports[`tryParseSpreadsheet > works for numbers with comma in them 1`] = `
{
"spreadsheet": {
"labels": [
"Week 1",
"Week 2",
"Week 3",
],
"title": "Users",
"values": Array [
"values": [
814,
10301,
4264,

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Test dragCreate add element to the scene when pointer dragging long enough arrow 1`] = `1`;
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > arrow 1`] = `1`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough arrow 2`] = `
Object {
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > arrow 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -11,7 +11,7 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -19,18 +19,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
30,
50,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -49,16 +49,16 @@ Object {
}
`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough diamond 1`] = `1`;
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > diamond 1`] = `1`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough diamond 2`] = `
Object {
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > diamond 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -66,7 +66,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -83,16 +83,16 @@ Object {
}
`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough ellipse 1`] = `1`;
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > ellipse 1`] = `1`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough ellipse 2`] = `
Object {
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > ellipse 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -100,7 +100,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -117,8 +117,8 @@ Object {
}
`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough line 1`] = `
Object {
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > line 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -126,7 +126,7 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -134,18 +134,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
30,
50,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -164,16 +164,16 @@ Object {
}
`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough rectangle 1`] = `1`;
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > rectangle 1`] = `1`;
exports[`Test dragCreate add element to the scene when pointer dragging long enough rectangle 2`] = `
Object {
exports[`Test dragCreate > add element to the scene when pointer dragging long enough > rectangle 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -181,7 +181,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 337897,

File diff suppressed because one or more lines are too long

View file

@ -1,11 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Test Linear Elements Test bound text element should match styles for text editor 1`] = `
exports[`Test Linear Elements > Test bound text element > should match styles for text editor 1`] = `
<textarea
class="excalidraw-wysiwyg"
data-type="wysiwyg"
dir="auto"
style="position: absolute; display: inline-block; min-height: 1em; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10.5px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(30, 30, 30); opacity: 1; filter: var(--theme-filter); max-height: -7.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
style="position: absolute; display: inline-block; min-height: 1em; backface-visibility: hidden; margin: 0px; padding: 0px; border: 0px; outline: 0; resize: none; background: transparent; overflow: hidden; z-index: var(--zIndex-wysiwyg); word-break: break-word; white-space: pre-wrap; overflow-wrap: break-word; box-sizing: content-box; width: 10.5px; height: 25px; left: 35px; top: 7.5px; transform: translate(0px, 0px) scale(1) rotate(0deg); text-align: center; vertical-align: middle; color: rgb(30, 30, 30); opacity: 1; filter: var(--theme-filter); max-height: -7.5px; font: Emoji 20px 20px; line-height: 1.25; font-family: Virgil, Segoe UI Emoji;"
tabindex="0"
wrap="off"
/>

View file

@ -1,13 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`duplicate element on move when ALT is clicked rectangle 1`] = `
Object {
exports[`duplicate element on move when ALT is clicked > rectangle 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0_copy",
"isDeleted": false,
@ -15,7 +15,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 401146281,
@ -32,14 +32,14 @@ Object {
}
`;
exports[`duplicate element on move when ALT is clicked rectangle 2`] = `
Object {
exports[`duplicate element on move when ALT is clicked > rectangle 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -47,7 +47,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 337897,
@ -64,14 +64,14 @@ Object {
}
`;
exports[`move element rectangle 1`] = `
Object {
exports[`move element > rectangle 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -79,7 +79,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 337897,
@ -96,19 +96,19 @@ Object {
}
`;
exports[`move element rectangles with binding arrow 1`] = `
Object {
exports[`move element > rectangles with binding arrow 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [
Object {
"boundElements": [
{
"id": "id2",
"type": "arrow",
},
],
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id0",
"isDeleted": false,
@ -116,7 +116,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 337897,
@ -133,19 +133,19 @@ Object {
}
`;
exports[`move element rectangles with binding arrow 2`] = `
Object {
exports[`move element > rectangles with binding arrow 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [
Object {
"boundElements": [
{
"id": "id2",
"type": "arrow",
},
],
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 300,
"id": "id1",
"isDeleted": false,
@ -153,7 +153,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 449462985,
@ -170,20 +170,20 @@ Object {
}
`;
exports[`move element rectangles with binding arrow 3`] = `
Object {
exports[`move element > rectangles with binding arrow 3`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"endArrowhead": null,
"endBinding": Object {
"endBinding": {
"elementId": "id1",
"focus": -0.46666666666666673,
"gap": 10,
},
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 81.48231043525051,
"id": "id2",
"isDeleted": false,
@ -191,23 +191,23 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
81,
81.48231043525051,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 401146281,
"startArrowhead": null,
"startBinding": Object {
"startBinding": {
"elementId": "id0",
"focus": -0.6000000000000001,
"gap": 10,

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`multi point mode in linear elements arrow 1`] = `
Object {
exports[`multi point mode in linear elements > arrow 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -9,33 +9,33 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 110,
"id": "id0",
"isDeleted": false,
"lastCommittedPoint": Array [
"lastCommittedPoint": [
70,
110,
],
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
20,
30,
],
Array [
[
70,
110,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -54,8 +54,8 @@ Object {
}
`;
exports[`multi point mode in linear elements line 1`] = `
Object {
exports[`multi point mode in linear elements > line 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -63,33 +63,33 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 110,
"id": "id0",
"isDeleted": false,
"lastCommittedPoint": Array [
"lastCommittedPoint": [
70,
110,
],
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
20,
30,
],
Array [
[
70,
110,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`select single element on the scene arrow 1`] = `
Object {
exports[`select single element on the scene > arrow 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -9,7 +9,7 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -17,18 +17,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
30,
50,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -47,8 +47,8 @@ Object {
}
`;
exports[`select single element on the scene arrow escape 1`] = `
Object {
exports[`select single element on the scene > arrow escape 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
@ -56,7 +56,7 @@ Object {
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -64,18 +64,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
30,
50,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -94,14 +94,14 @@ Object {
}
`;
exports[`select single element on the scene diamond 1`] = `
Object {
exports[`select single element on the scene > diamond 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -109,7 +109,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -126,14 +126,14 @@ Object {
}
`;
exports[`select single element on the scene ellipse 1`] = `
Object {
exports[`select single element on the scene > ellipse 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -141,7 +141,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": 337897,
@ -158,14 +158,14 @@ Object {
}
`;
exports[`select single element on the scene rectangle 1`] = `
Object {
exports[`select single element on the scene > rectangle 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 50,
"id": "id0",
"isDeleted": false,
@ -173,7 +173,7 @@ Object {
"locked": false,
"opacity": 100,
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": 337897,

View file

@ -1,4 +1,5 @@
import { queryByTestId, render, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
import { getDefaultAppState } from "../appState";

View file

@ -15,6 +15,61 @@ describe("element binding", () => {
await render(<ExcalidrawApp />);
});
it("should create valid binding if duplicate start/end points", async () => {
const rect = API.createElement({
type: "rectangle",
x: 0,
width: 50,
height: 50,
});
const arrow = API.createElement({
type: "arrow",
x: 100,
y: 0,
width: 100,
height: 1,
points: [
[0, 0],
[0, 0],
[100, 0],
[100, 0],
],
});
h.elements = [rect, arrow];
expect(arrow.startBinding).toBe(null);
API.setSelectedElements([arrow]);
expect(API.getSelectedElements()).toEqual([arrow]);
mouse.downAt(100, 0);
mouse.moveTo(55, 0);
mouse.up(0, 0);
expect(arrow.startBinding).toEqual({
elementId: rect.id,
focus: expect.toBeNonNaNNumber(),
gap: expect.toBeNonNaNNumber(),
});
mouse.downAt(100, 0);
mouse.move(-45, 0);
mouse.up();
expect(arrow.startBinding).toEqual({
elementId: rect.id,
focus: expect.toBeNonNaNNumber(),
gap: expect.toBeNonNaNNumber(),
});
mouse.down();
mouse.move(-50, 0);
mouse.up();
expect(arrow.startBinding).toBe(null);
expect(arrow.endBinding).toEqual({
elementId: rect.id,
focus: expect.toBeNonNaNNumber(),
gap: expect.toBeNonNaNNumber(),
});
});
//@TODO fix the test with rotation
it.skip("rotation of arrow should rebind both ends", () => {
const rectLeft = UI.createElement("rectangle", {

View file

@ -1,3 +1,4 @@
import { vi } from "vitest";
import ReactDOM from "react-dom";
import {
render,
@ -21,14 +22,14 @@ const { h } = window;
const mouse = new Pointer("mouse");
jest.mock("../keys.ts", () => {
const actual = jest.requireActual("../keys.ts");
vi.mock("../keys.ts", async (importOriginal) => {
const module: any = await importOriginal();
return {
__esmodule: true,
...actual,
...module,
isDarwin: false,
KEYS: {
...actual.KEYS,
...module.KEYS,
CTRL_OR_CMD: "ctrlKey",
},
};

View file

@ -1,3 +1,4 @@
import { vi } from "vitest";
import { render, updateSceneData, waitFor } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
@ -15,15 +16,18 @@ Object.defineProperty(window, "crypto", {
},
});
jest.mock("../excalidraw-app/data/index.ts", () => ({
__esmodule: true,
...jest.requireActual("../excalidraw-app/data/index.ts"),
getCollabServer: jest.fn(() => ({
url: /* doesn't really matter */ "http://localhost:3002",
})),
}));
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",
})),
};
});
jest.mock("../excalidraw-app/data/firebase.ts", () => {
vi.mock("../excalidraw-app/data/firebase.ts", () => {
const loadFromFirebase = async () => null;
const saveToFirebase = () => {};
const isSavedToFirebase = () => true;
@ -45,15 +49,17 @@ jest.mock("../excalidraw-app/data/firebase.ts", () => {
};
});
jest.mock("socket.io-client", () => {
return () => {
return {
close: () => {},
on: () => {},
once: () => {},
off: () => {},
emit: () => {},
};
vi.mock("socket.io-client", () => {
return {
default: () => {
return {
close: () => {},
on: () => {},
once: () => {},
off: () => {},
emit: () => {},
};
},
};
});

View file

@ -21,6 +21,7 @@ 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) => {
expect(renderScene.mock.calls.length).toMatchSnapshot(
@ -39,7 +40,7 @@ const mouse = new Pointer("mouse");
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View file

@ -1,15 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`restoreElements should restore arrow element correctly 1`] = `
Object {
exports[`restoreElements > should restore arrow element correctly 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [],
"boundElements": [],
"endArrowhead": null,
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id-arrow01",
"isDeleted": false,
@ -17,18 +17,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
100,
100,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": Any<Number>,
@ -47,14 +47,14 @@ Object {
}
`;
exports[`restoreElements should restore correctly with rectangle, ellipse and diamond elements 1`] = `
Object {
exports[`restoreElements > should restore correctly with rectangle, ellipse and diamond elements 1`] = `
{
"angle": 0,
"backgroundColor": "blue",
"boundElements": Array [],
"boundElements": [],
"fillStyle": "cross-hatch",
"frameId": null,
"groupIds": Array [
"groupIds": [
"1",
"2",
"3",
@ -66,7 +66,7 @@ Object {
"locked": false,
"opacity": 10,
"roughness": 2,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,
@ -83,14 +83,14 @@ Object {
}
`;
exports[`restoreElements should restore correctly with rectangle, ellipse and diamond elements 2`] = `
Object {
exports[`restoreElements > should restore correctly with rectangle, ellipse and diamond elements 2`] = `
{
"angle": 0,
"backgroundColor": "blue",
"boundElements": Array [],
"boundElements": [],
"fillStyle": "cross-hatch",
"frameId": null,
"groupIds": Array [
"groupIds": [
"1",
"2",
"3",
@ -102,7 +102,7 @@ Object {
"locked": false,
"opacity": 10,
"roughness": 2,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,
@ -119,14 +119,14 @@ Object {
}
`;
exports[`restoreElements should restore correctly with rectangle, ellipse and diamond elements 3`] = `
Object {
exports[`restoreElements > should restore correctly with rectangle, ellipse and diamond elements 3`] = `
{
"angle": 0,
"backgroundColor": "blue",
"boundElements": Array [],
"boundElements": [],
"fillStyle": "cross-hatch",
"frameId": null,
"groupIds": Array [
"groupIds": [
"1",
"2",
"3",
@ -138,7 +138,7 @@ Object {
"locked": false,
"opacity": 10,
"roughness": 2,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,
@ -155,14 +155,14 @@ Object {
}
`;
exports[`restoreElements should restore freedraw element correctly 1`] = `
Object {
exports[`restoreElements > should restore freedraw element correctly 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [],
"boundElements": [],
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 0,
"id": "id-freedraw01",
"isDeleted": false,
@ -170,10 +170,10 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [],
"pressures": Array [],
"points": [],
"pressures": [],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,
@ -191,16 +191,16 @@ Object {
}
`;
exports[`restoreElements should restore line and draw elements correctly 1`] = `
Object {
exports[`restoreElements > should restore line and draw elements correctly 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [],
"boundElements": [],
"endArrowhead": null,
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id-line01",
"isDeleted": false,
@ -208,18 +208,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
100,
100,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": Any<Number>,
@ -238,16 +238,16 @@ Object {
}
`;
exports[`restoreElements should restore line and draw elements correctly 2`] = `
Object {
exports[`restoreElements > should restore line and draw elements correctly 2`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"boundElements": Array [],
"boundElements": [],
"endArrowhead": null,
"endBinding": null,
"fillStyle": "hachure",
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id-draw01",
"isDeleted": false,
@ -255,18 +255,18 @@ Object {
"link": null,
"locked": false,
"opacity": 100,
"points": Array [
Array [
"points": [
[
0,
0,
],
Array [
[
100,
100,
],
],
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 2,
},
"seed": Any<Number>,
@ -285,18 +285,18 @@ Object {
}
`;
exports[`restoreElements should restore text element correctly passing value for each attribute 1`] = `
Object {
exports[`restoreElements > should restore text element correctly passing value for each attribute 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"baseline": 0,
"boundElements": Array [],
"boundElements": [],
"containerId": null,
"fillStyle": "hachure",
"fontFamily": 1,
"fontSize": 14,
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id-text01",
"isDeleted": false,
@ -306,7 +306,7 @@ Object {
"opacity": 100,
"originalText": "text",
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,
@ -326,18 +326,18 @@ Object {
}
`;
exports[`restoreElements should restore text element correctly with unknown font family, null text and undefined alignment 1`] = `
Object {
exports[`restoreElements > should restore text element correctly with unknown font family, null text and undefined alignment 1`] = `
{
"angle": 0,
"backgroundColor": "transparent",
"baseline": 0,
"boundElements": Array [],
"boundElements": [],
"containerId": null,
"fillStyle": "hachure",
"fontFamily": 1,
"fontSize": 10,
"frameId": null,
"groupIds": Array [],
"groupIds": [],
"height": 100,
"id": "id-text01",
"isDeleted": false,
@ -347,7 +347,7 @@ Object {
"opacity": 100,
"originalText": "test",
"roughness": 1,
"roundness": Object {
"roundness": {
"type": 3,
},
"seed": Any<Number>,

View file

@ -12,9 +12,10 @@ import { ImportedDataState } from "../../data/types";
import { NormalizedZoomValue } from "../../types";
import { DEFAULT_SIDEBAR, FONT_FAMILY, ROUNDNESS } from "../../constants";
import { newElementWith } from "../../element/mutateElement";
import { vi } from "vitest";
describe("restoreElements", () => {
const mockSizeHelper = jest.spyOn(sizeHelpers, "isInvisiblySmallElement");
const mockSizeHelper = vi.spyOn(sizeHelpers, "isInvisiblySmallElement");
beforeEach(() => {
mockSizeHelper.mockReset();
@ -152,7 +153,7 @@ describe("restoreElements", () => {
it("when arrow element has undefined endArrowHead", () => {
const arrowElement = API.createElement({ type: "arrow" });
Object.defineProperty(arrowElement, "endArrowhead", {
get: jest.fn(() => undefined),
get: vi.fn(() => undefined),
});
const restoredElements = restore.restoreElements([arrowElement], null);
@ -205,7 +206,7 @@ describe("restoreElements", () => {
[1, 1],
];
Object.defineProperty(lineElement_0, "points", {
get: jest.fn(() => pointsEl_0),
get: vi.fn(() => pointsEl_0),
});
const pointsEl_1 = [
@ -213,7 +214,7 @@ describe("restoreElements", () => {
[5, 6],
];
Object.defineProperty(lineElement_1, "points", {
get: jest.fn(() => pointsEl_1),
get: vi.fn(() => pointsEl_1),
});
const restoredElements = restore.restoreElements(
@ -245,7 +246,7 @@ describe("restoreElements", () => {
types.forEach((elType) => {
idCount += 1;
const element = API.createElement({
type: elType as "rectangle" | "ellipse" | "diamond",
type: elType as "rectangle" | "ellipse" | "diamond" | "embeddable",
id: idCount.toString(),
fillStyle: "cross-hatch",
strokeWidth: 2,
@ -440,7 +441,7 @@ describe("restoreAppState", () => {
const stubImportedAppState = getDefaultAppState();
Object.defineProperty(stubImportedAppState, "zoom", {
get: jest.fn(() => null),
get: vi.fn(() => null),
});
const stubLocalAppState = getDefaultAppState();

View file

@ -10,11 +10,12 @@ import {
} from "./test-utils";
import { ExcalidrawLinearElement } from "../element/types";
import { reseed } from "../random";
import { vi } from "vitest";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View file

@ -2,6 +2,7 @@ import { render } from "./test-utils";
import { API } from "./helpers/api";
import ExcalidrawApp from "../excalidraw-app";
import { vi } from "vitest";
const { h } = window;
@ -97,11 +98,11 @@ const waitForNextAnimationFrame = () => {
describe("fitToContent animated", () => {
beforeEach(() => {
jest.spyOn(window, "requestAnimationFrame");
vi.spyOn(window, "requestAnimationFrame");
});
afterEach(() => {
jest.restoreAllMocks();
vi.restoreAllMocks();
});
it("should ease scroll the viewport to the selected element", async () => {

View file

@ -31,6 +31,10 @@ export const rectangleFixture: ExcalidrawElement = {
...elementBase,
type: "rectangle",
};
export const embeddableFixture: ExcalidrawElement = {
...elementBase,
type: "embeddable",
};
export const ellipseFixture: ExcalidrawElement = {
...elementBase,
type: "ellipse",

View file

@ -20,21 +20,21 @@ import ExcalidrawApp from "../excalidraw-app";
import { mutateElement } from "../element/mutateElement";
import { NormalizedZoomValue } from "../types";
import { ROUNDNESS } from "../constants";
import { vi } from "vitest";
import * as blob from "../data/blob";
const { h } = window;
const mouse = new Pointer("mouse");
jest.mock("../data/blob", () => {
const originalModule = jest.requireActual("../data/blob");
// This needs to fixed in vitest mock, as when importActual used with mock
// the tests hangs - https://github.com/vitest-dev/vitest/issues/546.
// But fortunately spying and mocking the return value of spy works :p
const resizeImageFileSpy = vi.spyOn(blob, "resizeImageFile");
const generateIdFromFileSpy = vi.spyOn(blob, "generateIdFromFile");
resizeImageFileSpy.mockImplementation(async (imageFile: File) => imageFile);
generateIdFromFileSpy.mockImplementation(async () => "fileId" as FileId);
//Prevent Node.js modules errors (document is not defined etc...)
return {
__esModule: true,
...originalModule,
resizeImageFile: (imageFile: File) => imageFile,
generateIdFromFile: () => "fileId" as FileId,
};
});
beforeEach(async () => {
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
@ -42,7 +42,7 @@ beforeEach(async () => {
mouse.reset();
localStorage.clear();
sessionStorage.clear();
jest.clearAllMocks();
vi.clearAllMocks();
Object.assign(document, {
elementFromPoint: () => GlobalTestState.canvas,
@ -732,7 +732,6 @@ describe("image", () => {
it("flips an unrotated image horizontally correctly", async () => {
//paste image
await createImage();
await waitFor(() => {
expect((h.elements[0] as ExcalidrawImageElement).scale).toEqual([1, 1]);
expect(API.getSelectedElements().length).toBeGreaterThan(0);

View file

@ -178,8 +178,9 @@ export class API {
case "rectangle":
case "diamond":
case "ellipse":
case "embeddable":
element = newElement({
type: type as "rectangle" | "diamond" | "ellipse",
type: type as "rectangle" | "diamond" | "ellipse" | "embeddable",
width,
height,
...base,

View file

@ -1,3 +1,4 @@
import { vi } from "vitest";
import { fireEvent, render, waitFor } from "./test-utils";
import { queryByTestId } from "@testing-library/react";
@ -29,11 +30,15 @@ const mockLibraryFilePromise = new Promise<Blob>(async (resolve, reject) => {
}
});
jest.mock("../data/filesystem.ts", () => ({
__esmodule: true,
...jest.requireActual("../data/filesystem.ts"),
fileOpen: jest.fn(() => mockLibraryFilePromise),
}));
vi.mock("../data/filesystem.ts", async (importOriginal) => {
const module = await importOriginal();
return {
__esmodule: true,
//@ts-ignore
...module,
fileOpen: vi.fn(() => mockLibraryFilePromise),
};
});
describe("library", () => {
beforeEach(async () => {

View file

@ -24,8 +24,9 @@ import {
} from "../element/textElement";
import * as textElementUtils from "../element/textElement";
import { ROUNDNESS, VERTICAL_ALIGN } from "../constants";
import { vi } from "vitest";
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
const { h } = window;
const font = "20px Cascadia, width: Segoe UI Emoji" as FontString;
@ -179,16 +180,16 @@ describe("Test Linear Elements", () => {
expect(renderScene).toHaveBeenCalledTimes(11);
expect(line.points.length).toEqual(3);
expect(line.points).toMatchInlineSnapshot(`
Array [
Array [
[
[
0,
0,
],
Array [
[
70,
50,
],
Array [
[
40,
0,
],
@ -273,16 +274,16 @@ describe("Test Linear Elements", () => {
expect(line.points.length).toEqual(3);
expect(line.points).toMatchInlineSnapshot(`
Array [
Array [
[
[
0,
0,
],
Array [
[
70,
50,
],
Array [
[
40,
0,
],
@ -315,12 +316,12 @@ describe("Test Linear Elements", () => {
expect(midPointsWithRoundEdge[1]).not.toEqual(midPointsWithSharpEdge[1]);
expect(midPointsWithRoundEdge).toMatchInlineSnapshot(`
Array [
Array [
[
[
55.9697848965255,
47.442326230998205,
],
Array [
[
76.08587175006699,
43.294165939653226,
],
@ -363,12 +364,12 @@ describe("Test Linear Elements", () => {
expect(midPoints[0]).not.toEqual(newMidPoints[0]);
expect(midPoints[1]).not.toEqual(newMidPoints[1]);
expect(newMidPoints).toMatchInlineSnapshot(`
Array [
Array [
[
[
105.96978489652551,
67.4423262309982,
],
Array [
[
126.08587175006699,
63.294165939653226,
],
@ -412,29 +413,29 @@ describe("Test Linear Elements", () => {
expect((h.elements[0] as ExcalidrawLinearElement).points)
.toMatchInlineSnapshot(`
Array [
Array [
0,
0,
],
Array [
85,
75,
],
Array [
70,
50,
],
Array [
105,
70,
],
Array [
40,
0,
],
]
`);
[
[
0,
0,
],
[
85,
75,
],
[
70,
50,
],
[
105,
70,
],
[
40,
0,
],
]
`);
});
it("should update only the first segment midpoint when its point is dragged", async () => {
@ -558,29 +559,29 @@ describe("Test Linear Elements", () => {
expect((h.elements[0] as ExcalidrawLinearElement).points)
.toMatchInlineSnapshot(`
Array [
Array [
0,
0,
],
Array [
85.96978489652551,
77.4423262309982,
],
Array [
70,
50,
],
Array [
106.08587175006699,
73.29416593965323,
],
Array [
40,
0,
],
]
`);
[
[
0,
0,
],
[
85.96978489652551,
77.4423262309982,
],
[
70,
50,
],
[
106.08587175006699,
73.29416593965323,
],
[
40,
0,
],
]
`);
});
it("should update all the midpoints when its point is dragged", async () => {
@ -606,12 +607,12 @@ describe("Test Linear Elements", () => {
expect(midPoints[0]).not.toEqual(newMidPoints[0]);
expect(midPoints[1]).not.toEqual(newMidPoints[1]);
expect(newMidPoints).toMatchInlineSnapshot(`
Array [
Array [
[
[
31.884084517616053,
23.13275505472383,
],
Array [
[
77.74792546875662,
44.57840982272327,
],
@ -667,12 +668,12 @@ describe("Test Linear Elements", () => {
expect(midPoints[0]).not.toEqual(newMidPoints[0]);
expect(midPoints[1]).not.toEqual(newMidPoints[1]);
expect(newMidPoints).toMatchInlineSnapshot(`
Array [
Array [
[
[
55.9697848965255,
47.442326230998205,
],
Array [
[
76.08587175006699,
43.294165939653226,
],
@ -704,12 +705,12 @@ describe("Test Linear Elements", () => {
[dragEndPositionOffset[0] + line.x, dragEndPositionOffset[1] + line.y],
);
expect(line.points).toMatchInlineSnapshot(`
Array [
Array [
[
[
0,
0,
],
Array [
[
-60,
-100,
],
@ -768,7 +769,7 @@ describe("Test Linear Elements", () => {
textElement,
);
expect(position).toMatchInlineSnapshot(`
Object {
{
"x": 25,
"y": 10,
}
@ -790,7 +791,7 @@ describe("Test Linear Elements", () => {
textElement,
);
expect(position).toMatchInlineSnapshot(`
Object {
{
"x": 75,
"y": 60,
}
@ -824,7 +825,7 @@ describe("Test Linear Elements", () => {
textElement,
);
expect(position).toMatchInlineSnapshot(`
Object {
{
"x": 85.82201843191861,
"y": 75.63461309860818,
}
@ -939,11 +940,11 @@ describe("Test Linear Elements", () => {
expect(textElement.angle).toBe(0);
expect(getBoundTextElementPosition(arrow, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 75,
"y": 60,
}
`);
{
"x": 75,
"y": 60,
}
`);
expect(textElement.text).toMatchInlineSnapshot(`
"Online whiteboard
collaboration made
@ -951,26 +952,26 @@ describe("Test Linear Elements", () => {
`);
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
.toMatchInlineSnapshot(`
Array [
20,
20,
105,
80,
55.45893770831013,
45,
]
`);
[
20,
20,
105,
80,
55.45893770831013,
45,
]
`);
rotate(container, -35, 55);
expect(container.angle).toMatchInlineSnapshot(`1.3988061968364685`);
expect(textElement.angle).toBe(0);
expect(getBoundTextElementPosition(container, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 21.73926141863671,
"y": 73.31003398390868,
}
`);
{
"x": 21.73926141863671,
"y": 73.31003398390868,
}
`);
expect(textElement.text).toMatchInlineSnapshot(`
"Online whiteboard
collaboration made
@ -978,15 +979,15 @@ describe("Test Linear Elements", () => {
`);
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
.toMatchInlineSnapshot(`
Array [
20,
20,
102.41961302274555,
86.49012635273976,
55.45893770831013,
45,
]
`);
[
20,
20,
102.41961302274555,
86.49012635273976,
55.45893770831013,
45,
]
`);
});
it("should resize and position the bound text and bounding box correctly when 3 pointer arrow element resized", () => {
@ -1004,11 +1005,11 @@ describe("Test Linear Elements", () => {
expect(container.height).toBe(50);
expect(getBoundTextElementPosition(container, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 75,
"y": 60,
}
`);
{
"x": 75,
"y": 60,
}
`);
expect(textElement.text).toMatchInlineSnapshot(`
"Online whiteboard
collaboration made
@ -1016,33 +1017,33 @@ describe("Test Linear Elements", () => {
`);
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
.toMatchInlineSnapshot(`
Array [
20,
20,
105,
80,
55.45893770831013,
45,
]
`);
[
20,
20,
105,
80,
55.45893770831013,
45,
]
`);
resize(container, "ne", [300, 200]);
expect({ width: container.width, height: container.height })
.toMatchInlineSnapshot(`
Object {
"height": 130,
"width": 367,
}
`);
{
"height": 130,
"width": 367,
}
`);
expect(getBoundTextElementPosition(container, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 272,
"y": 45,
}
`);
{
"x": 272,
"y": 45,
}
`);
expect((h.elements[1] as ExcalidrawTextElementWithContainer).text)
.toMatchInlineSnapshot(`
"Online whiteboard
@ -1050,15 +1051,15 @@ describe("Test Linear Elements", () => {
`);
expect(LinearElementEditor.getElementAbsoluteCoords(container, true))
.toMatchInlineSnapshot(`
Array [
20,
35,
502,
95,
205.9061448421403,
52.5,
]
`);
[
20,
35,
502,
95,
205.9061448421403,
52.5,
]
`);
});
it("should resize and position the bound text correctly when 2 pointer linear element resized", () => {
@ -1072,11 +1073,11 @@ describe("Test Linear Elements", () => {
expect(container.width).toBe(40);
expect(getBoundTextElementPosition(container, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 25,
"y": 10,
}
`);
{
"x": 25,
"y": 10,
}
`);
expect(textElement.text).toMatchInlineSnapshot(`
"Online whiteboard
collaboration made
@ -1089,19 +1090,19 @@ describe("Test Linear Elements", () => {
expect({ width: container.width, height: container.height })
.toMatchInlineSnapshot(`
Object {
"height": 130,
"width": 340,
}
`);
{
"height": 130,
"width": 340,
}
`);
expect(getBoundTextElementPosition(container, textElement))
.toMatchInlineSnapshot(`
Object {
"x": 75,
"y": -5,
}
`);
{
"x": 75,
"y": -5,
}
`);
expect(textElement.text).toMatchInlineSnapshot(`
"Online whiteboard
collaboration made easy"
@ -1154,7 +1155,7 @@ describe("Test Linear Elements", () => {
"Online whiteboard collaboration
made easy"
`);
const handleBindTextResizeSpy = jest.spyOn(
const handleBindTextResizeSpy = vi.spyOn(
textElementUtils,
"handleBindTextResize",
);

View file

@ -12,11 +12,12 @@ import {
} from "../element/types";
import { UI, Pointer, Keyboard } from "./helpers/ui";
import { KEYS } from "../keys";
import { vi } from "vitest";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View file

@ -10,11 +10,12 @@ import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
import { ExcalidrawLinearElement } from "../element/types";
import { reseed } from "../random";
import { vi } from "vitest";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<Excalidraw/> <MainMenu/> should render main menu with host menu items if passed from host 1`] = `
exports[`<Excalidraw/> > <MainMenu/> > should render main menu with host menu items if passed from host 1`] = `
<div
class="dropdown-menu"
data-testid="dropdown-menu"
@ -108,7 +108,7 @@ exports[`<Excalidraw/> <MainMenu/> should render main menu with host menu items
</div>
`;
exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should render menu with default items when "UIOPtions" is "undefined" 1`] = `
exports[`<Excalidraw/> > Test UIOptions prop > Test canvasActions > should render menu with default items when "UIOPtions" is "undefined" 1`] = `
<div
class="dropdown-menu"
data-testid="dropdown-menu"
@ -516,6 +516,7 @@ exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should render menu
style="margin-top: 0.5rem;"
>
<div
data-testid="canvas-background-label"
style="font-size: .75rem; margin-bottom: .5rem;"
>
Canvas background

View file

@ -1,8 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`exportToSvg with default arguments 1`] = `
Object {
"activeTool": Object {
exports[`exportToSvg > with default arguments 1`] = `
{
"activeEmbeddable": null,
"activeTool": {
"customType": null,
"lastActiveTool": null,
"locked": false,
@ -39,7 +40,7 @@ Object {
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"frameRendering": Object {
"frameRendering": {
"clip": true,
"enabled": true,
"name": true,
@ -58,21 +59,21 @@ Object {
"openMenu": null,
"openPopup": null,
"openSidebar": null,
"pasteDialog": Object {
"pasteDialog": {
"data": null,
"shown": false,
},
"penDetected": false,
"penMode": false,
"pendingImageElementId": null,
"previousSelectedElementIds": Object {},
"previousSelectedElementIds": {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": Object {},
"selectedElementIds": {},
"selectedElementsAreBeingDragged": false,
"selectedGroupIds": Object {},
"selectedGroupIds": {},
"selectedLinearElement": null,
"selectionElement": null,
"shouldCacheIgnoreZoom": false,
@ -80,13 +81,13 @@ Object {
"showStats": false,
"showWelcomeScreen": false,
"startBoundElement": null,
"suggestedBindings": Array [],
"suggestedBindings": [],
"theme": "light",
"toast": null,
"viewBackgroundColor": "#ffffff",
"viewModeEnabled": false,
"zenModeEnabled": false,
"zoom": Object {
"zoom": {
"value": 1,
},
}

View file

@ -1,6 +1,6 @@
import { fireEvent, GlobalTestState, toggleMenu, render } from "../test-utils";
import { Excalidraw, Footer, MainMenu } from "../../packages/excalidraw/index";
import { queryByText, queryByTestId } from "@testing-library/react";
import { queryByText, queryByTestId, screen } from "@testing-library/react";
import { GRID_SIZE, THEME } from "../../constants";
import { t } from "../../i18n";
import { useMemo } from "react";
@ -42,7 +42,7 @@ describe("<Excalidraw/>", () => {
container.getElementsByClassName("disable-zen-mode--visible").length,
).toBe(0);
expect(h.state.zenModeEnabled).toBe(true);
screen.debug();
fireEvent.contextMenu(GlobalTestState.canvas, {
button: 2,
clientX: 1,
@ -74,7 +74,8 @@ describe("<Excalidraw/>", () => {
</Footer>
</Excalidraw>,
));
expect(container.querySelector(".footer-center")).toMatchInlineSnapshot(`
expect(container.querySelector(".footer-center")).toMatchInlineSnapshot(
`
<div
class="footer-center zen-mode-transition"
>
@ -82,7 +83,8 @@ describe("<Excalidraw/>", () => {
This is a custom footer
</div>
</div>
`);
`,
);
});
describe("Test gridModeEnabled prop", () => {
@ -199,6 +201,23 @@ describe("<Excalidraw/>", () => {
);
//open menu
toggleMenu(container);
expect(queryByTestId(container, "canvas-background-label")).toBeNull();
expect(queryByTestId(container, "canvas-background-picker")).toBeNull();
});
it("should hide the canvas background picker even if passed if the `canvasActions.changeViewBackgroundColor` is set to false", async () => {
const { container } = await render(
<Excalidraw
UIOptions={{ canvasActions: { changeViewBackgroundColor: false } }}
>
<MainMenu>
<MainMenu.DefaultItems.ChangeCanvasBackground />
</MainMenu>
</Excalidraw>,
);
//open menu
toggleMenu(container);
expect(queryByTestId(container, "canvas-background-label")).toBeNull();
expect(queryByTestId(container, "canvas-background-picker")).toBeNull();
});

View file

@ -1,15 +1,13 @@
import * as utils from "../../packages/utils";
import { diagramFactory } from "../fixtures/diagramFixture";
import { vi } from "vitest";
import * as mockedSceneExportUtils from "../../scene/export";
import { MIME_TYPES } from "../../constants";
jest.mock("../../scene/export", () => ({
__esmodule: true,
...jest.requireActual("../../scene/export"),
exportToSvg: jest.fn(),
}));
const exportToSvgSpy = vi.spyOn(mockedSceneExportUtils, "exportToSvg");
describe("exportToCanvas", () => {
describe("exportToCanvas", async () => {
const EXPORT_PADDING = 10;
it("with default arguments", async () => {
@ -32,10 +30,9 @@ describe("exportToCanvas", () => {
});
});
describe("exportToBlob", () => {
describe("exportToBlob", async () => {
describe("mime type", () => {
afterEach(jest.restoreAllMocks);
// afterEach(vi.restoreAllMocks);
it("should change image/jpg to image/jpeg", async () => {
const blob = await utils.exportToBlob({
...diagramFactory(),
@ -48,7 +45,6 @@ describe("exportToBlob", () => {
});
expect(blob?.type).toBe(MIME_TYPES.jpg);
});
it("should default to image/png", async () => {
const blob = await utils.exportToBlob({
...diagramFactory(),
@ -57,16 +53,14 @@ describe("exportToBlob", () => {
});
it("should warn when using quality with image/png", async () => {
const consoleSpy = jest
const consoleSpy = vi
.spyOn(console, "warn")
.mockImplementationOnce(() => void 0);
await utils.exportToBlob({
...diagramFactory(),
mimeType: MIME_TYPES.png,
quality: 1,
});
expect(consoleSpy).toHaveBeenCalledWith(
`"quality" will be ignored for "${MIME_TYPES.png}" mimeType`,
);
@ -75,10 +69,12 @@ describe("exportToBlob", () => {
});
describe("exportToSvg", () => {
const mockedExportUtil = mockedSceneExportUtils.exportToSvg as jest.Mock;
const passedElements = () => mockedExportUtil.mock.calls[0][0];
const passedOptions = () => mockedExportUtil.mock.calls[0][1];
afterEach(jest.resetAllMocks);
const passedElements = () => exportToSvgSpy.mock.calls[0][0];
const passedOptions = () => exportToSvgSpy.mock.calls[0][1];
afterEach(() => {
vi.clearAllMocks();
});
it("with default arguments", async () => {
await utils.exportToSvg({

View file

@ -17,10 +17,11 @@ import {
} from "./test-utils";
import { defaultLang } from "../i18n";
import { FONT_FAMILY } from "../constants";
import { vi } from "vitest";
const { h } = window;
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
const mouse = new Pointer("mouse");
const finger1 = new Pointer("touch", 1);
@ -156,6 +157,7 @@ describe("regression tests", () => {
}
it("change the properties of a shape", () => {
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(10, 10);
togglePopover("Background");

View file

@ -9,11 +9,12 @@ import { ExcalidrawTextElement } from "../element/types";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
import { KEYS } from "../keys";
import { vi } from "vitest";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

File diff suppressed because one or more lines are too long

View file

@ -61,7 +61,7 @@ describe("exportToSvg", () => {
);
expect(svgElement.getAttribute("filter")).toMatchInlineSnapshot(
`"themeFilter"`,
'"_themeFilter_f32792"',
);
});

View file

@ -13,11 +13,12 @@ import { reseed } from "../random";
import { API } from "./helpers/api";
import { Keyboard, Pointer, UI } from "./helpers/ui";
import { SHAPES } from "../shapes";
import { vi } from "vitest";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderScene");
const renderScene = vi.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();
@ -28,6 +29,74 @@ const { h } = window;
const mouse = new Pointer("mouse");
describe("box-selection", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
});
it("should allow adding to selection via box-select when holding shift", async () => {
const rect1 = API.createElement({
type: "rectangle",
x: 0,
y: 0,
width: 50,
height: 50,
backgroundColor: "red",
fillStyle: "solid",
});
const rect2 = API.createElement({
type: "rectangle",
x: 100,
y: 0,
width: 50,
height: 50,
});
h.elements = [rect1, rect2];
mouse.downAt(175, -20);
mouse.moveTo(85, 70);
mouse.up();
assertSelectedElements([rect2.id]);
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.downAt(75, -20);
mouse.moveTo(-15, 70);
mouse.up();
});
assertSelectedElements([rect2.id, rect1.id]);
});
it("should (de)select element when box-selecting over and out while not holding shift", async () => {
const rect1 = API.createElement({
type: "rectangle",
x: 0,
y: 0,
width: 50,
height: 50,
backgroundColor: "red",
fillStyle: "solid",
});
h.elements = [rect1];
mouse.downAt(75, -20);
mouse.moveTo(-15, 70);
assertSelectedElements([rect1.id]);
mouse.moveTo(100, -100);
assertSelectedElements([]);
mouse.up();
assertSelectedElements([]);
});
});
describe("inner box-selection", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);

View file

@ -228,3 +228,19 @@ export const togglePopover = (label: string) => {
UI.clickLabeledElement(label);
};
expect.extend({
toBeNonNaNNumber(received) {
const pass = typeof received === "number" && !isNaN(received);
if (pass) {
return {
message: () => `expected ${received} not to be a non-NaN number`,
pass: true,
};
}
return {
message: () => `expected ${received} to be a non-NaN number`,
pass: false,
};
},
});

View file

@ -90,6 +90,7 @@ const populateElements = (
{ ...h.state, ...appState, selectedElementIds },
h.elements,
h.state,
null,
),
...appState,
selectedElementIds,