1import { act } from "react";
2import { create, type ReactTestRenderer } from "react-test-renderer";
3import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
4
5import { SecretInput } from "./SecretInput";
6
7vi.mock("../ui/input", () => ({
8 Input: (props: Record<string, unknown>) => <input {...props} />,
9}));
10
11let renderer: ReactTestRenderer;
12
13beforeEach(() => vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true));
14afterEach(async () => {
15 await act(() => renderer?.unmount());
16 vi.unstubAllGlobals();
17});
18
19async function edit(value: string) {
20 await act(() => renderer.root.findByType("input").props.onFocus());
21 await act(() => renderer.root.findByType("input").props.onChange({ target: { value } }));
22 await act(() => renderer.root.findByType("input").props.onBlur());
23}
24
25describe("SecretInput", () => {
26 it("preserves a configured secret on empty edits and commits a replacement", async () => {
27 const onCommit = vi.fn();
28 await act(() => {
29 renderer = create(<SecretInput configured value="" onCommit={onCommit} />);
30 });
31 await edit("");
32 expect(onCommit).not.toHaveBeenCalled();
33 await edit("replacement");
34 expect(onCommit).toHaveBeenCalledExactlyOnceWith("replacement");
35 expect(renderer.root.findByType("input").props.value).toBe("");
36 });
37
38 it("allows clearing an unsaved draft", async () => {
39 const onCommit = vi.fn();
40 await act(() => {
41 renderer = create(<SecretInput value="draft" onCommit={onCommit} />);
42 });
43 await edit("");
44 expect(onCommit).toHaveBeenCalledExactlyOnceWith("");
45 });
46
47 it("keeps an in-progress replacement when settings refresh", async () => {
48 const onCommit = vi.fn();
49 await act(() => {
50 renderer = create(<SecretInput configured value="" onCommit={onCommit} />);
51 });
52 await act(() => renderer.root.findByType("input").props.onFocus());
53 await act(() =>
54 renderer.root.findByType("input").props.onChange({ target: { value: "replacement" } }),
55 );
56 await act(() => {
57 renderer.update(<SecretInput configured value="" onCommit={onCommit} />);
58 });
59 expect(renderer.root.findByType("input").props.value).toBe("replacement");
60 await act(() => renderer.root.findByType("input").props.onBlur());
61 expect(onCommit).toHaveBeenCalledExactlyOnceWith("replacement");
62 });
63});