import { render, screen, waitFor } from "@testing-library/react"; import { Event } from "cesium"; import { createRef, ReactNode } from "react"; import { beforeEach, describe, expect, it, vi, vitest } from "vitest"; import { createCesiumComponent, CesiumComponentRef } from "./component"; import { Provider } from "./context"; beforeEach(() => { console.warn = vi.fn(); }); describe("core/component", () => { it("should create and expose cesium element correctly on initialized", () => { const create = vi.fn(() => "foobar"); const value = { hoge: 1 } as any; const Component = createCesiumComponent({ name: "test", create, }); const ref = createRef>(); render( , ); expect(create).toBeCalledWith(value, { test: 1 }, null); expect(create).toBeCalledTimes(1); expect(ref.current?.cesiumElement).toBe("foobar"); }); it("should create and expose cesium element correctly on initialized asynchronously", async () => { const create = vi.fn(() => Promise.resolve("foobar")); const value = { hoge: 1 } as any; const Component = createCesiumComponent({ name: "test", create, }); const ref = createRef>(); render( , ); await waitFor(() => { expect(ref.current?.cesiumElement).toBe("foobar"); }); expect(create).toBeCalledWith(value, { test: 1 }, null); expect(create).toBeCalledTimes(1); }); it("should call destroy fn on unmounted", () => { const destroy = vi.fn(); const value = { hoge: 1 } as any; const Component = createCesiumComponent({ name: "test", create: () => "foobar", destroy, }); render( , ).unmount(); waitFor(() => { expect(destroy).toBeCalledWith("foobar", value, null, undefined); expect(destroy).toBeCalledTimes(1); }); }); it("should set cesium events after created", () => { const cesiumElement = { hoge: new Event(), }; const Component = createCesiumComponent void }>({ name: "test", create: () => cesiumElement, cesiumEventProps: { bar: "hoge" }, }); render( {}} />); expect(cesiumElement.hoge.numberOfListeners).toBe(1); }); it("should set cesium props after created", () => { const cesiumElement = { foo: 0, }; const Component = createCesiumComponent({ name: "test", create: () => cesiumElement, cesiumProps: ["foo"], setCesiumPropsAfterCreate: true, }); render(); expect(cesiumElement.foo).toBe(10); }); it("should update cesium props", async () => { const cesiumElement = { foo: 0, }; const Component = createCesiumComponent({ name: "test", create: () => cesiumElement, cesiumProps: ["foo"], }); const { rerender } = render(); await waitFor(() => { expect(cesiumElement.foo).toBe(0); }); rerender(); await waitFor(() => { expect(cesiumElement.foo).toBe(1); }); }); it("should update cesium events", async () => { const cesiumElement = { foo: new Event(), bar: new Event(), hoge: new Event(), }; const Component = createCesiumComponent< typeof cesiumElement, { foo?: () => void; bar?: () => void; hoge?: () => void } >({ name: "test", create: () => cesiumElement, cesiumEventProps: { foo: "foo", bar: "bar", hoge: "hoge", }, }); const { rerender } = render( {}} hoge={() => {}} />); expect(cesiumElement.foo.numberOfListeners).toBe(1); expect(cesiumElement.bar.numberOfListeners).toBe(0); expect(cesiumElement.hoge.numberOfListeners).toBe(1); rerender( {}} hoge={() => {}} />); await waitFor(() => { expect(cesiumElement.foo.numberOfListeners).toBe(0); expect(cesiumElement.bar.numberOfListeners).toBe(1); expect(cesiumElement.hoge.numberOfListeners).toBe(1); // TODO }); }); it("should remount when cesium read only props are updated", async () => { const cesiumElement = { foo: 0, }; const createFn = vi.fn((_ctx, props: { foo?: number }) => { if (typeof props.foo === "number") { cesiumElement.foo = props.foo; } return cesiumElement; }); const destroyFn = vi.fn(); const Component = createCesiumComponent({ name: "test", create: createFn, destroy: destroyFn, cesiumReadonlyProps: ["foo"], }); const { rerender } = render(); await waitFor(() => { expect(createFn).toBeCalledTimes(1); expect(destroyFn).toBeCalledTimes(0); expect(cesiumElement.foo).toBe(1); }); rerender(); waitFor(() => { expect(createFn).toBeCalledTimes(2); expect(destroyFn).toBeCalledTimes(1); expect(cesiumElement.foo).toBe(2); }); }); it("should call update", async () => { const updateFn = vi.fn(); const Component = createCesiumComponent<{ hoge: "hoge" }, { foo?: number }>({ name: "test", create: () => ({ hoge: "hoge" }), update: updateFn, }); const { rerender } = render(); expect(updateFn).toBeCalledTimes(0); rerender(); await waitFor(() => { expect(updateFn).toBeCalledTimes(1); expect(updateFn).toBeCalledWith({ hoge: "hoge", foo: 1 }, { foo: 1 }, {}, {}); }); }); it("should provide context", () => { const create1 = vi.fn(() => "test"); const create2 = vi.fn(() => "test"); const Component1 = createCesiumComponent({ name: "test", create: create1, provide: (): any => ({ context: "b" }), }); const Component2 = createCesiumComponent({ name: "test2", create: create2, }); render( , ); expect(create1).toBeCalledWith({ context: "a", context2: "foo" }, expect.anything(), null); expect(create2).toBeCalledWith({ context: "b", context2: "foo" }, expect.anything(), null); }); it("should invoke onUpdate event when being dirty", () => { const cesiumElement = { foo: 0, }; const onUpdate = vitest.fn(); const Component = createCesiumComponent({ name: "test", create: () => cesiumElement, cesiumProps: ["foo"], }); const { rerender } = render( , ); expect(cesiumElement.foo).toBe(0); expect(onUpdate).not.toBeCalled(); rerender( , ); waitFor(() => { expect(cesiumElement.foo).toBe(1); expect(onUpdate).toBeCalledTimes(1); }); }); it("should render container", () => { const createFn = vi.fn(() => "foobar"); const Component = createCesiumComponent({ name: "test", create: createFn, renderContainer: true, containerProps: ["className"], }); render( , ); expect(createFn).toBeCalledWith( expect.anything(), expect.anything(), expect.any(HTMLDivElement), ); const containers = screen.getAllByTestId("resium-container"); expect(containers.length).toBe(1); expect(containers[0].getAttribute("class")).toBe("hoge"); }); it("should keep state", () => { const provideFn = vi.fn(); const destroyFn = vi.fn(); const state = {}; const Component = createCesiumComponent({ name: "test", create: () => ["foobar", state], provide: provideFn, destroy: destroyFn, }); render( , ).unmount(); waitFor(() => { expect(provideFn).toBeCalledWith( expect.anything(), expect.anything(), expect.anything(), state, ); expect(destroyFn).toBeCalledWith(expect.anything(), expect.anything(), null, state); }); }); it("should not render when noChildren is true", () => { const Component = createCesiumComponent({ name: "test", noChildren: false, }); const { rerender } = render(

Hello!

, ); expect(screen.getByTestId("hello")).toBeInTheDocument(); const Component2 = createCesiumComponent({ name: "test", noChildren: true, }); rerender(

Hello!

, ); expect(screen.queryByTestId("hello")).not.toBeInTheDocument(); }); });