mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 05:32:58 +00:00
Selecting an item parked its action in a ref and waited for the Modal's onDismiss to flush it. MenuOverlay returns null as soon as the menu closes, so the Modal unmounted and onDismiss never fired: every close-on-select action was dropped. Creating a terminal from the compact workspace menu did nothing at all. Wait on a timer instead. The grace period was always about giving UIKit time to tear the surface down before a native presenter launches, and a callback fired from inside a subtree being removed is racing its own removal. The sheet had no flush at all, so it was broken the same way for a different reason.
64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
/**
|
|
* @vitest-environment jsdom
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { act, renderHook } from "@testing-library/react";
|
|
import { Platform } from "react-native";
|
|
import { IOS_TEARDOWN_GRACE_MS, useMenuState } from "./menu-context";
|
|
|
|
/**
|
|
* `Platform.OS` is a plain property on the react-native-web double, so the iOS branch is
|
|
* reachable by writing to it. The grace period only exists on iOS, which is exactly the platform
|
|
* whose selections used to be dropped.
|
|
*/
|
|
const originalPlatform = Platform.OS;
|
|
|
|
function setPlatform(os: string) {
|
|
Object.assign(Platform, { OS: os });
|
|
}
|
|
|
|
beforeEach(() => vi.useFakeTimers());
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
setPlatform(originalPlatform);
|
|
});
|
|
|
|
describe("useMenuState selectItem on iOS", () => {
|
|
beforeEach(() => setPlatform("ios"));
|
|
|
|
it("runs a closing item's action once the surface has had time to go away", () => {
|
|
const onSelect = vi.fn();
|
|
const { result } = renderHook(() => useMenuState({ defaultOpen: true }));
|
|
|
|
act(() => result.current.selectItem(onSelect, true));
|
|
expect(result.current.open).toBe(false);
|
|
expect(onSelect).not.toHaveBeenCalled();
|
|
|
|
act(() => vi.advanceTimersByTime(IOS_TEARDOWN_GRACE_MS));
|
|
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("leaves the menu open for an item that does not close it", () => {
|
|
const onSelect = vi.fn();
|
|
const { result } = renderHook(() => useMenuState({ defaultOpen: true }));
|
|
|
|
act(() => result.current.selectItem(onSelect, false));
|
|
expect(result.current.open).toBe(true);
|
|
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("useMenuState selectItem elsewhere", () => {
|
|
beforeEach(() => setPlatform("android"));
|
|
|
|
it("runs a closing item's action immediately", () => {
|
|
const onSelect = vi.fn();
|
|
const { result } = renderHook(() => useMenuState({ defaultOpen: true }));
|
|
|
|
act(() => result.current.selectItem(onSelect, true));
|
|
expect(result.current.open).toBe(false);
|
|
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|