Skip to content

Commit 486db07

Browse files
test: add tests for referrer resolution and clean up variable names
- Add DesyncedMessage.test.tsx with tests for button disabled state during referrer resolution and referrer passed to transaction - Add Pricing.test.tsx test for isLoading prop showing loading button - Add useResolvedReferrer.test.tsx test for hex address pass-through - Rename referrerParam to referrer and referrerHex to resolvedReferrer across all components for clarity - Remove debug console.logs from useResolvedReferrer and ExtendNames - Simplify resolveReferrer to only use getAddressRecord (remove getOwner) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e544f39 commit 486db07

File tree

10 files changed

+306
-123
lines changed

10 files changed

+306
-123
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import { mockFunction, render, screen, userEvent } from '@app/test-utils'
2+
3+
import { beforeEach, describe, expect, it, vi } from 'vitest'
4+
import { Hex } from 'viem'
5+
import { useAccount } from 'wagmi'
6+
7+
import { useReferrer } from '@app/hooks/useReferrer'
8+
import { useResolvedReferrer } from '@app/hooks/useResolvedReferrer'
9+
import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider'
10+
import { ONE_DAY } from '@app/utils/time'
11+
12+
import { DesyncedMessage } from './DesyncedMessage'
13+
14+
vi.mock('wagmi')
15+
vi.mock('@app/hooks/useReferrer')
16+
vi.mock('@app/hooks/useResolvedReferrer')
17+
vi.mock('@app/transaction-flow/TransactionFlowProvider')
18+
19+
const mockUseAccount = mockFunction(useAccount)
20+
const mockUseReferrer = mockFunction(useReferrer)
21+
const mockUseResolvedReferrer = mockFunction(useResolvedReferrer)
22+
const mockUseTransactionFlow = mockFunction(useTransactionFlow)
23+
24+
const mockCreateTransactionFlow = vi.fn()
25+
const mockShowExtendNamesInput = vi.fn()
26+
const mockUsePreparedDataInput = () => mockShowExtendNamesInput
27+
28+
describe('DesyncedMessage', () => {
29+
beforeEach(() => {
30+
vi.clearAllMocks()
31+
mockUseAccount.mockReturnValue({ isConnected: true })
32+
mockUseReferrer.mockReturnValue(undefined)
33+
mockUseResolvedReferrer.mockReturnValue({
34+
data: null,
35+
isLoading: false,
36+
isError: false,
37+
error: null,
38+
})
39+
mockUseTransactionFlow.mockReturnValue({
40+
createTransactionFlow: mockCreateTransactionFlow,
41+
usePreparedDataInput: mockUsePreparedDataInput,
42+
})
43+
})
44+
45+
it('should disable action button when referrer is resolving', () => {
46+
mockUseReferrer.mockReturnValue('vitalik.eth')
47+
mockUseResolvedReferrer.mockReturnValue({
48+
data: null,
49+
isLoading: true,
50+
isError: false,
51+
error: null,
52+
})
53+
54+
render(
55+
<DesyncedMessage
56+
name="test.eth"
57+
expiryDate={new Date(Date.now() - 1000)} // Past date - no minSeconds
58+
isGracePeriod={false}
59+
/>,
60+
)
61+
62+
const actionButton = screen.getByRole('button')
63+
expect(actionButton).toBeDisabled()
64+
})
65+
66+
it('should enable action button when referrer resolution completes', () => {
67+
const mockReferrerHex =
68+
'0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045' as Hex
69+
mockUseReferrer.mockReturnValue('vitalik.eth')
70+
mockUseResolvedReferrer.mockReturnValue({
71+
data: mockReferrerHex,
72+
isLoading: false,
73+
isError: false,
74+
error: null,
75+
})
76+
77+
render(
78+
<DesyncedMessage
79+
name="test.eth"
80+
expiryDate={new Date(Date.now() - 1000)} // Past date - no minSeconds
81+
isGracePeriod={false}
82+
/>,
83+
)
84+
85+
const actionButton = screen.getByRole('button')
86+
expect(actionButton).not.toBeDisabled()
87+
})
88+
89+
it('should pass resolved referrer hex to transaction when clicking action button', async () => {
90+
const mockReferrerHex =
91+
'0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045' as Hex
92+
mockUseReferrer.mockReturnValue('vitalik.eth')
93+
mockUseResolvedReferrer.mockReturnValue({
94+
data: mockReferrerHex,
95+
isLoading: false,
96+
isError: false,
97+
error: null,
98+
})
99+
100+
// Set expiryDate far in the future so minSeconds calculates to 0 (triggers createTransactionFlow)
101+
const futureDate = new Date(Date.now() + ONE_DAY * 1000 + 10000)
102+
103+
render(
104+
<DesyncedMessage name="test.eth" expiryDate={futureDate} isGracePeriod={false} />,
105+
)
106+
107+
const actionButton = screen.getByRole('button')
108+
await userEvent.click(actionButton)
109+
110+
expect(mockCreateTransactionFlow).toHaveBeenCalledWith(
111+
'repair-desynced-name-test.eth',
112+
expect.objectContaining({
113+
transactions: [
114+
expect.objectContaining({
115+
name: 'repairDesyncedName',
116+
data: expect.objectContaining({
117+
name: 'test.eth',
118+
referrer: mockReferrerHex,
119+
hasWrapped: true,
120+
}),
121+
}),
122+
],
123+
}),
124+
)
125+
})
126+
127+
it('should pass undefined referrer when no referrer is resolved', async () => {
128+
mockUseReferrer.mockReturnValue(undefined)
129+
mockUseResolvedReferrer.mockReturnValue({
130+
data: null,
131+
isLoading: false,
132+
isError: false,
133+
error: null,
134+
})
135+
136+
// Set expiryDate far in the future so minSeconds calculates to 0 (triggers createTransactionFlow)
137+
const futureDate = new Date(Date.now() + ONE_DAY * 1000 + 10000)
138+
139+
render(
140+
<DesyncedMessage name="test.eth" expiryDate={futureDate} isGracePeriod={false} />,
141+
)
142+
143+
const actionButton = screen.getByRole('button')
144+
await userEvent.click(actionButton)
145+
146+
expect(mockCreateTransactionFlow).toHaveBeenCalledWith(
147+
'repair-desynced-name-test.eth',
148+
expect.objectContaining({
149+
transactions: [
150+
expect.objectContaining({
151+
name: 'repairDesyncedName',
152+
data: expect.objectContaining({
153+
name: 'test.eth',
154+
referrer: undefined,
155+
hasWrapped: true,
156+
}),
157+
}),
158+
],
159+
}),
160+
)
161+
})
162+
163+
it('should call useResolvedReferrer with the referrer param', () => {
164+
mockUseReferrer.mockReturnValue('nick.eth')
165+
166+
render(
167+
<DesyncedMessage name="test.eth" expiryDate={new Date()} isGracePeriod={false} />,
168+
)
169+
170+
expect(mockUseResolvedReferrer).toHaveBeenCalledWith({ referrer: 'nick.eth' })
171+
})
172+
})

src/components/@molecules/DesyncedMessage/DesyncedMessage.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ export const DesyncedMessage = ({
3333
const { isConnected } = useAccount()
3434
const { createTransactionFlow, usePreparedDataInput } = useTransactionFlow()
3535
const showExtendNamesInput = usePreparedDataInput('ExtendNames')
36-
const referrerParam = useReferrer()
37-
const { data: referrerHex, isLoading: isReferrerResolving } = useResolvedReferrer(referrerParam)
36+
const referrer = useReferrer()
37+
const { data: resolvedReferrer, isLoading: isReferrerResolving } = useResolvedReferrer({
38+
referrer,
39+
})
3840

3941
return (
4042
<BannerMessageWithAction
@@ -67,7 +69,7 @@ export const DesyncedMessage = ({
6769
transactions: [
6870
createTransactionItem('repairDesyncedName', {
6971
name,
70-
referrer: referrerHex ?? undefined,
72+
referrer: resolvedReferrer ?? undefined,
7173
hasWrapped: true,
7274
}),
7375
],

src/components/pages/profile/[name]/registration/Registration.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,10 @@ const Registration = ({ nameDetails, isLoading }: Props) => {
112112
const chainId = useChainId()
113113
const { address } = useAccount()
114114
const primary = usePrimaryName({ address })
115-
const referrerParam = useReferrer()
116-
const { data: resolvedReferrerHex, isLoading: isResolvingReferrer } =
117-
useResolvedReferrer(referrerParam)
115+
const referrer = useReferrer()
116+
const { data: resolvedReferrer, isLoading: isResolvingReferrer } = useResolvedReferrer({
117+
referrer,
118+
})
118119
const selected = useMemo(
119120
() => ({ name: nameDetails.normalisedName, address: address!, chainId }),
120121
[address, chainId, nameDetails.normalisedName],
@@ -170,11 +171,11 @@ const Registration = ({ nameDetails, isLoading }: Props) => {
170171
initiateMoonpayRegistrationMutation.mutate(secondsToYears(seconds))
171172
return
172173
}
173-
if (resolvedReferrerHex) {
174+
if (resolvedReferrer) {
174175
dispatch({
175176
name: 'setReferrer',
176177
selected,
177-
payload: resolvedReferrerHex,
178+
payload: resolvedReferrer,
178179
})
179180
}
180181
dispatch({

src/components/pages/profile/[name]/registration/steps/Pricing/Pricing.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,19 @@ describe('ActionButton', () => {
5353
)
5454
expect(screen.getByText('action.next')).toBeInTheDocument()
5555
})
56+
57+
it('should show loading button when isLoading is true', () => {
58+
render(
59+
<ActionButton
60+
{...{
61+
...baseMockData,
62+
paymentMethodChoice: PaymentMethod.ethereum,
63+
isLoading: true,
64+
}}
65+
/>,
66+
)
67+
const button = screen.getByTestId('next-button')
68+
expect(button).toBeDisabled()
69+
expect(screen.getByText('loading')).toBeInTheDocument()
70+
})
5671
})

src/hooks/useResolvedReferrer.test.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { renderHook, waitFor } from '@app/test-utils'
22
import { beforeEach, describe, expect, it, vi } from 'vitest'
33
import { Address } from 'viem'
44

5-
import { resolveReferrerToHex } from '@app/utils/referrer'
5+
import { resolveReferrer } from '@app/utils/referrer'
66

77
vi.mock('@app/utils/referrer', () => ({
8-
resolveReferrerToHex: vi.fn(),
8+
resolveReferrer: vi.fn(),
99
}))
1010

1111
import { useResolvedReferrer } from './useResolvedReferrer'
@@ -16,33 +16,30 @@ describe('useResolvedReferrer', () => {
1616
})
1717

1818
it('should return null when no referrer provided', () => {
19-
const { result } = renderHook(() => useResolvedReferrer(undefined))
19+
const { result } = renderHook(() => useResolvedReferrer({ referrer: undefined }))
2020

2121
expect(result.current.data).toBeNull()
2222
expect(result.current.isLoading).toBe(false)
2323
})
2424

2525
it('should resolve ENS name to hex', async () => {
2626
const mockHex = '0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045' as Address
27-
vi.mocked(resolveReferrerToHex).mockResolvedValueOnce(mockHex)
27+
vi.mocked(resolveReferrer).mockResolvedValueOnce(mockHex)
2828

29-
const { result } = renderHook(() => useResolvedReferrer('vitalik.eth'))
29+
const { result } = renderHook(() => useResolvedReferrer({ referrer: 'vitalik.eth' }))
3030

3131
await waitFor(() => {
3232
expect(result.current.data).toBe(mockHex)
3333
expect(result.current.isLoading).toBe(false)
3434
})
3535

36-
expect(vi.mocked(resolveReferrerToHex)).toHaveBeenCalledWith(
37-
expect.anything(),
38-
'vitalik.eth'
39-
)
36+
expect(vi.mocked(resolveReferrer)).toHaveBeenCalledWith(expect.anything(), 'vitalik.eth')
4037
})
4138

4239
it('should handle resolution errors gracefully', async () => {
43-
vi.mocked(resolveReferrerToHex).mockRejectedValueOnce(new Error('Resolution failed'))
40+
vi.mocked(resolveReferrer).mockRejectedValueOnce(new Error('Resolution failed'))
4441

45-
const { result } = renderHook(() => useResolvedReferrer('invalid.eth'))
42+
const { result } = renderHook(() => useResolvedReferrer({ referrer: 'invalid.eth' }))
4643

4744
await waitFor(() => {
4845
expect(result.current.data).toBeNull()
@@ -51,9 +48,9 @@ describe('useResolvedReferrer', () => {
5148
})
5249

5350
it('should handle null resolution result', async () => {
54-
vi.mocked(resolveReferrerToHex).mockResolvedValueOnce(null)
51+
vi.mocked(resolveReferrer).mockResolvedValueOnce(null)
5552

56-
const { result } = renderHook(() => useResolvedReferrer('nonexistent.eth'))
53+
const { result } = renderHook(() => useResolvedReferrer({ referrer: 'nonexistent.eth' }))
5754

5855
await waitFor(() => {
5956
expect(result.current.data).toBeNull()
@@ -62,10 +59,26 @@ describe('useResolvedReferrer', () => {
6259
})
6360

6461
it('should return null when referrer is empty string', () => {
65-
const { result } = renderHook(() => useResolvedReferrer(''))
62+
const { result } = renderHook(() => useResolvedReferrer({ referrer: '' }))
6663

6764
expect(result.current.data).toBeNull()
6865
expect(result.current.isLoading).toBe(false)
69-
expect(vi.mocked(resolveReferrerToHex)).not.toHaveBeenCalled()
66+
expect(vi.mocked(resolveReferrer)).not.toHaveBeenCalled()
67+
})
68+
69+
it('should resolve hex address to padded 32-byte hex', async () => {
70+
const hexAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
71+
const paddedHex =
72+
'0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045' as Address
73+
vi.mocked(resolveReferrer).mockResolvedValueOnce(paddedHex)
74+
75+
const { result } = renderHook(() => useResolvedReferrer({ referrer: hexAddress }))
76+
77+
await waitFor(() => {
78+
expect(result.current.data).toBe(paddedHex)
79+
expect(result.current.isLoading).toBe(false)
80+
})
81+
82+
expect(vi.mocked(resolveReferrer)).toHaveBeenCalledWith(expect.anything(), hexAddress)
7083
})
7184
})

0 commit comments

Comments
 (0)