/** * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ import { render } from "@testing-library/react"; import type { RenderResult } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import assert from "assert"; import React from "react"; import { computeNextPosition, RectangleDimensions } from "./helpers"; import { Tooltip, TooltipPosition } from "./tooltip"; const getRectangle = ( parts: Omit, ): RectangleDimensions => { assert(parts.right >= parts.left); assert(parts.bottom >= parts.top); return { ...parts, width: parts.right - parts.left, height: parts.bottom - parts.top, }; }; describe("", () => { let requestAnimationFrameSpy: jest.SpyInstance; beforeEach(() => { requestAnimationFrameSpy = jest.spyOn(window, "requestAnimationFrame"); requestAnimationFrameSpy.mockImplementation((cb) => { cb(0); return 0; }); }); afterEach(() => { requestAnimationFrameSpy.mockRestore(); }); it("does not render to DOM if not visible", () => { const result = render( <> I am a tooltip
Target Text
, ); expect(result.baseElement).toMatchSnapshot(); }); it("renders to DOM when hovering over target", () => { const result = render( <> I am a tooltip
Target Text
, ); const target = result.baseElement.querySelector("#my-target"); assert(target); userEvent.hover(target); expect(result.baseElement).toMatchSnapshot(); }); it("renders to DOM when forced to by visible prop", () => { const result = render( <> I am a tooltip
Target Text
, ); expect(result.baseElement).toMatchSnapshot(); }); it("uses a portal if usePortal is specified", () => { const result = render(
I am a tooltip
Target Text
, ); expect(result.baseElement).toMatchSnapshot(); }); describe("when specifying a tooltip for a component", () => { let result: RenderResult; beforeEach(() => { result = render(
I am a tooltip
Target Text
, ); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); describe("when hovering over the target element", () => { beforeEach(() => { userEvent.hover(result.getByTestId("target")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("renders the tooltip", () => { expect(result.getByTestId("tooltip")).toBeInTheDocument(); }); describe("when no longer hovering the target", () => { beforeEach(() => { userEvent.unhover(result.getByTestId("target")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); }); }); }); describe("when specifying a tooltip for a component with show on parent hover", () => { let result: RenderResult; beforeEach(() => { result = render(
I am a tooltip
Target Text
, ); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); describe("when hovering over the target element", () => { beforeEach(() => { userEvent.hover(result.getByTestId("target")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("renders the tooltip", () => { expect(result.getByTestId("tooltip")).toBeInTheDocument(); }); describe("when no longer hovering the target", () => { beforeEach(() => { userEvent.unhover(result.getByTestId("target")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); }); }); describe("when hovering over the target's parent element", () => { beforeEach(() => { userEvent.hover(result.getByTestId("target-parent")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("renders the tooltip", () => { expect(result.getByTestId("tooltip")).toBeInTheDocument(); }); describe("when no longer hovering the target's parent", () => { beforeEach(() => { userEvent.unhover(result.getByTestId("target-parent")); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); }); }); }); describe("when specifying a tooltip for a component that doesn't exist with show on parent hover", () => { let result: RenderResult; beforeEach(() => { result = render( <> I am a tooltip , ); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); it("doesn't render the tooltip", () => { expect(result.queryByTestId("tooltip")).not.toBeInTheDocument(); }); }); }); describe("computeNextPosition technical tests", () => { describe("given a 1280x720 window", () => { beforeEach(() => { window.innerHeight = 720; window.innerWidth = 1280; }); [ { position: "right", targetRect: { top: 700, left: 700, bottom: 720, right: 720, }, }, { position: "bottom", targetRect: { top: 500, left: 1200, bottom: 520, right: 1280, }, }, { position: "top", targetRect: { top: 700, left: 1200, bottom: 720, right: 1280, }, }, { position: "left", targetRect: { top: 0, left: 1200, bottom: 720, right: 1280, }, }, ].forEach(({ position, targetRect }) => { it(`renders to the "${position}" by default if there is enough room`, () => { expect( computeNextPosition({ offset: 10, target: { getBoundingClientRect: () => getRectangle(targetRect), }, tooltip: { getBoundingClientRect: () => ({ height: 20, width: 20, }), }, }), ).toMatchObject({ position, }); }); }); it("doesn't throw if the preferredPosition is invalid", () => { expect( computeNextPosition({ preferredPositions: "some-invalid-data" as any, offset: 10, target: { getBoundingClientRect: () => getRectangle({ top: 50, left: 50, bottom: 100, right: 100, }), }, tooltip: { getBoundingClientRect: () => ({ height: 20, width: 20, }), }, }), ).toMatchObject({ position: "right", }); }); it("defaults to right if no other location works", () => { expect( computeNextPosition({ offset: 10, target: { getBoundingClientRect: () => getRectangle({ top: 0, left: 0, bottom: 720, right: 1280, }), }, tooltip: { getBoundingClientRect: () => ({ height: 20, width: 20, }), }, }), ).toMatchObject({ position: "right", }); }); it.each([ TooltipPosition.RIGHT, TooltipPosition.BOTTOM, TooltipPosition.TOP, TooltipPosition.LEFT, TooltipPosition.TOP_RIGHT, TooltipPosition.TOP_LEFT, TooltipPosition.BOTTOM_RIGHT, TooltipPosition.BOTTOM_LEFT, ])( "computes to the %p if there is space and it is specified as a preferred position", (position) => { expect( computeNextPosition({ preferredPositions: position, offset: 10, target: { getBoundingClientRect: () => getRectangle({ top: 50, left: 50, bottom: 100, right: 100, }), }, tooltip: { getBoundingClientRect: () => ({ height: 20, width: 20, }), }, }), ).toMatchObject({ position, }); }, ); }); });