Skip to content

Commit fd9e7cd

Browse files
authored
fix(app): parse chainID correctly (#3392)
* fix(app): parse chainID correctly * test(app): adding tests for useOnChainData
1 parent 307f895 commit fd9e7cd

File tree

2 files changed

+227
-2
lines changed

2 files changed

+227
-2
lines changed
+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// useOnChainData.test.ts
2+
import { vi, describe, it, expect, beforeEach } from "vitest";
3+
import { renderHook, act } from "@testing-library/react-hooks";
4+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5+
import { useAccount, useChains } from "wagmi";
6+
import { createPublicClient } from "viem";
7+
import { getAttestationData } from "../../utils/onChainStamps";
8+
import { useCustomization } from "../../hooks/useCustomization";
9+
import { chains, wagmiTransports } from "../../utils/chains";
10+
import { FeatureFlags } from "../../config/feature_flags";
11+
import { useOnChainData } from "../../hooks/useOnChainData";
12+
import { PROVIDER_ID } from "@gitcoin/passport-types";
13+
14+
// Mock dependencies
15+
vi.mock("@datadog/browser-logs");
16+
vi.mock("@datadog/browser-rum");
17+
vi.mock("wagmi");
18+
vi.mock("viem");
19+
vi.mock("../../hooks/useCustomization");
20+
vi.mock("../../utils/onChainStamps");
21+
22+
// Sample data for tests
23+
const mockAddress = "0x1234567890123456789012345678901234567890";
24+
const mockDecimalChainId = 1;
25+
const mockHexChainId = "0x1";
26+
const mockProviders = [
27+
{
28+
providerName: "github" as PROVIDER_ID,
29+
expirationDate: new Date("2023-12-31"),
30+
issuanceDate: new Date("2023-01-01"),
31+
},
32+
];
33+
34+
// Create wrapper with QueryClient
35+
const createWrapper = () => {
36+
const queryClient = new QueryClient({
37+
defaultOptions: {
38+
queries: {
39+
retry: false,
40+
},
41+
},
42+
});
43+
44+
return ({ children }: { children: React.ReactNode }) => (
45+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
46+
);
47+
};
48+
49+
describe("useOnChainData hook", () => {
50+
const mockQueryInvalidate = vi.fn();
51+
const mockConsoleError = vi.fn();
52+
53+
beforeEach(() => {
54+
vi.resetAllMocks();
55+
56+
// Setup feature flag
57+
FeatureFlags.FF_CHAIN_SYNC = true;
58+
59+
// Mock chain data
60+
const mockChains = [
61+
{
62+
id: mockHexChainId,
63+
attestationProvider: { status: "enabled" },
64+
useCustomCommunityId: false,
65+
},
66+
];
67+
Object.defineProperty(chains, "length", { value: mockChains.length });
68+
Object.assign(chains, mockChains);
69+
70+
// Mock wagmi hooks
71+
vi.mocked(useChains).mockReturnValue([{ id: mockDecimalChainId, name: "Ethereum" } as any]);
72+
vi.mocked(useAccount).mockReturnValue({
73+
address: mockAddress,
74+
chain: { id: mockDecimalChainId },
75+
} as any);
76+
77+
// Mock useCustomization
78+
vi.mocked(useCustomization).mockReturnValue({
79+
scorer: { id: 1 },
80+
} as any);
81+
82+
// Mock createPublicClient
83+
const mockPublicClient = {};
84+
vi.mocked(createPublicClient).mockReturnValue(mockPublicClient as any);
85+
86+
// Mock wagmiTransports
87+
Object.assign(wagmiTransports, {
88+
[mockDecimalChainId]: vi.fn(),
89+
});
90+
91+
// Mock getAttestationData
92+
vi.mocked(getAttestationData).mockResolvedValue({
93+
score: { value: 10, expirationDate: new Date("2023-12-31") },
94+
providers: mockProviders,
95+
} as any);
96+
97+
// Mock QueryClient's invalidateQueries
98+
vi.spyOn(QueryClient.prototype, "invalidateQueries").mockImplementation(mockQueryInvalidate);
99+
100+
// Mock console.error
101+
console.error = mockConsoleError;
102+
});
103+
104+
it("returns initial state while loading", async () => {
105+
// Arrange
106+
vi.mocked(getAttestationData).mockImplementation(
107+
() =>
108+
new Promise((resolve) => {
109+
setTimeout(() => {
110+
resolve({
111+
score: { value: 10, expirationDate: new Date("2023-12-31") },
112+
providers: mockProviders,
113+
} as any);
114+
}, 100);
115+
})
116+
);
117+
118+
// Act
119+
const { result, waitForNextUpdate } = renderHook(() => useOnChainData(), {
120+
wrapper: createWrapper(),
121+
});
122+
123+
// Assert
124+
expect(result.current.isPending).toBe(true);
125+
expect(result.current.data).toEqual({});
126+
expect(result.current.activeChainProviders).toEqual([]);
127+
128+
// Wait for update to complete
129+
await waitForNextUpdate();
130+
});
131+
132+
it("returns data when loaded successfully", async () => {
133+
// Arrange
134+
const expectedData = {
135+
score: 10,
136+
providers: mockProviders,
137+
expirationDate: new Date("2023-12-31"),
138+
};
139+
140+
// Act
141+
const { result, waitForNextUpdate } = renderHook(() => useOnChainData(), {
142+
wrapper: createWrapper(),
143+
});
144+
145+
// Wait for the query to complete
146+
await waitForNextUpdate();
147+
148+
// Assert
149+
expect(result.current.isPending).toBe(false);
150+
expect(result.current.data[mockHexChainId]).toEqual(expectedData);
151+
expect(result.current.activeChainProviders).toEqual(mockProviders);
152+
});
153+
154+
it("refreshes all chains when called without chainId", async () => {
155+
// Arrange
156+
const { result, waitForNextUpdate } = renderHook(() => useOnChainData(), {
157+
wrapper: createWrapper(),
158+
});
159+
160+
// Wait for initial query to complete
161+
await waitForNextUpdate();
162+
163+
// Act
164+
await act(async () => {
165+
result.current.refresh();
166+
});
167+
168+
// Assert
169+
expect(mockQueryInvalidate).toHaveBeenCalledWith({
170+
queryKey: ["onChain", "passport", mockAddress],
171+
});
172+
});
173+
174+
it("refreshes specific chain when chainId is provided", async () => {
175+
// Arrange
176+
const { result, waitForNextUpdate } = renderHook(() => useOnChainData(), {
177+
wrapper: createWrapper(),
178+
});
179+
180+
// Wait for initial query to complete
181+
await waitForNextUpdate();
182+
183+
// Act
184+
await act(async () => {
185+
result.current.refresh(mockHexChainId);
186+
});
187+
188+
// Assert
189+
expect(mockQueryInvalidate).toHaveBeenCalledWith({
190+
queryKey: ["onChain", "passport", mockAddress, mockHexChainId],
191+
});
192+
});
193+
194+
it("returns active chain providers for current chain", async () => {
195+
// Arrange
196+
const customProviders = [
197+
{
198+
providerName: "twitter" as PROVIDER_ID,
199+
expirationDate: new Date("2023-12-31"),
200+
issuanceDate: new Date("2023-01-01"),
201+
},
202+
];
203+
204+
vi.mocked(getAttestationData).mockResolvedValue({
205+
score: { value: 10, expirationDate: new Date("2023-12-31") },
206+
providers: customProviders,
207+
} as any);
208+
209+
// Act
210+
const { result, waitForNextUpdate } = renderHook(() => useOnChainData(), {
211+
wrapper: createWrapper(),
212+
});
213+
214+
// Wait for the query to complete
215+
await waitForNextUpdate();
216+
217+
// Assert
218+
expect(result.current.activeChainProviders).toEqual(customProviders);
219+
});
220+
});

app/hooks/useOnChainData.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { UseQueryResult, useQueries, useQueryClient } from "@tanstack/react-quer
1414
import { parseValidChains } from "./useOnChainStatus";
1515
import { useCustomization } from "./useCustomization";
1616
import { useAccount, useChains } from "wagmi";
17-
import { createPublicClient, http, PublicClient } from "viem";
17+
import { createPublicClient, PublicClient } from "viem";
1818

1919
export interface OnChainProviderMap {
2020
[chainId: string]: OnChainProviderType[];
@@ -145,9 +145,14 @@ const useOnChainDataQuery = (address?: string) => {
145145
});
146146
};
147147

148+
const decimalToHexChainId = (chainId: number): ChainId => {
149+
const hex = chainId.toString(16);
150+
return `0x${hex}`;
151+
};
152+
148153
export const useOnChainData = (): OnChainData => {
149154
const { address, chain } = useAccount();
150-
const chainId = chain?.id.toString(16) as ChainId;
155+
const chainId = decimalToHexChainId(chain?.id || 0);
151156
const queryClient = useQueryClient();
152157

153158
const { data, isError, error, isPending } = useOnChainDataQuery(address);

0 commit comments

Comments
 (0)