Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add account generator for unit test #304

Merged
merged 10 commits into from
Jul 31, 2024
2 changes: 1 addition & 1 deletion packages/starknet-snap/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MockSnapProvider } from './src/__mocks__/snap-provider.mock';
import { MockSnapProvider } from './test/snap-provider.mock';

// eslint-disable-next-line no-restricted-globals
const globalAny: any = global;
Expand Down
1 change: 1 addition & 0 deletions packages/starknet-snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@types/sinon-chai": "^3.2.8",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"bip39": "^3.1.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"cross-env": "^7.0.3",
Expand Down
42 changes: 0 additions & 42 deletions packages/starknet-snap/src/__mocks__/snap-provider.mock.ts

This file was deleted.

172 changes: 168 additions & 4 deletions packages/starknet-snap/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { onRpcRequest } from '.';
import { constants } from 'starknet';

import { onRpcRequest, onHomePage } from '.';
import { manageStateSpy } from '../test/snap-provider.mock';
import { generateAccounts, type StarknetAccount } from '../test/utils';
import * as createAccountApi from './createAccount';
import type { SnapState } from './types/snapState';
import {
ETHER_MAINNET,
ETHER_SEPOLIA_TESTNET,
STARKNET_MAINNET_NETWORK,
STARKNET_SEPOLIA_TESTNET_NETWORK,
} from './utils/constants';
import * as keyPairUtils from './utils/keyPair';
import { LogLevel, logger } from './utils/logger';
import * as starknetUtils from './utils/starknetUtils';

jest.mock('./utils/logger');

describe('onRpcRequest', function () {
describe('onRpcRequest', () => {
const createMockSpy = () => {
const createAccountSpy = jest.spyOn(createAccountApi, 'createAccount');
const keyPairSpy = jest.spyOn(keyPairUtils, 'getAddressKeyDeriver');
Expand All @@ -29,7 +41,7 @@ describe('onRpcRequest', function () {
};
};

it('processes request successfully', async function () {
it('processes request successfully', async () => {
const { createAccountSpy, keyPairSpy, getLogLevelSpy } = createMockSpy();

createAccountSpy.mockReturnThis();
Expand All @@ -42,7 +54,7 @@ describe('onRpcRequest', function () {
expect(createAccountSpy).toHaveBeenCalledTimes(1);
});

it('throws `Unable to execute the rpc request` error if an error has thrown and LogLevel is 0', async function () {
it('throws `Unable to execute the rpc request` error if an error has thrown and LogLevel is 0', async () => {
const { createAccountSpy, keyPairSpy, getLogLevelSpy } = createMockSpy();

createAccountSpy.mockRejectedValue(new Error('Custom Error'));
Expand Down Expand Up @@ -80,3 +92,155 @@ describe('onRpcRequest', function () {
},
);
});

describe('onHomePage', () => {
const state: SnapState = {
accContracts: [],
erc20Tokens: [ETHER_MAINNET, ETHER_SEPOLIA_TESTNET],
networks: [STARKNET_MAINNET_NETWORK, STARKNET_SEPOLIA_TESTNET_NETWORK],
transactions: [],
currentNetwork: undefined,
};

const mockState = (snapState: SnapState) => {
manageStateSpy.mockResolvedValue(snapState);
};

const mockAccount = async (chainId: constants.StarknetChainId) => {
const accounts = await generateAccounts(chainId);
return accounts[0];
};

const mockAccountDiscovery = (account: StarknetAccount) => {
const getKeysFromAddressIndexSpy = jest.spyOn(
starknetUtils,
'getKeysFromAddressIndex',
);
const getCorrectContractAddressSpy = jest.spyOn(
starknetUtils,
'getCorrectContractAddress',
);

getKeysFromAddressIndexSpy.mockResolvedValue({
privateKey: account.privateKey,
publicKey: account.publicKey,
addressIndex: account.addressIndex,
derivationPath: account.derivationPath as unknown as any,
});

getCorrectContractAddressSpy.mockResolvedValue({
address: account.address,
signerPubKey: account.publicKey,
upgradeRequired: false,
deployRequired: false,
});

return {
getKeysFromAddressIndexSpy,
getCorrectContractAddressSpy,
};
};

const mockGetBalance = (balance: string) => {
const getBalanceSpy = jest.spyOn(starknetUtils, 'getBalance');
getBalanceSpy.mockResolvedValue(balance);
};

it('renders user address, user balance and network', async () => {
const account = await mockAccount(constants.StarknetChainId.SN_SEPOLIA);
mockState(state);
mockAccountDiscovery(account);
mockGetBalance('1000');

const result = await onHomePage();

expect(result).toStrictEqual({
content: {
type: 'panel',
children: [
{ type: 'text', value: 'Address' },
{
type: 'copyable',
value: account.address,
},
{
type: 'row',
label: 'Network',
value: {
type: 'text',
value: STARKNET_SEPOLIA_TESTNET_NETWORK.name,
},
},
{
type: 'row',
label: 'Balance',
value: {
type: 'text',
value: '0.000000000000001 ETH',
},
},
{ type: 'divider' },
{
type: 'text',
value:
'Visit the [companion dapp for Starknet](https://snaps.consensys.io/starknet) to manage your account.',
},
],
},
});
});

it('renders with network from state if `currentNetwork` is not undefined', async () => {
const network = STARKNET_MAINNET_NETWORK;
const account = await mockAccount(constants.StarknetChainId.SN_MAIN);
mockState({
...state,
currentNetwork: network,
});
mockAccountDiscovery(account);
mockGetBalance('1000');

const result = await onHomePage();

expect(result).toStrictEqual({
content: {
type: 'panel',
children: [
{ type: 'text', value: 'Address' },
{
type: 'copyable',
value: account.address,
},
{
type: 'row',
label: 'Network',
value: {
type: 'text',
value: network.name,
},
},
{
type: 'row',
label: 'Balance',
value: {
type: 'text',
value: '0.000000000000001 ETH',
},
},
{ type: 'divider' },
{
type: 'text',
value:
'Visit the [companion dapp for Starknet](https://snaps.consensys.io/starknet) to manage your account.',
},
],
},
});
});

it('throws `Unable to initialize Snap HomePage` error when state not found', async () => {
await expect(onHomePage()).rejects.toThrow(
'Unable to initialize Snap HomePage',
);
});
});
56 changes: 56 additions & 0 deletions packages/starknet-snap/test/snap-provider.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { BIP44CoinTypeNode } from '@metamask/key-tree';
import { generateMnemonic } from 'bip39';
import { generateBip44Entropy } from './utils';

export type SnapProvider = {
registerRpcMessageHandler: (fn) => unknown;
request(options: {
method: string;
params?: { [key: string]: unknown } | unknown[];
}): unknown;
};

export const manageStateSpy = jest.fn();

export const dialogSpy = jest.fn();

export const requestSpy = jest.fn();

export class MockSnapProvider implements SnapProvider {
public readonly registerRpcMessageHandler = jest.fn();

/* eslint-disable */

async getBip44Entropy() {
return await generateBip44Entropy(generateMnemonic());
}

public readonly rpcSpys = {
snap_getBip32Entropy: jest.fn(),
snap_getBip44Entropy: this.getBip44Entropy,
snap_dialog: dialogSpy,
snap_manageState: manageStateSpy,
};
/* eslint-disable */

/**
* Calls requestSpy or this.rpcSpys[req.method], if the method has
* a dedicated spy.
* @param args
* @param args.method
* @param args.params
*/
public request(args: {
method: string;
params: { [key: string]: unknown } | unknown[];
}): unknown {
const { method, params } = args;
if (Object.hasOwnProperty.call(this.rpcSpys, method)) {
if (Array.isArray(params)) {
return this.rpcSpys[method](...params);
}
return this.rpcSpys[method](params);
}
return requestSpy(args);
}
}
Loading
Loading