/* * Copyright 2015 Palantir Technologies, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Fragment } from "react/jsx-runtime"; import { GraphIcon } from "@blueprintjs/icons"; import { beforeEach, describe, expect, it, vi } from "@blueprintjs/test-commons/vitest"; import * as Utils from "./utils"; describe("Utils", () => { it("isFunction", () => { expect(Utils.isFunction(() => 3)).toBe(true); expect(Utils.isFunction(undefined)).toBe(false); }); it("isReactNodeEmpty", () => { // empty nodes expect(Utils.isReactNodeEmpty(undefined), "undefined").toBe(true); expect(Utils.isReactNodeEmpty(null), "null").toBe(true); expect(Utils.isReactNodeEmpty(""), '""').toBe(true); expect(Utils.isReactNodeEmpty([]), "[]").toBe(true); expect(Utils.isReactNodeEmpty([undefined, null, false, ""]), "array").toBe(true); // not empty nodes expect(Utils.isReactNodeEmpty(0), "0").toBe(false); expect(Utils.isReactNodeEmpty("text"), "text").toBe(false); expect(Utils.isReactNodeEmpty(
), "
").toBe(false); expect(Utils.isReactNodeEmpty([null,
]), "array").toBe(false); }); it("isBlueprintIconElement", () => { function NotAnIcon() { return ; } NotAnIcon.displayName = "NotAnIcon"; // a real @blueprintjs/icons element is recognized expect(Utils.isBlueprintIconElement(), "").toBe(true); // other elements are not, even Blueprint components with a different displayName namespace expect(Utils.isBlueprintIconElement(), "").toBe(false); expect(Utils.isBlueprintIconElement(
), "
").toBe(false); // non-elements are not expect(Utils.isBlueprintIconElement("graph"), '"graph"').toBe(false); expect(Utils.isBlueprintIconElement(undefined), "undefined").toBe(false); }); it("elementIsOrContains", () => { const child = document.createElement("span"); const parent = document.createElement("div"); const grandparent = document.createElement("div"); parent.appendChild(child); grandparent.appendChild(parent); expect(Utils.elementIsOrContains(child, child)).toBe(true); expect(Utils.elementIsOrContains(parent, child)).toBe(true); expect(Utils.elementIsOrContains(grandparent, parent)).toBe(true); expect(Utils.elementIsOrContains(grandparent, child)).toBe(true); expect(Utils.elementIsOrContains(child, parent)).toBe(false); expect(Utils.elementIsOrContains(parent, grandparent)).toBe(false); }); it("arrayLengthCompare", () => { expect(Utils.arrayLengthCompare([1, 2], [])).toBeGreaterThan(0); expect(Utils.arrayLengthCompare([1, 2], [1, 2])).toBe(0); expect(Utils.arrayLengthCompare([], [1, 2])).toBeLessThan(0); expect(Utils.arrayLengthCompare([1])).toBeGreaterThan(0); expect(Utils.arrayLengthCompare()).toBe(0); expect(Utils.arrayLengthCompare(undefined, [1])).toBeLessThan(0); }); it("approxEqual", () => { const DEFAULT_EPSILON = 0.00001; expect(Utils.approxEqual(0, DEFAULT_EPSILON)).toBe(true); expect(Utils.approxEqual(-1 * DEFAULT_EPSILON, -2 * DEFAULT_EPSILON)).toBe(true); expect(Utils.approxEqual(10, 10 + DEFAULT_EPSILON + DEFAULT_EPSILON / 10)).toBe(false); expect(Utils.approxEqual(10, 10 - DEFAULT_EPSILON - DEFAULT_EPSILON / 10)).toBe(false); }); it("clamp", () => { expect(Utils.clamp(10, 0, 20), "value between min/max").toBe(10); expect(Utils.clamp(0, 10, 20), "value below min").toBe(10); expect(Utils.clamp(40, 0, 20), "value above max").toBe(20); expect(() => Utils.clamp(0, 20, 10)).toThrow(/less than/); }); it("countDecimalPlaces", () => { expect(Utils.countDecimalPlaces(1)).toBe(0); expect(Utils.countDecimalPlaces(0.11)).toBe(2); expect(Utils.countDecimalPlaces(-1.1111111111)).toBe(10); expect(Utils.countDecimalPlaces(1e-10)).toBe(10); expect(Utils.countDecimalPlaces(NaN)).toBe(0); }); it("uniqueId", () => { const ns = "testNamespace"; const otherNs = "otherNamespace"; expect(Utils.uniqueId(ns)).toBe(`${ns}-0`); expect(Utils.uniqueId(ns)).toBe(`${ns}-1`); expect(Utils.uniqueId(ns)).toBe(`${ns}-2`); expect(Utils.uniqueId(otherNs)).toBe(`${otherNs}-0`); }); // TODO: not sure how to test this. perhaps with the help of https://github.com/alexreardon/raf-stub? it.skip("throttleEvent"); describe("ensureElement", () => { it("handles undefined/null", () => { expect(Utils.ensureElement(undefined)).toBeUndefined(); expect(Utils.ensureElement(null)).toBeUndefined(); }); it("wraps strings & numbers", () => { expect(Utils.ensureElement("foo")?.type).toBe("span"); expect(Utils.ensureElement(1234)?.type).toBe("span"); }); it("returns undefined for whitespace strings", () => { expect(Utils.ensureElement(" ")).toBeUndefined(); }); it("passes through JSX elements", () => { const el =
my element
; expect(Utils.ensureElement(el)).toBe(el); }); // React 16 only if (Fragment !== undefined) { it("wraps JSX fragments in element", () => { const el = Utils.ensureElement( <> one two three , ); expect(el?.type).toBe("span"); }); } }); describe("throttleReactEventCallback", () => { const callback = vi.fn(); const fakeEvent = { persist: vi.fn(), preventDefault: vi.fn() }; let throttledCallback: (event: React.SyntheticEvent) => void; beforeEach(() => { callback.mockReset(); fakeEvent.persist.mockReset(); fakeEvent.preventDefault.mockReset(); }); it("invokes event.persist() to prevent React from pooling before we can reference the event in rAF", () => { throttledCallback = Utils.throttleReactEventCallback(callback); throttledCallback(fakeEvent as unknown as React.SyntheticEvent); expect(fakeEvent.persist).toHaveBeenCalledOnce(); }); it("can preventDefault", () => { throttledCallback = Utils.throttleReactEventCallback(callback, { preventDefault: true, }); throttledCallback(fakeEvent as unknown as React.SyntheticEvent); expect(fakeEvent.preventDefault).toHaveBeenCalledOnce(); }); // TODO: how to test this properly? perhaps with the help of https://github.com/alexreardon/raf-stub? it.skip("properly throttles callback using requestAnimationFrame"); }); });