Skip to content

Commit

Permalink
test coverage 100%
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkas committed Aug 7, 2024
1 parent c4d7abd commit c843ec0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/identity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ export type GetAttestationsOptions = {
* Note: exported as public Type
*/
export type GetAvatar = {
chain?: Chain; // Optional chain for domain resolution
ensName: string; // The ENS name to fetch the avatar for.
chain?: Chain; // Optional chain for domain resolution
};

/**
Expand Down
70 changes: 57 additions & 13 deletions src/identity/utils/getAvatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe('getAvatar', () => {
universalResolverAddress: RESOLVER_ADDRESSES_BY_CHAIN_ID[base.id],
});
expect(getChainPublicClient).toHaveBeenCalledWith(base);
expect(getChainPublicClient).not.toHaveBeenCalledWith(mainnet);
});

it('should resolve to base sepolia avatar', async () => {
Expand All @@ -69,50 +70,93 @@ describe('getAvatar', () => {
universalResolverAddress: RESOLVER_ADDRESSES_BY_CHAIN_ID[baseSepolia.id],
});
expect(getChainPublicClient).toHaveBeenCalledWith(baseSepolia);
expect(getChainPublicClient).not.toHaveBeenCalledWith(mainnet);
});

it('should default to mainnet when base mainnet avatar is not available', async () => {
const ensName = 'shrek.base.eth';
const expectedAvatarUrl = null;
const expectedBaseAvatarUrl = null;
const expectedMainnetAvatarUrl = 'mainnetname.eth';

mockGetEnsAvatar
.mockResolvedValueOnce(expectedBaseAvatarUrl)
.mockResolvedValueOnce(expectedMainnetAvatarUrl);

mockGetEnsAvatar.mockResolvedValue(expectedAvatarUrl);
const avatarUrl = await getAvatar({ ensName, chain: base });

expect(avatarUrl).toBe(null);
expect(mockGetEnsAvatar).toHaveBeenCalledWith({
expect(avatarUrl).toBe(expectedMainnetAvatarUrl);
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(1, {
name: ensName,
universalResolverAddress: RESOLVER_ADDRESSES_BY_CHAIN_ID[base.id],
});

expect(getChainPublicClient).toHaveBeenCalledWith(base);
expect(getChainPublicClient).toHaveBeenNthCalledWith(1, base);

// getAvatar defaulted to mainnet
expect(getChainPublicClient).toHaveBeenCalledWith(mainnet);
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(2, {
name: ensName,
universalResolverAddress: undefined,
});
expect(getChainPublicClient).toHaveBeenNthCalledWith(2, mainnet);
});

it('should default to mainnet when base sepolia avatar is not available', async () => {
const ensName = 'shrek.basetest.eth';
const expectedAvatarUrl = null;
const expectedBaseAvatarUrl = null;
const expectedMainnetAvatarUrl = 'mainnetname.eth';

mockGetEnsAvatar.mockResolvedValue(expectedAvatarUrl);
mockGetEnsAvatar
.mockResolvedValueOnce(expectedBaseAvatarUrl)
.mockResolvedValueOnce(expectedMainnetAvatarUrl);

const avatarUrl = await getAvatar({ ensName, chain: baseSepolia });

expect(avatarUrl).toBe(expectedAvatarUrl);
expect(mockGetEnsAvatar).toHaveBeenCalledWith({
expect(avatarUrl).toBe(expectedMainnetAvatarUrl);
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(1, {
name: ensName,
universalResolverAddress: RESOLVER_ADDRESSES_BY_CHAIN_ID[baseSepolia.id],
});
expect(getChainPublicClient).toHaveBeenCalledWith(baseSepolia);
expect(getChainPublicClient).toHaveBeenNthCalledWith(1, baseSepolia);

// getAvatar defaulted to mainnet
expect(getChainPublicClient).toHaveBeenCalledWith(mainnet);
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(2, {
name: ensName,
universalResolverAddress: undefined,
});
expect(getChainPublicClient).toHaveBeenNthCalledWith(2, mainnet);
});

it('should throw an error on unsupported chain', async () => {
const ensName = 'shrek.basetest.eth';
await expect(getAvatar({ ensName, chain: optimism })).rejects.toBe(
'ChainId not supported, avatar resolution is only supported on Ethereum and Base.',
);
expect(getChainPublicClient).not.toHaveBeenCalled();
});

it('should ignore base call error and default to mainnet', async () => {
const ensName = 'shrek.base.eth';
const expectedMainnetAvatarUrl = 'mainnetname.eth';

mockGetEnsAvatar
.mockImplementationOnce(() => {
throw new Error('thrown error');
})
.mockResolvedValueOnce(expectedMainnetAvatarUrl);

const avatarUrl = await getAvatar({ ensName, chain: base });

expect(avatarUrl).toBe(expectedMainnetAvatarUrl);
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(1, {
name: ensName,
universalResolverAddress: RESOLVER_ADDRESSES_BY_CHAIN_ID[base.id],
});
expect(getChainPublicClient).toHaveBeenNthCalledWith(1, base);

// getAvatar defaulted to mainnet
expect(mockGetEnsAvatar).toHaveBeenNthCalledWith(2, {
name: ensName,
universalResolverAddress: undefined,
});
expect(getChainPublicClient).toHaveBeenNthCalledWith(2, mainnet);
});
});
9 changes: 7 additions & 2 deletions src/identity/utils/getAvatar.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { mainnet } from 'viem/chains';
import { normalize } from 'viem/ens';
import type { GetEnsAvatarReturnType } from 'wagmi/actions';
import { isBase } from '../../isBase';
import { isEthereum } from '../../isEthereum';
import { getChainPublicClient } from '../../network/getChainPublicClient';
import { RESOLVER_ADDRESSES_BY_CHAIN_ID } from '../constants';
import type { GetAvatar, GetAvatarReturnType } from '../types';

/**
* An asynchronous function to fetch the Ethereum Name Service (ENS)
* avatar for a given Ethereum name. It returns the ENS name if it exists,
* or null if it doesn't or in case of an error.
*/

export const getAvatar = async ({
ensName,
chain = mainnet,
Expand All @@ -31,7 +36,7 @@ export const getAvatar = async ({
});

if (baseEnsAvatar) {
return baseEnsAvatar as GetEnsAvatarReturnType;
return baseEnsAvatar;
}
} catch (_error) {
// This is a best effort attempt, so we don't need to do anything here.
Expand Down

0 comments on commit c843ec0

Please sign in to comment.