1import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
2
3const expoMocks = vi.hoisted(() => ({
4 requireNativeView: vi.fn(),
5}));
6const nativeView = () => null;
7const originalExpo = globalThis.expo;
8
9function setExpoViewConfigAvailable() {
10 globalThis.expo = {
11 getViewConfig: vi.fn().mockReturnValue({ validAttributes: {}, directEventTypes: {} }),
12 } as unknown as typeof globalThis.expo;
13}
14
15vi.mock("expo", () => ({
16 requireNativeView: expoMocks.requireNativeView,
17}));
18
19describe("resolveNativeReviewDiffView", () => {
20 beforeEach(() => {
21 vi.clearAllMocks();
22 vi.resetModules();
23 globalThis.expo = undefined as unknown as typeof globalThis.expo;
24 });
25
26 afterEach(() => {
27 globalThis.expo = originalExpo;
28 });
29
30 it("returns null when the native review diff view config is unavailable", async () => {
31 const { resolveNativeReviewDiffView } = await import("./nativeReviewDiffSurface");
32 expect(resolveNativeReviewDiffView()).toBeNull();
33 expect(expoMocks.requireNativeView).not.toHaveBeenCalled();
34 });
35
36 it("returns the payload bridge when the native review diff view is installed", async () => {
37 setExpoViewConfigAvailable();
38 expoMocks.requireNativeView.mockReturnValue(nativeView);
39 const { resolveNativeReviewDiffView } = await import("./nativeReviewDiffSurface");
40 const resolvedView = resolveNativeReviewDiffView();
41 expect(resolvedView).not.toBeNull();
42 expect(resolvedView).not.toBe(nativeView);
43 expect(resolveNativeReviewDiffView()).toBe(resolvedView);
44 expect(expoMocks.requireNativeView).toHaveBeenCalledWith("T3ReviewDiffSurface");
45 });
46
47 it("returns null when the view manager cannot be required", async () => {
48 setExpoViewConfigAvailable();
49 const cause = new Error("boom");
50 expoMocks.requireNativeView.mockImplementation(() => {
51 throw cause;
52 });
53 const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
54 const { resolveNativeReviewDiffView } = await import("./nativeReviewDiffSurface");
55
56 expect(resolveNativeReviewDiffView()).toBeNull();
57 expect(resolveNativeReviewDiffView()).toBeNull();
58 expect(expoMocks.requireNativeView).toHaveBeenCalledTimes(1);
59 expect(consoleError).toHaveBeenCalledWith(
60 expect.objectContaining({
61 _tag: "NativeViewResolutionError",
62 nativeModuleName: "T3ReviewDiffSurface",
63 cause,
64 }),
65 );
66 expect(consoleError).toHaveBeenCalledTimes(1);
67 });
68});
69
70describe("isPendingNativeViewRegistration", () => {
71 it("recognizes registration races for the installed native view name", async () => {
72 const { isPendingNativeViewRegistration } = await import("./nativeReviewDiffSurface");
73
74 expect(
75 isPendingNativeViewRegistration(
76 new Error("Unable to find the 'T3ReviewDiffSurface' view for this native tag"),
77 ),
78 ).toBe(true);
79 expect(
80 isPendingNativeViewRegistration(
81 new Error("Unable to find the 'T3ReviewDiffView' view for this native tag"),
82 ),
83 ).toBe(false);
84 expect(
85 isPendingNativeViewRegistration(
86 new Error(
87 "Unable to find the class expo.modules.t3reviewdiff.T3ReviewDiffView view with tag 1150",
88 ),
89 ),
90 ).toBe(true);
91 });
92});
93
94describe("isNativeReviewDiffDrawEvent", () => {
95 it("accepts only native events emitted after diff rows draw", async () => {
96 const { isNativeReviewDiffDrawEvent } = await import("./nativeReviewDiffSurface");
97
98 expect(isNativeReviewDiffDrawEvent({ message: "draw-metrics" })).toBe(true);
99 expect(isNativeReviewDiffDrawEvent({ message: "visible-range" })).toBe(true);
100 expect(isNativeReviewDiffDrawEvent({ message: "rows-decoded" })).toBe(false);
101 });
102});