mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	refactor: separate elements logic into a standalone package (#9285)
This commit is contained in:
		
							
								
								
									
										271
									
								
								packages/common/tests/keys.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								packages/common/tests/keys.test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,271 @@
 | 
			
		||||
import { KEYS, matchKey } from "../src/keys";
 | 
			
		||||
 | 
			
		||||
describe("key matcher", async () => {
 | 
			
		||||
  it("should not match unexpected key", async () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "N" }), KEYS.Y),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "Unidentified" }), KEYS.Z),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "z" }), KEYS.Y),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "y" }), KEYS.Z),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "Z" }), KEYS.Y),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "Y" }), KEYS.Z),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should match key (case insensitive) when key is latin", async () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "z" }), KEYS.Z),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "y" }), KEYS.Y),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "Z" }), KEYS.Z),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "Y" }), KEYS.Y),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should match key on QWERTY, QWERTZ, AZERTY", async () => {
 | 
			
		||||
    // QWERTY
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "y", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // QWERTZ
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyY" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "y", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // AZERTY
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyW" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "y", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should match key on DVORAK, COLEMAK", async () => {
 | 
			
		||||
    // DVORAK
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeySemicolon" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "y", code: "KeyF" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // COLEMAK
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "y", code: "KeyJ" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should match key on Turkish-Q", async () => {
 | 
			
		||||
    // Turkish-Q
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyN" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "Y", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should not fallback when code is not defined", async () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "я" }), KEYS.Z),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(new KeyboardEvent("keydown", { key: "卜" }), KEYS.Y),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should not fallback when code is incorrect", async () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "z", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "Y", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeFalsy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should fallback to code when key is non-latin", async () => {
 | 
			
		||||
    // Macedonian
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "з", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ѕ", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Russian
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "я", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "н", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Serbian
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ѕ", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "з", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Greek
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ζ", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "υ", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Hebrew
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ז", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ט", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Cangjie - Traditional
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "重", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "卜", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // Japanese
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "つ", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ん", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
    // 2-Set Korean
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ㅋ", code: "KeyZ" }),
 | 
			
		||||
        KEYS.Z,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
    expect(
 | 
			
		||||
      matchKey(
 | 
			
		||||
        new KeyboardEvent("keydown", { key: "ㅛ", code: "KeyY" }),
 | 
			
		||||
        KEYS.Y,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										62
									
								
								packages/common/tests/queue.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								packages/common/tests/queue.test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
import { Queue } from "../src/queue";
 | 
			
		||||
 | 
			
		||||
describe("Queue", () => {
 | 
			
		||||
  const calls: any[] = [];
 | 
			
		||||
 | 
			
		||||
  const createJobFactory =
 | 
			
		||||
    <T>(
 | 
			
		||||
      // for purpose of this test, Error object will become a rejection value
 | 
			
		||||
      resolutionOrRejectionValue: T,
 | 
			
		||||
      ms = 1,
 | 
			
		||||
    ) =>
 | 
			
		||||
    () => {
 | 
			
		||||
      return new Promise<T>((resolve, reject) => {
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          if (resolutionOrRejectionValue instanceof Error) {
 | 
			
		||||
            reject(resolutionOrRejectionValue);
 | 
			
		||||
          } else {
 | 
			
		||||
            resolve(resolutionOrRejectionValue);
 | 
			
		||||
          }
 | 
			
		||||
        }, ms);
 | 
			
		||||
      }).then((x) => {
 | 
			
		||||
        calls.push(x);
 | 
			
		||||
        return x;
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    calls.length = 0;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should await and resolve values in order of enqueueing", async () => {
 | 
			
		||||
    const queue = new Queue();
 | 
			
		||||
 | 
			
		||||
    const p1 = queue.push(createJobFactory("A", 50));
 | 
			
		||||
    const p2 = queue.push(createJobFactory("B"));
 | 
			
		||||
    const p3 = queue.push(createJobFactory("C"));
 | 
			
		||||
 | 
			
		||||
    expect(await p3).toBe("C");
 | 
			
		||||
    expect(await p2).toBe("B");
 | 
			
		||||
    expect(await p1).toBe("A");
 | 
			
		||||
 | 
			
		||||
    expect(calls).toEqual(["A", "B", "C"]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("should reject a job if it throws, and not affect other jobs", async () => {
 | 
			
		||||
    const queue = new Queue();
 | 
			
		||||
 | 
			
		||||
    const err = new Error("B");
 | 
			
		||||
 | 
			
		||||
    queue.push(createJobFactory("A", 50));
 | 
			
		||||
    const p2 = queue.push(createJobFactory(err));
 | 
			
		||||
    const p3 = queue.push(createJobFactory("C"));
 | 
			
		||||
 | 
			
		||||
    const p2err = p2.catch((err) => err);
 | 
			
		||||
 | 
			
		||||
    await p3;
 | 
			
		||||
 | 
			
		||||
    expect(await p2err).toBe(err);
 | 
			
		||||
 | 
			
		||||
    expect(calls).toEqual(["A", "C"]);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										31
									
								
								packages/common/tests/url.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								packages/common/tests/url.test.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import { normalizeLink } from "../src/url";
 | 
			
		||||
 | 
			
		||||
describe("normalizeLink", () => {
 | 
			
		||||
  // NOTE not an extensive XSS test suite, just to check if we're not
 | 
			
		||||
  // regressing in sanitization
 | 
			
		||||
  it("should sanitize links", () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      // eslint-disable-next-line no-script-url
 | 
			
		||||
      normalizeLink(`javascript://%0aalert(document.domain)`).startsWith(
 | 
			
		||||
        // eslint-disable-next-line no-script-url
 | 
			
		||||
        `javascript:`,
 | 
			
		||||
      ),
 | 
			
		||||
    ).toBe(false);
 | 
			
		||||
    expect(normalizeLink("ola")).toBe("ola");
 | 
			
		||||
    expect(normalizeLink(" ola")).toBe("ola");
 | 
			
		||||
 | 
			
		||||
    expect(normalizeLink("https://www.excalidraw.com")).toBe(
 | 
			
		||||
      "https://www.excalidraw.com",
 | 
			
		||||
    );
 | 
			
		||||
    expect(normalizeLink("www.excalidraw.com")).toBe("www.excalidraw.com");
 | 
			
		||||
    expect(normalizeLink("/ola")).toBe("/ola");
 | 
			
		||||
    expect(normalizeLink("http://test")).toBe("http://test");
 | 
			
		||||
    expect(normalizeLink("ftp://test")).toBe("ftp://test");
 | 
			
		||||
    expect(normalizeLink("file://")).toBe("file://");
 | 
			
		||||
    expect(normalizeLink("file://")).toBe("file://");
 | 
			
		||||
    expect(normalizeLink("[test](https://test)")).toBe("[test](https://test)");
 | 
			
		||||
    expect(normalizeLink("[[test]]")).toBe("[[test]]");
 | 
			
		||||
    expect(normalizeLink("<test>")).toBe("<test>");
 | 
			
		||||
    expect(normalizeLink("test&")).toBe("test&");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user