Skip to content

Commit

Permalink
Internal rewrite (#288)
Browse files Browse the repository at this point in the history
* All Connectors emit Disconnect Event

* Connectors emit connected events with params

* make emit protected, ONLY callable by connectors, also fix cew test

* switch address change to be emited event

* WIP

* replace loadConfig with resurector + loadPreviusState
Move celo provider helpers to their own files.
add listeners

* remove persists

* fix connector stub

* end reducer effecting local storage

* fix tests, no local storage

* connector listerns must be listening BEFORE initialise is called

* remove clearPreviousConfig calls from connectors

* split up client settup from fetching URI. allow getURI to be called multiple times without creating new clients

* expend test for wallet connect

* missed extra import

* WIP better netowkr changers

* a little cleanup

* remove unused code

* fix tests

* tests pass

* wip Wallet connect connector connecting steps, changing networks, disconnecting, and inbetween steps

* improve wc, handle CBW disconnect, remove account

* update todos

* fix lint errors

* fix ts build

* cleanup

* replace console.xxx with our custom logger

* self review

* clear up verbose test runs

* lint up

* clean up tests. remove weird exit code

* revert

* add second test condition

* more self review

* review (@nicolasbrugneaux) rename destroy to disconnect for linguistic symmetry with connect action

* a more reasonable time

* unmount in the tests

* mocking the balance might be why there is a balance

Co-authored-by: Aaron DeRuvo <aaron.deruvo@clabs.co>
  • Loading branch information
aaronmgdr and aaronmgdr committed Jul 28, 2022
1 parent 2aaeaaf commit a4ee834
Show file tree
Hide file tree
Showing 55 changed files with 1,736 additions and 1,128 deletions.
6 changes: 6 additions & 0 deletions packages/example/components/mock-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const mockLogger = {
log: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export async function assertHasBalance(

throw new Error(`Got an error when trying to check balance: ${message}`);
}

if (convertedBalance < 0.1) {
throw new Error(
'Your wallet does not have enough funds for the transaction'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { fireEvent, render, waitFor } from '@testing-library/react';
import { generateTestingUtils } from 'eth-testing';
import { TestingUtils } from 'eth-testing/lib/testing-utils';

import { mockLogger } from '../mock-logger';
import { ConnectWalletCheck } from './connect-wallet';

declare global {
interface Window {
ethereum: ReturnType<TestingUtils['getProvider']> & { send?: () => void };
}
}

describe('ConnectWalletCheck', () => {
const testingUtils = generateTestingUtils({ providerType: 'MetaMask' });

Expand Down Expand Up @@ -48,6 +48,7 @@ describe('ConnectWalletCheck', () => {
connectModal={{
providersOptions: { searchable: true },
}}
logger={mockLogger}
>
<ConnectWalletCheck />
</CeloProvider>
Expand Down
19 changes: 10 additions & 9 deletions packages/example/components/test-plan/perform-action.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { CeloProvider, localStorageKeys, Mainnet } from '@celo/react-celo';
import { render, waitFor } from '@testing-library/react';
import { generateTestingUtils } from 'eth-testing';
import { TestingUtils } from 'eth-testing/lib/testing-utils';
import Web3 from 'web3';

import { mockLogger } from '../mock-logger';
import { SendTransaction } from './perform-actions';

declare global {
Expand Down Expand Up @@ -39,14 +39,6 @@ describe('SendTransaction', () => {
localStorage.setItem(localStorageKeys.lastUsedAddress, mockAddress);
localStorage.setItem(localStorageKeys.lastUsedWalletType, 'MetaMask');

// * TODO: Figure out how to mock balance.
/**
* This balance mock is not actually affecting the balance in the wallet.
*/
testingUtils.mockBalance(
mockAddress,
Web3.utils.toWei('1000000000000000000')
);
/**
* As far as I can tell, the balance is retrieved with an `eth_call` request,
* which is sent using this function. So this might be a good point to investigate
Expand All @@ -73,13 +65,22 @@ describe('SendTransaction', () => {
connectModal={{
providersOptions: { searchable: true },
}}
logger={mockLogger}
>
<SendTransaction />
</CeloProvider>
);

expect(screen.getByRole('status')).toHaveTextContent('not started');

await waitFor(() => {
expect(
screen.getByText(
'This sends a very small transaction to impact market contract.'
)
).toBeVisible();
});

await waitFor(() =>
expect(
screen.getByText(
Expand Down
6 changes: 3 additions & 3 deletions packages/example/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function HomePage(): React.ReactElement {
network,
connect,
supportsFeeCurrency,
destroy,
disconnect,
performActions,
walletType,
feeCurrency,
Expand Down Expand Up @@ -183,7 +183,7 @@ function HomePage(): React.ReactElement {
</h1>

{address ? (
<PrimaryButton onClick={destroy}>Disconnect</PrimaryButton>
<PrimaryButton onClick={disconnect}>Disconnect</PrimaryButton>
) : (
<PrimaryButton
onClick={() =>
Expand Down Expand Up @@ -357,7 +357,7 @@ export default function Home(): React.ReactElement {
url: 'https://react-celo.vercel.app',
icon: 'https://react-celo.vercel.app/favicon.ico',
}}
network={Alfajores}
defaultNetwork={Alfajores.name}
connectModal={{
providersOptions: { searchable: true },
}}
Expand Down
4 changes: 3 additions & 1 deletion packages/example/pages/wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ export default function Wallet(): React.ReactElement {
accept: approveConnection,
reject: rejectConnection,
meta: {
title: `new connection from dApp ${payload.params[0].peerMeta.name}`,
title: `new connection from dApp ${
payload?.params[0]?.peerMeta?.name || ''
}`,
raw: payload,
},
});
Expand Down
108 changes: 108 additions & 0 deletions packages/react-celo/__tests__/connectors/celo-extension-wallet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { CeloContract } from '@celo/contractkit/lib/base';
import { generateTestingUtils } from 'eth-testing';

import {
CeloExtensionWalletConnector,
ConnectorEvents,
} from '../../src/connectors';
import { Alfajores, Baklava, WalletTypes } from '../../src/constants';
import { setApplicationLogger } from '../../src/utils/logger';
import { mockLogger } from '../test-logger';

const ACCOUNT = '0xf61B443A155b07D2b2cAeA2d99715dC84E839EEf';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const CEW_CALLBACKS = new Map<string, (args: any) => void>();

describe('CeloExtensionWalletConnector', () => {
const testingUtils = generateTestingUtils({
providerType: 'MetaMask',
verbose: false,
});

const provider = testingUtils.getProvider();

beforeAll(() => {
// Manually inject the mocked provider in the window as MetaMask does
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
(global.window.celo = {
...provider,
send: (params, cb) =>
cb(null, {
jsonrpc: params.jsonrpc,
id: Number(params.id),
result: [ACCOUNT],
}),
enable: () => Promise.resolve(undefined),
publicConfigStore: {
on: (e: string, cb: (args: { networkVersion: number }) => void) => {
CEW_CALLBACKS.set(e, cb);
},
},
}),
testingUtils.mockConnectedWallet([ACCOUNT], {
chainId: Alfajores.chainId,
});
testingUtils.mockAccounts([ACCOUNT]);
testingUtils.mockRequestAccounts([ACCOUNT]);
setApplicationLogger(mockLogger);
});
let connector: CeloExtensionWalletConnector;
const onConnect = jest.fn();
const onDisconnect = jest.fn();
const onChangeAddress = jest.fn();
const onChangeNetwork = jest.fn();
beforeEach(() => {
connector = new CeloExtensionWalletConnector(
Alfajores,
CeloContract.GoldToken
);
connector.on(ConnectorEvents.CONNECTED, onConnect);
connector.on(ConnectorEvents.DISCONNECTED, onDisconnect);
connector.on(ConnectorEvents.ADDRESS_CHANGED, onChangeAddress);
connector.on(ConnectorEvents.NETWORK_CHANGED, onChangeNetwork);
});

afterEach(() => {
// Clear all mocks between tests
testingUtils.clearAllMocks();
});
describe('initialise()', () => {
it('emits CONNECTED with network, address, walletType', async () => {
await connector.initialise();
expect(connector.initialised).toBe(true);
expect(onConnect).toBeCalledWith({
networkName: Alfajores.name,
address: ACCOUNT,
walletType: WalletTypes.CeloExtensionWallet,
});
});
});

describe('startNetworkChangeFromApp()', () => {
it('throws since CEW doesnt support that', () => {
expect(() => connector.startNetworkChangeFromApp()).toThrowError();
});
});

describe('continueNetworkUpdateFromWallet()', () => {
it('emits NETWORK_CHANGED EVENT', () => {
connector.continueNetworkUpdateFromWallet(Baklava);
expect(onChangeNetwork).toBeCalledWith(Baklava.name);
});

it('creates a new kit', () => {
const originalKit = connector.kit;
connector.continueNetworkUpdateFromWallet(Baklava);
expect(connector.kit).not.toBe(originalKit);
});
});

describe('close()', () => {
it('emits DISCONNECTED event', () => {
connector.close();
expect(onDisconnect).toHaveBeenCalled();
});
});
});
99 changes: 68 additions & 31 deletions packages/react-celo/__tests__/connectors/coinbase-wallet.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CeloContract } from '@celo/contractkit/lib/base';
import crypto from 'crypto';
import { generateTestingUtils } from 'eth-testing';

import { CoinbaseWalletConnector } from '../../src/connectors';
import { Alfajores, Baklava, localStorageKeys } from '../../src/constants';
import { getTypedStorageKey } from '../../src/utils/local-storage';
import { CoinbaseWalletConnector, ConnectorEvents } from '../../src/connectors';
import { Alfajores, Baklava } from '../../src/constants';
import { setApplicationLogger } from '../../src/utils/logger';
import { mockLogger } from '../test-logger';

const ACCOUNT = '0xf61B443A155b07D2b2cAeA2d99715dC84E839EEf';

Expand All @@ -30,56 +30,93 @@ describe('CoinbaseWalletConnector', () => {
getRandomValues: (arr: number[]) => crypto.randomBytes(arr.length),
},
});

testingUtils.mockNotConnectedWallet();
testingUtils.mockAccounts([ACCOUNT]);
setApplicationLogger(mockLogger);
testingUtils.mockConnectedWallet([ACCOUNT], {
chainId: `0x${Alfajores.chainId.toString(16)}`,
});
testingUtils.mockRequestAccounts([ACCOUNT]);
testingUtils.lowLevel.mockRequest('wallet_switchEthereumChain', {});
testingUtils.lowLevel.mockRequest('wallet_switchEthereumChain', {
chainId: `0x${Alfajores.chainId.toString(16)}`,
});
});

afterEach(() => {
// Clear all mocks between tests
testingUtils.clearAllMocks();
});

const dapp = { name: 'CB', icon: 'wallet.png' };

const onConnect = jest.fn();

it('initialises', async () => {
const connector = new CoinbaseWalletConnector(
Alfajores,
CeloContract.GoldToken
);
const connector = new CoinbaseWalletConnector(Alfajores, dapp);
connector.on(ConnectorEvents.CONNECTED, onConnect);
await connector.initialise();
expect(connector.account).toEqual(ACCOUNT);
expect(connector.initialised).toBe(true);
expect(onConnect).toHaveBeenCalledWith({
address: '0xf61B443A155b07D2b2cAeA2d99715dC84E839EEf',
networkName: 'Alfajores',
walletType: 'CoinbaseWallet',
});
});

describe('when network change', () => {
let connector: CoinbaseWalletConnector;
beforeEach(() => {
testingUtils.mockConnectedWallet([ACCOUNT]);
connector = new CoinbaseWalletConnector(
Alfajores,
CeloContract.GoldToken
);
});
const onChangeNetwork = jest.fn();
testingUtils.mockChainId(`0x${Baklava.chainId.toString(16)}`);

it('sets network in local storage and in connection', async () => {
await connector.updateKitWithNetwork(Baklava);
expect(getTypedStorageKey(localStorageKeys.lastUsedNetwork)).toEqual(
Baklava.name
);
beforeEach(() => {
testingUtils.mockConnectedWallet([ACCOUNT], {
chainId: `0x${Alfajores.chainId.toString(16)}`,
});
connector = new CoinbaseWalletConnector(Alfajores, dapp);
connector.on(ConnectorEvents.NETWORK_CHANGED, onChangeNetwork);
});

const callback = jest.fn();

it('reacts to network being changed from CoinbaseWallet side', async () => {
connector.onNetworkChange(callback);
describe('continueNetworkUpdateFromWallet()', () => {
it('emits NETWORK_CHANGED EVENT', () => {
connector.continueNetworkUpdateFromWallet(Baklava);
expect(onChangeNetwork).toBeCalledWith(Baklava.name);
});

it('creates a new kit', () => {
const originalKit = connector.kit;
connector.continueNetworkUpdateFromWallet(Baklava);
expect(connector.kit).not.toBe(originalKit);
});
});
});
describe('when wallet changes address', () => {
const onAddressChange = jest.fn();
let connector: CoinbaseWalletConnector;
beforeEach(async () => {
testingUtils.mockConnectedWallet([ACCOUNT], {
chainId: `0x${Alfajores.chainId.toString(16)}`,
});
connector = new CoinbaseWalletConnector(Alfajores, dapp);
connector.on(ConnectorEvents.ADDRESS_CHANGED, onAddressChange);
// Seems to only work when init is called after the callback is set
await connector.initialise();
});
it('emits ADDRESS_CHANGED', () => {
const newAddress = '0x11eed0F399d76Fe419FAf19a80ae7a52DE948D76';
testingUtils.mockAccountsChanged([newAddress]);
expect(onAddressChange).toBeCalledWith(newAddress);
});
});

testingUtils.mockChainChanged('0x1');

expect(callback).toHaveBeenLastCalledWith(1);
describe('close()', () => {
const onDisconnect = jest.fn();
let connector: CoinbaseWalletConnector;
beforeEach(() => {
connector = new CoinbaseWalletConnector(Alfajores, dapp);
connector.on(ConnectorEvents.DISCONNECTED, onDisconnect);
});
it('emits DISCONNECTED event', () => {
connector.close();
expect(onDisconnect).toBeCalled();
});
});
});
Loading

0 comments on commit a4ee834

Please sign in to comment.