diff --git a/src/components/@molecules/AdvancedEditor/AdvancedEditorTabContent.tsx b/src/components/@molecules/AdvancedEditor/AdvancedEditorTabContent.tsx index 8f33f41f3..add1b707f 100644 --- a/src/components/@molecules/AdvancedEditor/AdvancedEditorTabContent.tsx +++ b/src/components/@molecules/AdvancedEditor/AdvancedEditorTabContent.tsx @@ -6,8 +6,8 @@ import { ScrollBox } from '@ensdomains/thorin' import { RecordInput } from '@app/components/@molecules/RecordInput/RecordInput' import useAdvancedEditor from '@app/hooks/useAdvancedEditor' import { convertFormSafeKey, formSafeKey } from '@app/utils/editor' -import { validateCryptoAddress } from '@app/utils/validate' import { validateAbi } from '@app/validators/validateAbi' +import { validateCryptoAddress } from '@app/validators/validateAddress' import { validateContentHash } from '@app/validators/validateContentHash' const TabContentsContainer = styled.div( diff --git a/src/components/pages/profile/[name]/registration/steps/Profile/profileRecordUtils.ts b/src/components/pages/profile/[name]/registration/steps/Profile/profileRecordUtils.ts index 471dd9083..65ab51e8b 100644 --- a/src/components/pages/profile/[name]/registration/steps/Profile/profileRecordUtils.ts +++ b/src/components/pages/profile/[name]/registration/steps/Profile/profileRecordUtils.ts @@ -50,9 +50,9 @@ export const profileRecordsToRecordOptions = ( if (record.type === 'addr') { return { ...options, - coinTypes: [ + coins: [ ...(options.coins?.filter((r) => r.coin !== recordItem.key) || []), - recordItem, + { coin: key, value }, ], } } diff --git a/src/hooks/abilities/useAbilities.ts b/src/hooks/abilities/useAbilities.ts index c8f15614a..7210588e7 100644 --- a/src/hooks/abilities/useAbilities.ts +++ b/src/hooks/abilities/useAbilities.ts @@ -77,22 +77,27 @@ export const DEFAULT_ABILITIES: Abilities = { canSendManager: false, } -export const useAbilities = (name: string) => { +type UseAbilitiesParameters = { + name: string + enabled?: boolean +} + +export const useAbilities = ({ name, enabled = true }: UseAbilitiesParameters) => { const { t } = useTranslation('profile') const parent = name?.split('.')?.slice(1).join('.') const { address } = useAccountSafely() - const basicNameData = useBasicName({ name, enabled: !!name && !!address }) + const basicNameData = useBasicName({ name, enabled: enabled && !!name && !!address }) const resolverAuthorisation = useResolverIsAuthorised({ name, - enabled: !!name && !!address, + enabled: enabled && !!name && !!address, }) const parentBasicNameData = useBasicName({ name: parent, - enabled: !!parent && !!address, + enabled: enabled && !!parent && !!address, }) // useHasSubnames checks internally if name exists & if it is subname before it enables itself diff --git a/src/hooks/ensjs/public/usePrimaryName.ts b/src/hooks/ensjs/public/usePrimaryName.ts index be7d69c07..4e7b5e51c 100644 --- a/src/hooks/ensjs/public/usePrimaryName.ts +++ b/src/hooks/ensjs/public/usePrimaryName.ts @@ -32,7 +32,7 @@ export const usePrimaryName = ({ if (!res || !res.name || (!res.match && !allowMismatch)) return null return { ...res, - name: res.name as string | undefined, + name: res.name as string, beautifiedName: tryBeautify(res.name), } }, diff --git a/src/hooks/useProfileActions.test.ts b/src/hooks/useProfileActions.test.ts index 7d052fc25..aee106edd 100644 --- a/src/hooks/useProfileActions.test.ts +++ b/src/hooks/useProfileActions.test.ts @@ -1,113 +1,176 @@ import { mockFunction, renderHook } from '@app/test-utils' -import { labelhash } from '@ensdomains/ensjs/utils/labels' +import { labelhash } from 'viem' -import { usePrimary } from '@app/hooks/ensjs/public/usePrimaryName' +import { usePrimaryName } from '@app/hooks/ensjs/public/usePrimaryName' import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' - +import { DeepPartial } from '@app/types' +import { createDateAndValue } from '@app/utils/utils' + +import { useAbilities } from './abilities/useAbilities' +import { useAccountSafely } from './account/useAccountSafely' +import { useContractAddress } from './chain/useContractAddress' +import { useExpiry } from './ensjs/public/useExpiry' +import { useOwner } from './ensjs/public/useOwner' +import { useWrapperData } from './ensjs/public/useWrapperData' import { useHasGlobalError } from './errors/useHasGlobalError' +import { useResolverStatus } from './resolver/useResolverStatus' +import { useProfile } from './useProfile' import { useProfileActions } from './useProfileActions' -import useWrapperApprovedForAll from './useWrapperApprovedForAll' const NOW_TIMESTAMP = 1588994800000 jest.spyOn(Date, 'now').mockImplementation(() => NOW_TIMESTAMP) -const mockUseResolverStatus = jest.fn().mockReturnValue({ - data: { - isAuthorized: true, - }, - isLoading: false, -}) -jest.mock('@app/hooks/resolver/useResolverStatus', () => ({ - useResolverStatus: () => mockUseResolverStatus(), -})) -jest.mock('@app/hooks/usePrimary') jest.mock('@app/transaction-flow/TransactionFlowProvider') + +jest.mock('@app/hooks/account/useAccountSafely') +jest.mock('@app/hooks/abilities/useAbilities') + +jest.mock('./useProfile') +jest.mock('./ensjs/public/useOwner') +jest.mock('./ensjs/public/useWrapperData') +jest.mock('./ensjs/public/useExpiry') + +jest.mock('./resolver/useResolverStatus') +jest.mock('@app/hooks/ensjs/public/usePrimaryName') + jest.mock('./errors/useHasGlobalError') -jest.mock('./useWrapperApprovedForAll') -const mockUsePrimary = mockFunction(usePrimary) +jest.mock('./chain/useContractAddress') + const mockUseTransactionFlow = mockFunction(useTransactionFlow) + +const mockUseAccountSafely = mockFunction(useAccountSafely) +const mockUseAbilities = mockFunction(useAbilities) + +const mockUseProfile = mockFunction(useProfile) +const mockUseOwner = mockFunction(useOwner) +const mockUseWrapperData = mockFunction(useWrapperData) +const mockUseExpiry = mockFunction(useExpiry) + +const mockUseResolverStatus = mockFunction(useResolverStatus) +const mockUsePrimaryName = mockFunction(usePrimaryName) + const mockUseHasGlobalError = mockFunction(useHasGlobalError) -const mockUseWrapperApprovedForAll = mockFunction(useWrapperApprovedForAll) -const mockCreateTransactionFlow = jest.fn() -const mockusePreparedDataInput = jest.fn() +const mockUseContractAddress = mockFunction(useContractAddress) -describe('useProfileActions', () => { - const props: any = { - name: 'test.eth', - address: '0x1234567890', - profile: { - address: '0x1234567890', - isMigrated: true, - }, - abilities: { - canEdit: true, - canDelete: true, - canDeleteContract: 'testcontract', - canDeleteMethod: 'testmethod', - canDeleteError: null, - canReclaim: true, - isPCCBurned: false, - isParentOwner: true, - }, - ownerData: { - ownershipLevel: 'nameWrapper', - owner: '0x1234567890', - }, - wrapperData: { - child: { - CANNOT_SET_RESOLVER: false, - }, +const mockCreateTransactionFlow = jest.fn() +const mockUsePreparedDataInput = jest.fn() + +type MockHookData { data: any }> = DeepPartial< + ReturnType['data'] +> + +const mockUseAbilitiesData: MockHookData = { + canEdit: true, + canDelete: true, + canDeleteContract: 'testcontract' as any, + canDeleteMethod: 'testmethod' as any, + canDeleteError: undefined, + canReclaim: true, + isPCCBurned: false, + isParentOwner: true, +} + +const mockUseProfileData: MockHookData = { + address: '0x1234567890', + isMigrated: true, +} + +const mockUseOwnerData: MockHookData = { + ownershipLevel: 'nameWrapper', + owner: '0x1234567890', +} + +const mockUseWrapperDataData: MockHookData = { + fuses: { + child: { + CANNOT_SET_RESOLVER: false, }, - expiryDate: new Date(NOW_TIMESTAMP + 1), - } + }, +} + +const mockUseExpiryData: MockHookData = { + expiry: createDateAndValue(BigInt(NOW_TIMESTAMP + 1)), +} + +const mockUseResolverStatusData: MockHookData = { + isAuthorized: true, +} +describe('useProfileActions', () => { beforeEach(() => { - mockUsePrimary.mockReturnValue({ - data: { name: undefined, beautifiedName: undefined }, + mockUseAccountSafely.mockReturnValue({ address: '0x1234567890' }) + mockUseAbilities.mockReturnValue({ + data: mockUseAbilitiesData, + isLoading: false, + }) + + mockUseProfile.mockReturnValue({ + data: mockUseProfileData, + isLoading: false, + }) + mockUseOwner.mockReturnValue({ + data: mockUseOwnerData, + isLoading: false, + }) + mockUseWrapperData.mockReturnValue({ + data: mockUseWrapperDataData, + isLoading: false, + }) + mockUseExpiry.mockReturnValue({ + data: mockUseExpiryData, + isLoading: false, + }) + + mockUseResolverStatus.mockReturnValue({ + data: mockUseResolverStatusData, + isLoading: false, + }) + mockUsePrimaryName.mockReturnValue({ + data: null, isLoading: false, - status: 'success', }) mockUseTransactionFlow.mockReturnValue({ usePreparedDataInput: () => (...args: any[]) => - mockusePreparedDataInput(...args), + mockUsePreparedDataInput(...args), createTransactionFlow: (...args: any[]) => mockCreateTransactionFlow(...args), }) mockUseHasGlobalError.mockReturnValue(false) - mockUseWrapperApprovedForAll.mockReturnValue({ approvedForAll: true, isLoading: false }) + + mockUseContractAddress.mockReturnValue('0xresolver') }) afterEach(() => { jest.clearAllMocks() }) - it('returns an object with profileActions and loading properties', () => { - const { result } = renderHook(() => useProfileActions(props)) + it('returns an object with profileActions and isLoading properties', () => { + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current).toHaveProperty('profileActions') - expect(result.current).toHaveProperty('loading') + expect(result.current).toHaveProperty('isLoading') }) it('returns an empty array for profileActions if address is falsy', () => { - const { result } = renderHook(() => useProfileActions({ ...props, address: '' })) + mockUseAccountSafely.mockReturnValue({ address: undefined }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current.profileActions).toEqual([]) }) it('returns an empty array for profileActions if isLoading is true', () => { - mockUsePrimary.mockReturnValue({ - data: { name: undefined, beautifiedName: undefined }, + mockUsePrimaryName.mockReturnValue({ + data: undefined, isLoading: true, - status: 'success', }) - const { result } = renderHook(() => useProfileActions({ ...props, isLoading: true })) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current.profileActions).toEqual([]) }) it('returns the correct action if subnameAbilities.canDelete is true', () => { - const { result } = renderHook(() => useProfileActions(props)) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current.profileActions).toContainEqual( expect.objectContaining({ label: 'tabs.profile.actions.deleteSubname.label', @@ -117,7 +180,7 @@ describe('useProfileActions', () => { }) it('returns the correct action if subnameAbilities.canReclaim is true', () => { - const { result } = renderHook(() => useProfileActions(props)) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current.profileActions).toContainEqual( expect.objectContaining({ label: 'tabs.profile.actions.reclaim.label', @@ -127,16 +190,15 @@ describe('useProfileActions', () => { }) it('should return the correct copy when subnameAbilities.canDeleteError has a string value', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - abilities: { - ...props.abilities, - canDelete: false, - canDeleteError: 'test error', - }, - }), - ) + mockUseAbilities.mockReturnValue({ + data: { + ...mockUseAbilitiesData, + canDelete: false, + canDeleteError: 'test error', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) expect(result.current.profileActions).toContainEqual( expect.objectContaining({ label: 'tabs.profile.actions.deleteSubname.label', @@ -147,7 +209,7 @@ describe('useProfileActions', () => { describe('delete subname', () => { it('should return a single transaction with normal subname when address is parent owner', () => { - const { result } = renderHook(() => useProfileActions(props)) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const deleteAction = result.current.profileActions?.find( (a) => a.label === 'tabs.profile.actions.deleteSubname.label', ) @@ -166,31 +228,32 @@ describe('useProfileActions', () => { }) }) it('should show data input if normal subname but address is child owner', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - abilities: { ...props.abilities, isParentOwner: false }, - }), - ) + mockUseAbilities.mockReturnValue({ + data: { + ...mockUseAbilitiesData, + isParentOwner: false, + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const deleteAction = result.current.profileActions?.find( (a) => a.label === 'tabs.profile.actions.deleteSubname.label', ) deleteAction!.onClick() - expect(mockusePreparedDataInput).toHaveBeenCalledWith( + expect(mockUsePreparedDataInput).toHaveBeenCalledWith( `delete-subname-not-parent-warning-test.eth`, { name: 'test.eth', contract: 'testcontract' }, ) }) it('should return a two step transaction flow for an unwrapped subname with wrapped parent', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - abilities: { - ...props.abilities, - canDeleteRequiresWrap: true, - }, - }), - ) + mockUseAbilities.mockReturnValue({ + data: { + ...mockUseAbilitiesData, + canDeleteRequiresWrap: true, + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const deleteAction = result.current.profileActions?.find( (a) => a.label === 'tabs.profile.actions.deleteSubname.label', ) @@ -202,7 +265,7 @@ describe('useProfileActions', () => { data: { contract: 'nameWrapper', name: 'test.eth', - newOwner: '0x1234567890', + newOwnerAddress: '0x1234567890', }, }, { @@ -230,7 +293,7 @@ describe('useProfileActions', () => { describe('set primary name', () => { it('should return an action for a single transaction with base mock data', async () => { - const { result } = renderHook(() => useProfileActions(props)) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -244,15 +307,14 @@ describe('useProfileActions', () => { }) it('should not return an action if profile is not migrated', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - isMigrated: false, - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + isMigrated: false, + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -260,18 +322,21 @@ describe('useProfileActions', () => { }) it('should not return an action if profile user is not controller or wrapped owner or resolved address', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - owner: '0xotherowner', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseOwner.mockReturnValue({ + data: { + ...mockUseOwnerData, + owner: '0xotherowner', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -279,19 +344,21 @@ describe('useProfileActions', () => { }) it('should return an action if profile user is controller but not wrapped owner or resolved address', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'registry', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseOwner.mockReturnValue({ + data: { + ...mockUseOwnerData, + ownershipLevel: 'registry', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -299,19 +366,14 @@ describe('useProfileActions', () => { }) it('should return an action if profile user is wrapped owner but not controller or resolved address', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -319,44 +381,40 @@ describe('useProfileActions', () => { }) it('should return an action if profile user is resolved address but not controller or wrapped owner', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0x1234567890', - }, - ownerData: { - ...props.ownerData, - owner: '0xotherowner', - }, - }), - ) + mockUseOwner.mockReturnValue({ + data: { + ...mockUseOwnerData, + owner: '0xotherowner', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) expect(setPrimaryAction).toBeDefined() }) - it('should return an action if expiry date is less than now', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - expiryDate: new Date(NOW_TIMESTAMP - 1), - }), - ) + it('should not return an action if expiry date is less than now', () => { + mockUseExpiry.mockReturnValue({ + data: { + expiry: createDateAndValue(BigInt(NOW_TIMESTAMP - 1)), + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) expect(setPrimaryAction).toBeUndefined() }) - it('should return not return action if primary name matches current name', () => { - mockUsePrimary.mockReturnValueOnce({ - data: { name: 'test.eth' }, + it('should not return an action if primary name matches current name', () => { + mockUsePrimaryName.mockReturnValue({ + data: { name: 'test.eth', beautifiedName: 'test.eth' }, isLoading: false, }) - const { result } = renderHook(() => useProfileActions(props)) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -368,24 +426,24 @@ describe('useProfileActions', () => { data: { isAuthorized: false }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - wrapperData: { + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseWrapperData.mockReturnValue({ + data: { + fuses: { child: { CANNOT_SET_RESOLVER: true, }, }, - }), - ) + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -393,28 +451,24 @@ describe('useProfileActions', () => { }) it('should return action if profile address does not match, is wrapped owner and CSR fuse is burned and resolver status isAuthorized is true', () => { - mockUseResolverStatus.mockReturnValueOnce({ - data: { isAuthorized: true }, + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - wrapperData: { + mockUseWrapperData.mockReturnValue({ + data: { + fuses: { child: { CANNOT_SET_RESOLVER: true, }, }, - }), - ) + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -426,24 +480,14 @@ describe('useProfileActions', () => { data: { isAuthorized: false }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - wrapperData: { - child: { - CANNOT_SET_RESOLVER: false, - }, - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -455,24 +499,30 @@ describe('useProfileActions', () => { data: { isAuthorized: false }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'registry', - }, - wrapperData: { + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseOwner.mockReturnValue({ + data: { + ...mockUseOwnerData, + ownershipLevel: 'registry', + }, + }) + mockUseWrapperData.mockReturnValue({ + data: { + fuses: { child: { CANNOT_SET_RESOLVER: true, }, }, - }), - ) + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -480,15 +530,14 @@ describe('useProfileActions', () => { }) it('should return an action with 2 transaction steps if profile address does not match user address', () => { - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -502,23 +551,21 @@ describe('useProfileActions', () => { data: { isAuthorized: false, hasMigratedRecord: true }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - selfAbilities: { - ...props.selfAbilites, - canEdit: true, - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseAbilities.mockReturnValue({ + data: { + ...mockUseAbilitiesData, + canEdit: true, + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -532,23 +579,21 @@ describe('useProfileActions', () => { data: { isAuthorized: false, hasMigratedRecord: false }, isLoading: false, }) - const { result } = renderHook(() => - useProfileActions({ - ...props, - profile: { - ...props.profile, - address: '0xotheraddress', - }, - selfAbilities: { - ...props.selfAbilites, - canEdit: true, - }, - ownerData: { - ...props.ownerData, - ownershipLevel: 'nameWrapper', - }, - }), - ) + mockUseProfile.mockReturnValue({ + data: { + ...mockUseProfileData, + address: '0xotheraddress', + }, + isLoading: false, + }) + mockUseAbilities.mockReturnValue({ + data: { + ...mockUseAbilitiesData, + canEdit: true, + }, + isLoading: false, + }) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) @@ -559,21 +604,18 @@ describe('useProfileActions', () => { it('should return an action that triggers unknownLable input if name is encrypted', () => { const { result } = renderHook(() => - useProfileActions({ - ...props, - name: `[${labelhash('test').slice(2)}].eth`, - }), + useProfileActions({ name: `[${labelhash('test').slice(2)}].eth` }), ) const setPrimaryAction = result.current.profileActions?.find( (action: any) => action.label === 'tabs.profile.actions.setAsPrimaryName.label', ) setPrimaryAction?.onClick() - expect(mockusePreparedDataInput).toHaveBeenCalled() - expect(mockusePreparedDataInput.mock.calls[0][0]).toBe( + expect(mockUsePreparedDataInput).toHaveBeenCalled() + expect(mockUsePreparedDataInput.mock.calls[0][0]).toBe( `setPrimaryName-[${labelhash('test').slice(2)}].eth-0x1234567890`, ) expect( - mockusePreparedDataInput.mock.calls[0][1].transactionFlowItem.transactions.length, + mockUsePreparedDataInput.mock.calls[0][1].transactionFlowItem.transactions.length, ).toEqual(1) }) }) diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index fc6dfc383..65ccc1830 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -1,23 +1,25 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Address } from 'viem' -import { GetOwnerReturnType, GetWrapperDataReturnType } from '@ensdomains/ensjs/public' import { checkIsDecrypted } from '@ensdomains/ensjs/utils' import { useTransactionFlow } from '@app/transaction-flow/TransactionFlowProvider' import { makeIntroItem } from '@app/transaction-flow/intro' import { makeTransactionItem } from '@app/transaction-flow/transaction' import { GenericTransaction } from '@app/transaction-flow/types' -import { Profile } from '@app/types' import { checkAvailablePrimaryName } from '@app/utils/checkAvailablePrimaryName' import { nameParts } from '@app/utils/name' import { useAbilities } from './abilities/useAbilities' +import { useAccountSafely } from './account/useAccountSafely' +import { useExpiry } from './ensjs/public/useExpiry' +import { useOwner } from './ensjs/public/useOwner' import { usePrimaryName } from './ensjs/public/usePrimaryName' +import { useWrapperData } from './ensjs/public/useWrapperData' import { useHasGlobalError } from './errors/useHasGlobalError' import { useGetPrimaryNameTransactionFlowItem } from './primary/useGetPrimaryNameTransactionFlowItem' import { useResolverStatus } from './resolver/useResolverStatus' +import { useProfile } from './useProfile' type Action = { onClick: () => void @@ -33,29 +35,23 @@ type Action = { type Props = { name: string - address: Address | undefined - profile: Profile | undefined - abilities: ReturnType['data'] - ownerData: GetOwnerReturnType | undefined - wrapperData: GetWrapperDataReturnType | undefined - expiryDate: Date | undefined + enabled?: boolean } -export const useProfileActions = ({ - name, - address, - profile, - abilities, - ownerData, - wrapperData, - expiryDate, -}: Props) => { +export const useProfileActions = ({ name, enabled = true }: Props) => { const { t } = useTranslation('profile') const { createTransactionFlow, usePreparedDataInput } = useTransactionFlow() - const isWrapped = ownerData?.ownershipLevel === 'nameWrapper' + const { address } = useAccountSafely() + const { data: abilities, isLoading: isAbilitiesLoading } = useAbilities({ name, enabled }) + + const { data: profile, isLoading: isProfileLoading } = useProfile({ name }) + const { data: ownerData, isLoading: isOwnerLoading } = useOwner({ name }) + const { data: wrapperData, isLoading: isWrapperDataLoading } = useWrapperData({ name }) + const { data: expiryData, isLoading: isExpiryLoading } = useExpiry({ name }) + const expiryDate = expiryData?.expiry?.date - const resolverStatus = useResolverStatus({ + const { data: resolverStatus, isLoading: isResolverStatusLoading } = useResolverStatus({ name, migratedRecordsMatch: address ? { type: 'address', match: { id: 60, value: address } } @@ -63,11 +59,11 @@ export const useProfileActions = ({ enabled: !!ownerData, }) - const primary = usePrimaryName({ address }) + const { data: primaryData, isLoading: isPrimaryNameLoading } = usePrimaryName({ address }) const isAvailablePrimaryName = checkAvailablePrimaryName( - primary.data?.name, - resolverStatus.data, + primaryData?.name, + resolverStatus, )({ name, relation: { @@ -81,12 +77,14 @@ export const useProfileActions = ({ isMigrated: !!profile?.isMigrated, }) + const isWrapped = ownerData?.ownershipLevel === 'nameWrapper' + const getPrimaryNameTransactionFlowItem = useGetPrimaryNameTransactionFlowItem({ address, isWrapped, profileAddress: profile?.address, resolverAddress: profile?.resolverAddress, - resolverStatus: resolverStatus.data, + resolverStatus, }) const hasGlobalError = useHasGlobalError() @@ -101,7 +99,14 @@ export const useProfileActions = ({ ) const isLoading = - primary.isLoading || resolverStatus.isLoading || getPrimaryNameTransactionFlowItem.isLoading + isAbilitiesLoading || + isProfileLoading || + isOwnerLoading || + isWrapperDataLoading || + isExpiryLoading || + isPrimaryNameLoading || + isResolverStatusLoading || + getPrimaryNameTransactionFlowItem.isLoading const profileActions = useMemo(() => { const actions: Action[] = [] @@ -276,6 +281,6 @@ export const useProfileActions = ({ return { profileActions, - loading: isLoading, + isLoading: isLoading, } } diff --git a/src/hooks/useProfileEditor.tsx b/src/hooks/useProfileEditor.tsx index d4f99abb1..d5e980de7 100644 --- a/src/hooks/useProfileEditor.tsx +++ b/src/hooks/useProfileEditor.tsx @@ -21,7 +21,7 @@ import { formSafeKey, getDirtyFields, } from '@app/utils/editor' -import { validateCryptoAddress } from '@app/utils/validate' +import { validateCryptoAddress } from '@app/validators/validateAddress' import { ContentHashProvider } from '../utils/contenthash' import { validateContentHash } from '../validators/validateContentHash' diff --git a/src/hooks/useProfileEditorForm.test.ts b/src/hooks/useProfileEditorForm.test.ts index dcc9f5a80..18d57ee6e 100644 --- a/src/hooks/useProfileEditorForm.test.ts +++ b/src/hooks/useProfileEditorForm.test.ts @@ -1,4 +1,4 @@ -import { renderHook } from '@testing-library/react-hooks' +import { act, renderHook } from '@app/test-utils' import registerI18n from '@app/../public/locales/en/register.json' import supportedAddresses from '@app/constants/supportedAddresses.json' @@ -23,7 +23,7 @@ const avatarRecrod: ProfileRecord = { const records: ProfileRecord[] = [baseRecord, avatarRecrod] const validEth = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' -const invalidEth = '0x70997970C51812dc3A010C7d01b50e0d17dc79C4' +const invalidEth = '0x70997970C51812dc3A010C7d01b50e0d17dc79X4' const validOnion = 'onion3://p53lf57qovyuvwsc6xnrppyply3vtqm7l6pcobkmyqsiofyeznfu5uqd' const invalidOnion = 'onion3://p53lf57qovyuvwsc6xnrppyply3vtqm7l6pcobkmyqsiofyeznfu5uq' @@ -223,7 +223,9 @@ describe('useProfileEditorForm', () => { describe('removeRecordByTypeAndKey', () => { it('should be able to remove a address record by type and key', async () => { const { result } = renderHook(() => useProfileEditorForm(records)) - result.current.removeRecordByGroupAndKey('address', 'ETH') + act(() => { + result.current.removeRecordByGroupAndKey('address', 'ETH') + }) expect(result.current.getRecords().length).toBe(1) }) }) @@ -231,10 +233,12 @@ describe('useProfileEditorForm', () => { describe('addRecords', () => { it('should add value property if it does not exist', async () => { const { result } = renderHook(() => useProfileEditorForm(records)) - result.current.addRecords({ - key: 'description', - group: 'general', - type: 'text', + act(() => { + result.current.addRecords({ + key: 'description', + group: 'general', + type: 'text', + }) }) expect(result.current.getRecords()).toEqual([ baseRecord, @@ -250,11 +254,13 @@ describe('useProfileEditorForm', () => { it('should transfer value property for an added record', async () => { const { result } = renderHook(() => useProfileEditorForm(records)) - result.current.addRecords({ - key: 'description', - group: 'general', - type: 'text', - value: 'testing', + act(() => { + result.current.addRecords({ + key: 'description', + group: 'general', + type: 'text', + value: 'testing', + }) }) expect(result.current.getRecords()).toEqual([ baseRecord, @@ -270,10 +276,12 @@ describe('useProfileEditorForm', () => { it('should be able to add text record individually', async () => { const { result } = renderHook(() => useProfileEditorForm(records)) - result.current.addRecords({ - key: 'description', - group: 'general', - type: 'text', + act(() => { + result.current.addRecords({ + key: 'description', + group: 'general', + type: 'text', + }) }) expect(result.current.getRecords()).toEqual([ baseRecord, @@ -298,11 +306,13 @@ describe('useProfileEditorForm', () => { ...records, ] const { result } = renderHook(() => useProfileEditorForm(currentRecords)) - result.current.addRecords({ - key: 'description', - group: 'general', - type: 'text', - value: 'test', + act(() => { + result.current.addRecords({ + key: 'description', + group: 'general', + type: 'text', + value: 'test', + }) }) expect(result.current.getRecords()).toEqual(currentRecords) }) @@ -326,7 +336,9 @@ describe('useProfileEditorForm', () => { const { result } = renderHook(() => useProfileEditorForm(currentRecords)) const contenthashOptions = profileRecordOptions.filter((o) => o.type === 'contenthash') expect(contenthashOptions.length).toBe(5) - result.current.addRecords(contenthashOptions) + act(() => { + result.current.addRecords(contenthashOptions) + }) expect(result.current.getRecords()).toEqual(currentRecords) }) @@ -338,7 +350,9 @@ describe('useProfileEditorForm', () => { const { result } = renderHook(() => useProfileEditorForm(currentRecords)) const contenthashOptions = profileRecordOptions.filter((o) => o.type === 'contenthash') expect(contenthashOptions.length).toBe(5) - result.current.addRecords(contenthashOptions) + act(() => { + result.current.addRecords(contenthashOptions) + }) expect(result.current.getRecords()).toEqual(currentRecords) }) }) diff --git a/src/hooks/useProfileEditorForm.tsx b/src/hooks/useProfileEditorForm.tsx index 3258870aa..6b8c62ed0 100644 --- a/src/hooks/useProfileEditorForm.tsx +++ b/src/hooks/useProfileEditorForm.tsx @@ -8,7 +8,7 @@ import { import { ProfileRecord, ProfileRecordGroup } from '@app/constants/profileRecordOptions' import supportedAddresses from '@app/constants/supportedAddresses.json' import { AvatarEditorType } from '@app/types' -import { validateCryptoAddress } from '@app/utils/validate' +import { validateCryptoAddress } from '@app/validators/validateAddress' import { validateContentHash } from '@app/validators/validateContentHash' import { validateUrl } from '@app/validators/validateUrl' diff --git a/src/hooks/useRegistrationParams.test.ts b/src/hooks/useRegistrationParams.test.ts index 36e8b5832..e04406fcb 100644 --- a/src/hooks/useRegistrationParams.test.ts +++ b/src/hooks/useRegistrationParams.test.ts @@ -10,7 +10,7 @@ describe('useRegistrationParams()', () => { owner: '0xowner', registrationData: { years: 1, - resolver: '0xresolver', + resolverAddress: '0xresolver', secret: '0xsecret', records: [ { @@ -26,19 +26,19 @@ describe('useRegistrationParams()', () => { }), ) expect(result.current).toMatchInlineSnapshot(` - Object { + { "duration": 31536000, - "fuses": Object { - "named": Array [], - "unnamed": Array [], + "fuses": { + "named": [], + "unnamed": [], }, "name": "test", "owner": "0xowner", - "records": Object { + "records": { "clearRecords": false, - "coinTypes": Array [ - Object { - "key": "ETH", + "coins": [ + { + "coin": "ETH", "value": "0x4b2d639aC1B0497e932F8cE234eFd3B3Df9a9B74", }, ], @@ -56,7 +56,7 @@ describe('useRegistrationParams()', () => { owner: '0xowner', registrationData: { years: 1, - resolver: '0xresolver', + resolverAddress: '0xresolver', secret: '0xsecret', records: [ { @@ -84,28 +84,28 @@ describe('useRegistrationParams()', () => { }), ) expect(result.current).toMatchInlineSnapshot(` - Object { + { "duration": 31536000, - "fuses": Object { - "named": Array [], - "unnamed": Array [], + "fuses": { + "named": [], + "unnamed": [], }, "name": "test", "owner": "0xowner", - "records": Object { + "records": { "clearRecords": false, - "coinTypes": Array [ - Object { - "key": "ETH", + "coins": [ + { + "coin": "ETH", "value": "0x4b2d639aC1B0497e932F8cE234eFd3B3Df9a9B74", }, - Object { - "key": "BTC", + { + "coin": "BTC", "value": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", }, ], - "texts": Array [ - Object { + "texts": [ + { "key": "description", "value": "This is a description", }, @@ -124,7 +124,7 @@ describe('useRegistrationParams()', () => { owner: '0xowner', registrationData: { years: 1, - resolver: '0xresolver', + resolverAddress: '0xresolver', secret: '0xsecret', records: [], clearRecords: true, @@ -133,15 +133,15 @@ describe('useRegistrationParams()', () => { }), ) expect(result.current).toMatchInlineSnapshot(` - Object { + { "duration": 31536000, - "fuses": Object { - "named": Array [], - "unnamed": Array [], + "fuses": { + "named": [], + "unnamed": [], }, "name": "test", "owner": "0xowner", - "records": Object { + "records": { "clearRecords": true, }, "resolverAddress": "0xresolver", @@ -157,11 +157,10 @@ describe('useRegistrationParams()', () => { owner: '0xowner', registrationData: { years: 1, - resolver: '0xresolver', + resolverAddress: '0xresolver', secret: '0xsecret', records: [], permissions: { - CAN_DO_EVERYTHING: false, CANNOT_APPROVE: false, CANNOT_BURN_FUSES: false, CANNOT_CREATE_SUBDOMAIN: false, @@ -176,17 +175,17 @@ describe('useRegistrationParams()', () => { }), ) expect(result.current).toMatchInlineSnapshot(` - Object { + { "duration": 31536000, - "fuses": Object { - "named": Array [ + "fuses": { + "named": [ "CANNOT_UNWRAP", ], - "unnamed": Array [], + "unnamed": [], }, "name": "test", "owner": "0xowner", - "records": Object { + "records": { "clearRecords": false, }, "resolverAddress": "0xresolver", diff --git a/src/hooks/useRegistrationReducer.test.ts b/src/hooks/useRegistrationReducer.test.ts deleted file mode 100644 index 04b411872..000000000 --- a/src/hooks/useRegistrationReducer.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-expect-error -import getRandomValues from 'polyfill-crypto.getrandomvalues' - -import { randomSecret } from './useRegistrationReducer' - -Object.defineProperty(window, 'crypto', { - value: { - getRandomValues, - }, -}) - -describe('useRegistrationReducer', () => { - it('randomSecret should add platform and version referral prefix', async () => { - const platformSource = '9923eb94' - const version = '00000003' - const secret = randomSecret() - expect(secret.length).toEqual(66) - expect(secret.slice(2, 16 + 2)).toEqual(platformSource + version) - }) -}) diff --git a/src/hooks/useResolverHasInterfaces.test.ts b/src/hooks/useResolverHasInterfaces.test.ts index 098774b1d..713647557 100644 --- a/src/hooks/useResolverHasInterfaces.test.ts +++ b/src/hooks/useResolverHasInterfaces.test.ts @@ -1,39 +1,27 @@ -import { mockFunction, renderHook } from '@app/test-utils' - -import { useNetwork, useProvider } from 'wagmi' +import { renderHook, waitFor } from '@app/test-utils' +import { KNOWN_RESOLVER_DATA } from '@app/constants/resolverAddressData' +import { RESOLVER_INTERFACE_IDS, ResolverInterfaceName } from '@app/constants/resolverInterfaceIds' import { useResolverHasInterfaces } from '@app/hooks/useResolverHasInterfaces' -import { KnownResolveAddresses } from '@app/validators/validateResolver' - -jest.mock('wagmi', () => ({ - ...jest.requireActual('@tanstack/react-query'), - useNetwork: jest.fn(), - useProvider: jest.fn(), -})) -const mockUseProvider = mockFunction(useProvider) -const mockUseNetwork = mockFunction(useNetwork) +const ResolverAddresses = KNOWN_RESOLVER_DATA[1]! -const ResolverAddresses = Object.keys(KnownResolveAddresses) as string[] +const interfaceIdToName = (interfaceId: string) => + Object.entries(RESOLVER_INTERFACE_IDS).find( + ([, value]) => value === interfaceId, + )![0] as ResolverInterfaceName describe('useResolverHasInterfaces', () => { - beforeEach(() => { - mockUseProvider.mockReturnValue({}) - mockUseNetwork.mockReturnValue({ - chain: { - id: 1, - }, - }) - }) - - ResolverAddresses.forEach((address) => { - it(`should return true for known resolver address: ${address}`, async () => { - const interfaces = KnownResolveAddresses[address].supportedInterfaces - const { result, waitForValueToChange } = renderHook(() => - useResolverHasInterfaces(interfaces, address, false), + ResolverAddresses.forEach((item) => { + it(`should return true for known resolver address: ${item.address}`, async () => { + const { result } = renderHook(() => + useResolverHasInterfaces({ + interfaceNames: item.supportedInterfaces.map(interfaceIdToName), + resolverAddress: item.address, + }), ) - await waitForValueToChange(() => result.current.isLoading) - expect(result.current.hasInterface).toBe(true) + await waitFor(() => !result.current.isLoading) + expect(result.current.data).toEqual(item.supportedInterfaces.map(() => true)) }) }) }) diff --git a/src/hooks/useValidate.test.ts b/src/hooks/useValidate.test.ts index 0ab72d460..f568d6161 100644 --- a/src/hooks/useValidate.test.ts +++ b/src/hooks/useValidate.test.ts @@ -4,15 +4,15 @@ import { useValidate } from './useValidate' describe('useValidate', () => { it('should return isNonASCII as false if all ascii', async () => { - const { result } = renderHook(() => useValidate('test')) + const { result } = renderHook(() => useValidate({ input: 'test' })) expect(result.current.isNonASCII).toEqual(false) }) it('should return isNonASCII as true if contains non ascii', async () => { - const { result } = renderHook(() => useValidate('test❤️')) + const { result } = renderHook(() => useValidate({ input: 'test❤️' })) expect(result.current.isNonASCII).toEqual(true) }) it('should not error if % symbol is in input', async () => { - const { result } = renderHook(() => useValidate('%')) + const { result } = renderHook(() => useValidate({ input: '%' })) expect(result.current.isValid).toEqual(false) }) }) diff --git a/src/hooks/useValidateSubnameLabel.test.ts b/src/hooks/useValidateSubnameLabel.test.ts index 6a0625b65..3f395aef4 100644 --- a/src/hooks/useValidateSubnameLabel.test.ts +++ b/src/hooks/useValidateSubnameLabel.test.ts @@ -1,33 +1,30 @@ import { mockFunction, renderHook } from '@app/test-utils' -import { toUtf8Bytes } from '@ethersproject/strings/lib/utf8' +import { stringToBytes } from 'viem' + +import { GetOwnerReturnType, GetWrapperDataReturnType } from '@ensdomains/ensjs/public' import { DeepPartial } from '@app/types' -import { useEns } from '@app/utils/EnsProvider' import { emptyAddress } from '@app/utils/constants' +import { useOwner } from './ensjs/public/useOwner' +import { useWrapperData } from './ensjs/public/useWrapperData' import { usePccExpired } from './fuses/usePccExpired' -import { useGetWrapperData } from './useGetWrapperData' import { useValidateSubnameLabel } from './useValidateSubnameLabel' const BYTE256 = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' -jest.mock('@app/utils/EnsProvider') -jest.mock('@app/hooks/useGetWrapperData') +jest.mock('@app/hooks/ensjs/public/useWrapperData') +jest.mock('@app/hooks/ensjs/public/useOwner') jest.mock('@app/hooks/fuses/usePccExpired') -const mockUseEns = mockFunction(useEns) -const mockGetOwner = jest.fn() -mockUseEns.mockReturnValue({ - ready: true, - getOwner: mockGetOwner, -}) +const mockUseWrapperData = mockFunction(useWrapperData) +const mockUseOwner = mockFunction(useOwner) -const mockUseGetWrapperData = mockFunction(useGetWrapperData) const mockUsePccExpired = mockFunction(usePccExpired) -type OwnerData = Awaited['getOwner']>> +type OwnerData = NonNullable const makeOwnerData = ( type: 'nameWrapper' | 'registrar' | 'registry', overrides: DeepPartial = {}, @@ -54,24 +51,27 @@ const makeOwnerData = ( } as OwnerData } -type WrapperData = Awaited['getWrapperData']>> +type WrapperData = NonNullable const makeWrapperData = (overrides: DeepPartial = {}) => { - const { parent = {}, child = {}, ...data } = overrides + const { fuses: { parent, child, value } = { parent: {}, child: {} }, ...data } = overrides return { - parent: { - PARENT_CANNOT_CONTROL: false, - CAN_EXTEND_EXPIRY: false, - ...parent, - }, - child: { - CANNOT_UNWRAP: false, - CANNOT_BURN_FUSES: false, - CANNOT_TRANSFER: false, - CANNOT_SET_RESOLVER: false, - CANNOT_SET_TTL: false, - CANNOT_APPROVE: false, - CANNOT_CREATE_SUBDOMAIN: false, - ...child, + fuses: { + parent: { + PARENT_CANNOT_CONTROL: false, + CAN_EXTEND_EXPIRY: false, + ...parent, + }, + child: { + CANNOT_UNWRAP: false, + CANNOT_BURN_FUSES: false, + CANNOT_TRANSFER: false, + CANNOT_SET_RESOLVER: false, + CANNOT_SET_TTL: false, + CANNOT_APPROVE: false, + CANNOT_CREATE_SUBDOMAIN: false, + ...child, + }, + value, }, owner: '0x0000000000000000000000000000000000000000', ...data, @@ -89,7 +89,6 @@ const groups = [ label: 'test', ownerData: makeOwnerData('registrar'), wrapperData: makeWrapperData(), - skipWaitForNextUpdate: true, result: { valid: false, isLoading: false, @@ -259,8 +258,13 @@ const groups = [ label: 'pccburned', ownerData: makeOwnerData('nameWrapper'), wrapperData: makeWrapperData({ - parent: { PARENT_CANNOT_CONTROL: true }, - expiryDate: new Date('2020-01-01'), + fuses: { + parent: { PARENT_CANNOT_CONTROL: true }, + }, + expiry: { + date: new Date('2020-01-01'), + value: BigInt(new Date('2020-01-01').getTime()), + }, }), result: { valid: false, @@ -276,7 +280,6 @@ const groups = [ label: 'hello world', ownerData: makeOwnerData('nameWrapper'), wrapperData: makeWrapperData(), - skipWaitForNextUpdate: true, result: { valid: false, isLoading: false, @@ -290,7 +293,6 @@ const groups = [ label: 'hello.world', ownerData: makeOwnerData('nameWrapper'), wrapperData: makeWrapperData(), - skipWaitForNextUpdate: true, result: { valid: false, isLoading: false, @@ -304,7 +306,6 @@ const groups = [ label: 'hello.world', ownerData: makeOwnerData('nameWrapper'), wrapperData: makeWrapperData(), - skipWaitForNextUpdate: true, pccBurned: true, result: { valid: false, @@ -322,7 +323,7 @@ afterEach(() => { describe('BYTE256', () => { it('should be 256 bytes', async () => { - expect(toUtf8Bytes(BYTE256).length).toEqual(256) + expect(stringToBytes(BYTE256).length).toEqual(256) }) }) @@ -331,17 +332,23 @@ describe('useValidateSubnameLabel', () => { describe(group.description, () => { group.tests.forEach((test) => { it(test.description, async () => { - mockGetOwner.mockReturnValue(test.ownerData) - mockUseGetWrapperData.mockReturnValue({ - wrapperData: test.wrapperData, + mockUseOwner.mockReturnValue({ + data: test.ownerData, + isLoading: false, + }) + mockUseWrapperData.mockReturnValue({ + data: test.wrapperData, isLoading: false, }) mockUsePccExpired.mockReturnValue(!!(test as any).pccExpired) - const { result, waitForNextUpdate } = renderHook(() => - useValidateSubnameLabel(test.name, test.label, test.isWrapped), + const { result } = renderHook(() => + useValidateSubnameLabel({ + name: test.name, + label: test.label, + isWrapped: test.isWrapped, + }), ) - if (!test.skipWaitForNextUpdate) await waitForNextUpdate() expect(result.current).toEqual(test.result) }) }) diff --git a/src/test-utils.tsx b/src/test-utils.tsx index a1cfa7c1b..06c175c2d 100644 --- a/src/test-utils.tsx +++ b/src/test-utils.tsx @@ -1,7 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { RenderOptions, render } from '@testing-library/react' -import { RenderHookOptions, renderHook } from '@testing-library/react-hooks' +import { RenderHookOptions, RenderOptions, render, renderHook } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { MockConnector } from '@wagmi/core/connectors/mock' import React, { FC, ReactElement } from 'react' diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 2a25ef15a..0bdd35956 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -143,3 +143,8 @@ export const calculateValueWithBuffer = (value: bigint) => const encodedLabelRegex = /\[[a-fA-F0-9]{64}\]/g export const getEncodedLabelAmount = (name: string) => name.match(encodedLabelRegex)?.length || 0 + +export const createDateAndValue = (value: TValue) => ({ + date: new Date(Number(value)), + value, +}) diff --git a/src/utils/validate.ts b/src/validators/validateAddress.ts similarity index 100% rename from src/utils/validate.ts rename to src/validators/validateAddress.ts