snapshot of https://github.com/excalidraw/excalidraw.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Tests / test (push) Failing after 15s
Some checks failed
Tests / test (push) Failing after 15s
This commit is contained in:
127
packages/utils/tests/__snapshots__/export.test.ts.snap
Normal file
127
packages/utils/tests/__snapshots__/export.test.ts.snap
Normal file
@@ -0,0 +1,127 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`exportToSvg > with default arguments 1`] = `
|
||||
{
|
||||
"activeEmbeddable": null,
|
||||
"activeLockedId": null,
|
||||
"activeTool": {
|
||||
"customType": null,
|
||||
"fromSelection": false,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
"bindMode": "orbit",
|
||||
"bindingPreference": "enabled",
|
||||
"boxSelectionMode": "contain",
|
||||
"collaborators": Map {},
|
||||
"colorTopPicks": {
|
||||
"bucketFill": null,
|
||||
"elementBackground": null,
|
||||
"elementStroke": null,
|
||||
"stickyNoteBackground": null,
|
||||
"stickyNoteStroke": null,
|
||||
},
|
||||
"contextMenu": null,
|
||||
"croppingElementId": null,
|
||||
"currentHoveredFontFamily": null,
|
||||
"currentItemArrowType": "round",
|
||||
"currentItemBackgroundColor": "transparent",
|
||||
"currentItemEndArrowhead": "arrow",
|
||||
"currentItemFillStyle": "solid",
|
||||
"currentItemFontFamily": 5,
|
||||
"currentItemFontSize": 20,
|
||||
"currentItemOpacity": 100,
|
||||
"currentItemRoughness": 1,
|
||||
"currentItemRoundness": "sharp",
|
||||
"currentItemStartArrowhead": null,
|
||||
"currentItemStickynoteBackgroundColor": "#ffdf6b",
|
||||
"currentItemStickynoteStrokeColor": "#1e1e1e",
|
||||
"currentItemStrokeColor": "#1e1e1e",
|
||||
"currentItemStrokeStyle": "solid",
|
||||
"currentItemStrokeVariability": "constant",
|
||||
"currentItemStrokeWidthKey": "medium",
|
||||
"currentItemTextAlign": "left",
|
||||
"cursorButton": "up",
|
||||
"defaultSidebarDockedPreference": false,
|
||||
"editingFrame": null,
|
||||
"editingGroupId": null,
|
||||
"editingTextElement": null,
|
||||
"elementsToHighlight": null,
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"exportEmbedScene": false,
|
||||
"exportPadding": undefined,
|
||||
"exportScale": 1,
|
||||
"exportWithDarkMode": false,
|
||||
"fileHandle": null,
|
||||
"frameRendering": {
|
||||
"clip": true,
|
||||
"enabled": true,
|
||||
"name": true,
|
||||
"outline": true,
|
||||
},
|
||||
"frameToHighlight": null,
|
||||
"gridModeEnabled": false,
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"hoveredArrowTextAnchor": null,
|
||||
"hoveredElementIds": {},
|
||||
"inputDevice": "auto",
|
||||
"isBindingEnabled": true,
|
||||
"isCropping": false,
|
||||
"isLoading": false,
|
||||
"isMidpointSnappingEnabled": true,
|
||||
"isResizing": false,
|
||||
"isRotating": false,
|
||||
"lastPointerDownWith": "mouse",
|
||||
"lockedMultiSelections": {},
|
||||
"multiElement": null,
|
||||
"name": "name",
|
||||
"newElement": null,
|
||||
"objectsSnapModeEnabled": false,
|
||||
"openDialog": null,
|
||||
"openMenu": null,
|
||||
"openPopup": null,
|
||||
"openSidebar": null,
|
||||
"originSnapOffset": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"penDetected": false,
|
||||
"penMode": false,
|
||||
"preferredSelectionTool": {
|
||||
"initialized": false,
|
||||
"type": "selection",
|
||||
},
|
||||
"previousSelectedElementIds": {},
|
||||
"resizingElement": null,
|
||||
"scrollConstraints": null,
|
||||
"scrollX": 0,
|
||||
"scrollY": 0,
|
||||
"scrolledOutside": false,
|
||||
"searchMatches": null,
|
||||
"selectedElementIds": {},
|
||||
"selectedElementsAreBeingDragged": false,
|
||||
"selectedGroupIds": {},
|
||||
"selectedLinearElement": null,
|
||||
"selectionElement": null,
|
||||
"shouldCacheIgnoreZoom": false,
|
||||
"showHyperlinkPopup": false,
|
||||
"showWelcomeScreen": false,
|
||||
"snapLines": [],
|
||||
"stats": {
|
||||
"open": false,
|
||||
"panels": 3,
|
||||
},
|
||||
"suggestedBinding": null,
|
||||
"theme": "light",
|
||||
"toast": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": {
|
||||
"value": 1,
|
||||
},
|
||||
}
|
||||
`;
|
||||
102
packages/utils/tests/__snapshots__/utils.test.ts.snap
Normal file
102
packages/utils/tests/__snapshots__/utils.test.ts.snap
Normal file
@@ -0,0 +1,102 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`exportToSvg > with default arguments 1`] = `
|
||||
{
|
||||
"activeEmbeddable": null,
|
||||
"activeTool": {
|
||||
"customType": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
"collaborators": Map {},
|
||||
"contextMenu": null,
|
||||
"currentChartType": "bar",
|
||||
"currentItemBackgroundColor": "transparent",
|
||||
"currentItemEndArrowhead": "arrow",
|
||||
"currentItemFillStyle": "solid",
|
||||
"currentItemFontFamily": 1,
|
||||
"currentItemFontSize": 20,
|
||||
"currentItemOpacity": 100,
|
||||
"currentItemRoughness": 1,
|
||||
"currentItemRoundness": "round",
|
||||
"currentItemStartArrowhead": null,
|
||||
"currentItemStrokeColor": "#1e1e1e",
|
||||
"currentItemStrokeStyle": "solid",
|
||||
"currentItemStrokeWidthKey": "medium",
|
||||
"currentItemTextAlign": "left",
|
||||
"cursorButton": "up",
|
||||
"defaultSidebarDockedPreference": false,
|
||||
"draggingElement": null,
|
||||
"editingElement": null,
|
||||
"editingFrame": null,
|
||||
"editingGroupId": null,
|
||||
"editingLinearElement": null,
|
||||
"elementsToHighlight": null,
|
||||
"errorMessage": null,
|
||||
"exportBackground": true,
|
||||
"exportEmbedScene": false,
|
||||
"exportPadding": undefined,
|
||||
"exportScale": 1,
|
||||
"exportWithDarkMode": false,
|
||||
"fileHandle": null,
|
||||
"followedBy": Set {},
|
||||
"frameRendering": {
|
||||
"clip": true,
|
||||
"enabled": true,
|
||||
"name": true,
|
||||
"outline": true,
|
||||
},
|
||||
"frameToHighlight": null,
|
||||
"gridSize": null,
|
||||
"isBindingEnabled": true,
|
||||
"isLoading": false,
|
||||
"isResizing": false,
|
||||
"isRotating": false,
|
||||
"lastPointerDownWith": "mouse",
|
||||
"multiElement": null,
|
||||
"name": "name",
|
||||
"objectsSnapModeEnabled": false,
|
||||
"openDialog": null,
|
||||
"openMenu": null,
|
||||
"openPopup": null,
|
||||
"openSidebar": null,
|
||||
"originSnapOffset": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"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": false,
|
||||
"snapLines": [],
|
||||
"startBoundElement": null,
|
||||
"suggestedBindings": [],
|
||||
"theme": "light",
|
||||
"toast": null,
|
||||
"userToFollow": null,
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"viewModeEnabled": false,
|
||||
"zenModeEnabled": false,
|
||||
"zoom": {
|
||||
"value": 1,
|
||||
},
|
||||
}
|
||||
`;
|
||||
219
packages/utils/tests/export.test.ts
Normal file
219
packages/utils/tests/export.test.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import { MIME_TYPES } from "@excalidraw/common";
|
||||
import * as clipboardModule from "@excalidraw/excalidraw/clipboard";
|
||||
import * as imageModule from "@excalidraw/excalidraw/data/image";
|
||||
import * as jsonModule from "@excalidraw/excalidraw/data/json";
|
||||
import * as mockedSceneExportUtils from "@excalidraw/excalidraw/scene/export";
|
||||
import { diagramFactory } from "@excalidraw/excalidraw/tests/fixtures/diagramFixture";
|
||||
import { vi } from "vitest";
|
||||
|
||||
import * as utils from "../src";
|
||||
|
||||
const exportToSvgSpy = vi.spyOn(mockedSceneExportUtils, "exportToSvg");
|
||||
|
||||
describe("exportToCanvas", async () => {
|
||||
const EXPORT_PADDING = 10;
|
||||
|
||||
it("with default arguments", async () => {
|
||||
const canvas = await utils.exportToCanvas({
|
||||
...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
|
||||
});
|
||||
|
||||
expect(canvas.width).toBe(100 + 2 * EXPORT_PADDING);
|
||||
expect(canvas.height).toBe(100 + 2 * EXPORT_PADDING);
|
||||
});
|
||||
|
||||
it("when custom width and height", async () => {
|
||||
const canvas = await utils.exportToCanvas({
|
||||
...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
|
||||
getDimensions: () => ({ width: 200, height: 200, scale: 1 }),
|
||||
});
|
||||
|
||||
expect(canvas.width).toBe(200);
|
||||
expect(canvas.height).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe("exportToBlob", async () => {
|
||||
describe("mime type", () => {
|
||||
it("should change image/jpg to image/jpeg", async () => {
|
||||
const blob = await utils.exportToBlob({
|
||||
...diagramFactory(),
|
||||
getDimensions: (width, height) => ({ width, height, scale: 1 }),
|
||||
// testing typo in MIME type (jpg → jpeg)
|
||||
mimeType: "image/jpg",
|
||||
appState: {
|
||||
exportBackground: true,
|
||||
},
|
||||
});
|
||||
expect(blob?.type).toBe(MIME_TYPES.jpg);
|
||||
});
|
||||
it("should default to image/png", async () => {
|
||||
const blob = await utils.exportToBlob({
|
||||
...diagramFactory(),
|
||||
});
|
||||
expect(blob?.type).toBe(MIME_TYPES.png);
|
||||
});
|
||||
|
||||
it("should warn when using quality with image/png", async () => {
|
||||
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`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("exportToSvg", () => {
|
||||
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({
|
||||
...diagramFactory({
|
||||
overrides: { appState: void 0 },
|
||||
}),
|
||||
});
|
||||
|
||||
const passedOptionsWhenDefault = {
|
||||
...passedOptions(),
|
||||
// To avoid varying snapshots
|
||||
name: "name",
|
||||
};
|
||||
expect(passedElements().length).toBe(3);
|
||||
expect(passedOptionsWhenDefault).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// Regression test: when all elements are deleted, exportToSvg should pass
|
||||
// zero elements to the lower-level export (line 184-188 of export.ts calls
|
||||
// getNonDeletedElements which filters them out).
|
||||
it("with deleted elements", async () => {
|
||||
await utils.exportToSvg({
|
||||
...diagramFactory({
|
||||
overrides: { appState: void 0 },
|
||||
elementOverrides: { isDeleted: true },
|
||||
}),
|
||||
});
|
||||
|
||||
expect(passedElements().length).toBe(0);
|
||||
});
|
||||
|
||||
it("with exportPadding", async () => {
|
||||
await utils.exportToSvg({
|
||||
...diagramFactory({
|
||||
overrides: { appState: { name: "diagram name" } },
|
||||
}),
|
||||
exportPadding: 0,
|
||||
});
|
||||
|
||||
expect(passedElements().length).toBe(3);
|
||||
expect(passedOptions()).toEqual(
|
||||
expect.objectContaining({ exportPadding: 0 }),
|
||||
);
|
||||
});
|
||||
|
||||
it("with exportEmbedScene", async () => {
|
||||
await utils.exportToSvg({
|
||||
...diagramFactory({
|
||||
overrides: {
|
||||
appState: { name: "diagram name", exportEmbedScene: true },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
expect(passedElements().length).toBe(3);
|
||||
expect(passedOptions().exportEmbedScene).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Elements persisted before `created` existed omit it (modeled with the cast
|
||||
// below, as the declared input type is a complete element). Every public
|
||||
// export entry point restores its input, so such elements must reach the
|
||||
// lower-level code restored, with `created: null`.
|
||||
describe("elements lacking creation metadata", () => {
|
||||
const legacyDiagram = () => {
|
||||
const { elements, ...rest } = diagramFactory({
|
||||
overrides: { appState: void 0 },
|
||||
});
|
||||
return {
|
||||
...rest,
|
||||
elements: elements.map(
|
||||
({ created, ...element }) => element,
|
||||
) as unknown as typeof elements,
|
||||
};
|
||||
};
|
||||
const createdOf = (elements: readonly { created: number | null }[]) =>
|
||||
elements.map((element) => element.created);
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("exportToSvg and exportToCanvas restore the elements", async () => {
|
||||
const exportToSvgSpy = vi.spyOn(mockedSceneExportUtils, "exportToSvg");
|
||||
const diagram = legacyDiagram();
|
||||
|
||||
await utils.exportToSvg(diagram);
|
||||
|
||||
expect(createdOf(exportToSvgSpy.mock.calls[0][0])).toEqual([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
]);
|
||||
expect(diagram.elements[0]).not.toHaveProperty("created");
|
||||
|
||||
const exportToCanvasSpy = vi.spyOn(
|
||||
mockedSceneExportUtils,
|
||||
"exportToCanvas",
|
||||
);
|
||||
const canvas = await utils.exportToCanvas(diagram);
|
||||
|
||||
expect(createdOf(exportToCanvasSpy.mock.calls[0][0])).toEqual([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
]);
|
||||
expect(canvas.width).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("exportToBlob embeds the restored scene into png metadata", async () => {
|
||||
const serializeSpy = vi.spyOn(jsonModule, "serializeAsJSON");
|
||||
vi.spyOn(imageModule, "encodePngMetadata").mockImplementation(
|
||||
async ({ blob }) => blob,
|
||||
);
|
||||
|
||||
await utils.exportToBlob({
|
||||
...legacyDiagram(),
|
||||
mimeType: MIME_TYPES.png,
|
||||
appState: { exportEmbedScene: true },
|
||||
});
|
||||
|
||||
expect(serializeSpy).toHaveBeenCalledTimes(1);
|
||||
expect(createdOf(serializeSpy.mock.calls[0][0])).toEqual([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
it("exportToClipboard copies the restored elements as json", async () => {
|
||||
const copySpy = vi
|
||||
.spyOn(clipboardModule, "copyToClipboard")
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
await utils.exportToClipboard({ ...legacyDiagram(), type: "json" });
|
||||
|
||||
expect(copySpy).toHaveBeenCalledTimes(1);
|
||||
expect(createdOf(copySpy.mock.calls[0][0])).toEqual([null, null, null]);
|
||||
});
|
||||
});
|
||||
146
packages/utils/tests/geometry.test.ts
Normal file
146
packages/utils/tests/geometry.test.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
pointFrom,
|
||||
lineSegment,
|
||||
polygon,
|
||||
pointOnLineSegment,
|
||||
polygonIncludesPoint,
|
||||
segmentsIntersectAt,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import type {
|
||||
GlobalPoint,
|
||||
LineSegment,
|
||||
Polygon,
|
||||
Radians,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
import { pointInEllipse, pointOnEllipse, type Ellipse } from "../src/shape";
|
||||
|
||||
describe("point and line", () => {
|
||||
// const l: Line<GlobalPoint> = line(point(1, 0), point(1, 2));
|
||||
|
||||
// it("point on left or right of line", () => {
|
||||
// expect(pointLeftofLine(point(0, 1), l)).toBe(true);
|
||||
// expect(pointLeftofLine(point(1, 1), l)).toBe(false);
|
||||
// expect(pointLeftofLine(point(2, 1), l)).toBe(false);
|
||||
|
||||
// expect(pointRightofLine(point(0, 1), l)).toBe(false);
|
||||
// expect(pointRightofLine(point(1, 1), l)).toBe(false);
|
||||
// expect(pointRightofLine(point(2, 1), l)).toBe(true);
|
||||
// });
|
||||
|
||||
const s: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 0),
|
||||
pointFrom(1, 2),
|
||||
);
|
||||
|
||||
it("point on the line", () => {
|
||||
expect(pointOnLineSegment(pointFrom(0, 1), s)).toBe(false);
|
||||
expect(pointOnLineSegment(pointFrom(1, 1), s, 0)).toBe(true);
|
||||
expect(pointOnLineSegment(pointFrom(2, 1), s)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("point and polygon", () => {
|
||||
it("point in polygon", () => {
|
||||
const poly: Polygon<GlobalPoint> = polygon(
|
||||
pointFrom(0, 0),
|
||||
pointFrom(2, 0),
|
||||
pointFrom(2, 2),
|
||||
pointFrom(0, 2),
|
||||
);
|
||||
expect(polygonIncludesPoint(pointFrom(1, 1), poly)).toBe(true);
|
||||
expect(polygonIncludesPoint(pointFrom(3, 3), poly)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("point and ellipse", () => {
|
||||
const ellipse: Ellipse<GlobalPoint> = {
|
||||
center: pointFrom(0, 0),
|
||||
angle: 0 as Radians,
|
||||
halfWidth: 2,
|
||||
halfHeight: 1,
|
||||
};
|
||||
|
||||
it("point on ellipse", () => {
|
||||
[
|
||||
pointFrom(0, 1),
|
||||
pointFrom(0, -1),
|
||||
pointFrom(2, 0),
|
||||
pointFrom(-2, 0),
|
||||
].forEach((p) => {
|
||||
expect(pointOnEllipse(p, ellipse)).toBe(true);
|
||||
});
|
||||
expect(pointOnEllipse(pointFrom(-1.4, 0.7), ellipse, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(pointFrom(-1.4, 0.71), ellipse, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(pointFrom(1.4, 0.7), ellipse, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(pointFrom(1.4, 0.71), ellipse, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(pointFrom(1, -0.86), ellipse, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(pointFrom(1, -0.86), ellipse, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(pointFrom(-1, -0.86), ellipse, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(pointFrom(-1, -0.86), ellipse, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(pointFrom(-1, 0.8), ellipse)).toBe(false);
|
||||
expect(pointOnEllipse(pointFrom(1, -0.8), ellipse)).toBe(false);
|
||||
});
|
||||
|
||||
it("point in ellipse", () => {
|
||||
[
|
||||
pointFrom(0, 1),
|
||||
pointFrom(0, -1),
|
||||
pointFrom(2, 0),
|
||||
pointFrom(-2, 0),
|
||||
].forEach((p) => {
|
||||
expect(pointInEllipse(p, ellipse)).toBe(true);
|
||||
});
|
||||
|
||||
expect(pointInEllipse(pointFrom(-1, 0.8), ellipse)).toBe(true);
|
||||
expect(pointInEllipse(pointFrom(1, -0.8), ellipse)).toBe(true);
|
||||
|
||||
expect(pointInEllipse(pointFrom(-1, 1), ellipse)).toBe(false);
|
||||
expect(pointInEllipse(pointFrom(-1.4, 0.8), ellipse)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("line and line", () => {
|
||||
const lineA: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 4),
|
||||
pointFrom(3, 4),
|
||||
);
|
||||
const lineB: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(2, 1),
|
||||
pointFrom(2, 7),
|
||||
);
|
||||
const lineC: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 8),
|
||||
pointFrom(3, 8),
|
||||
);
|
||||
const lineD: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 8),
|
||||
pointFrom(3, 8),
|
||||
);
|
||||
const lineE: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 9),
|
||||
pointFrom(3, 9),
|
||||
);
|
||||
const lineF: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(1, 2),
|
||||
pointFrom(3, 4),
|
||||
);
|
||||
const lineG: LineSegment<GlobalPoint> = lineSegment(
|
||||
pointFrom(0, 1),
|
||||
pointFrom(2, 3),
|
||||
);
|
||||
|
||||
it("intersection", () => {
|
||||
expect(segmentsIntersectAt(lineA, lineB)).toEqual([2, 4]);
|
||||
expect(segmentsIntersectAt(lineA, lineC)).toBe(null);
|
||||
expect(segmentsIntersectAt(lineB, lineC)).toBe(null);
|
||||
expect(segmentsIntersectAt(lineC, lineD)).toBe(null); // Line overlapping line is not intersection!
|
||||
expect(segmentsIntersectAt(lineE, lineD)).toBe(null);
|
||||
expect(segmentsIntersectAt(lineF, lineG)).toBe(null);
|
||||
});
|
||||
});
|
||||
70
packages/utils/tests/utils.unmocked.test.ts
Normal file
70
packages/utils/tests/utils.unmocked.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { decodePngMetadata } from "@excalidraw/excalidraw/data/image";
|
||||
import { decodeSvgBase64Payload } from "@excalidraw/excalidraw/scene/export";
|
||||
import { API } from "@excalidraw/excalidraw/tests/helpers/api";
|
||||
|
||||
import type { ImportedDataState } from "@excalidraw/excalidraw/data/types";
|
||||
|
||||
import * as utils from "../src";
|
||||
|
||||
// NOTE this test file is using the actual API, unmocked. Hence splitting it
|
||||
// from the other test file, because I couldn't figure out how to test
|
||||
// mocked and unmocked API in the same file.
|
||||
|
||||
describe("embedding scene data", () => {
|
||||
describe("exportToSvg", () => {
|
||||
it("embedding scene data shouldn't modify them", async () => {
|
||||
const rectangle = API.createElement({ type: "rectangle" });
|
||||
const ellipse = API.createElement({ type: "ellipse" });
|
||||
|
||||
const sourceElements = [rectangle, ellipse];
|
||||
|
||||
const svgNode = await utils.exportToSvg({
|
||||
elements: sourceElements,
|
||||
appState: {
|
||||
viewBackgroundColor: "#ffffff",
|
||||
gridModeEnabled: false,
|
||||
exportEmbedScene: true,
|
||||
},
|
||||
files: null,
|
||||
});
|
||||
|
||||
const svg = svgNode.outerHTML;
|
||||
|
||||
const parsedString = decodeSvgBase64Payload({ svg });
|
||||
const importedData: ImportedDataState = JSON.parse(parsedString);
|
||||
|
||||
expect(sourceElements.map((x) => x.id)).toEqual(
|
||||
importedData.elements?.map((el) => el.id),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// skipped because we can't test png encoding right now
|
||||
// (canvas.toBlob not supported in jsdom)
|
||||
describe.skip("exportToBlob", () => {
|
||||
it("embedding scene data shouldn't modify them", async () => {
|
||||
const rectangle = API.createElement({ type: "rectangle" });
|
||||
const ellipse = API.createElement({ type: "ellipse" });
|
||||
|
||||
const sourceElements = [rectangle, ellipse];
|
||||
|
||||
const blob = await utils.exportToBlob({
|
||||
mimeType: "image/png",
|
||||
elements: sourceElements,
|
||||
appState: {
|
||||
viewBackgroundColor: "#ffffff",
|
||||
gridModeEnabled: false,
|
||||
exportEmbedScene: true,
|
||||
},
|
||||
files: null,
|
||||
});
|
||||
|
||||
const parsedString = await decodePngMetadata(blob);
|
||||
const importedData: ImportedDataState = JSON.parse(parsedString);
|
||||
|
||||
expect(sourceElements.map((x) => x.id)).toEqual(
|
||||
importedData.elements?.map((el) => el.id),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user