Skip to content
Source
Sign inSign up
Open History to see the last commit

keyboardControllerState.test.tsx

84 lines2.8 KB

1import { act } from "react";
2import { createRoot } from "react-dom/client";
3import type { KeyboardEventData } from "react-native-keyboard-controller";
4import { expect, it, vi } from "vite-plus/test";
5​
6const listeners = vi.hoisted(() => new Map<string, Set<(event: KeyboardEventData) => void>>());
7​
8// Exercises the patched library source with only its native bindings replaced.
9vi.mock("react-native-keyboard-controller/src/bindings", () => ({
10 KeyboardEvents: {
11 addListener: (name: string, listener: (event: KeyboardEventData) => void) => {
12 const named = listeners.get(name) ?? new Set();
13 named.add(listener);
14 listeners.set(name, named);
15 return { remove: () => named.delete(listener) };
16 },
17 },
18 KeyboardControllerNative: { dismiss: () => {} },
19}));
20​
21import { useKeyboardState } from "react-native-keyboard-controller/src/hooks/useKeyboardState";
22import { KeyboardController } from "react-native-keyboard-controller/src/module";
23​
24function emit(name: string, height: number) {
25 const event: KeyboardEventData = {
26 height,
27 duration: 0,
28 timestamp: 1,
29 target: 42,
30 type: "default",
31 appearance: "light",
32 };
33 act(() => {
34 for (const listener of listeners.get(name) ?? []) listener(event);
35 });
37​
38function KeyboardState() {
39 const { isVisible, height } = useKeyboardState();
40 return <output>{JSON.stringify({ isVisible, height })}</output>;
42​
43// The library keeps keyboard state at module scope; every test starts and ends hidden.
44function setup() {
45 emit("keyboardDidHide", 0);
46 return { [Symbol.dispose]: () => emit("keyboardDidHide", 0) };
48​
49function renderKeyboardState() {
50 const container = document.createElement("div");
51 const root = createRoot(container);
52 act(() => root.render(<KeyboardState />));
53 return { container, [Symbol.dispose]: () => act(() => root.unmount()) };
55​
56// Android's syncKeyboardPosition repairs a nonanimated IME inset change with
57// keyboardDidHide then keyboardDidShow, and no keyboardWillShow.
58it("reports the keyboard shown after Android repairs the IME inset without keyboardWillShow", () => {
59 using _keyboard = setup();
60 using screen = renderKeyboardState();
61​
62 emit("keyboardDidShow", 376);
63​
64 expect({
65 isVisible: KeyboardController.isVisible(),
66 height: KeyboardController.state().height,
67 hook: JSON.parse(screen.container.textContent ?? ""),
68 }).toEqual({ isVisible: true, height: 376, hook: { isVisible: true, height: 376 } });
69});
70​
71it("waits for the native hide before dismiss resolves after a didShow-only repair", async () => {
72 using _keyboard = setup();
73 emit("keyboardDidShow", 376);
74​
75 let dismissed = false;
76 const dismissal = KeyboardController.dismiss().then(() => {
77 dismissed = true;
78 });
79 await act(async () => {});
80​
81 expect(dismissed).toBe(false);
82 emit("keyboardDidHide", 0);
83 await dismissal;
84});