diff --git a/src/wallet.spec.ts b/src/wallet.spec.ts index e51ac5cc..75f9ef09 100644 --- a/src/wallet.spec.ts +++ b/src/wallet.spec.ts @@ -1,5 +1,5 @@ -import {beforeEach} from 'vitest'; import * as walletHandlers from './handlers/wallet.handlers'; +import {JSON_RPC_VERSION_2} from './types/rpc'; import {WALLET_WINDOW_CENTER, WALLET_WINDOW_TOP_RIGHT, windowFeatures} from './utils/window.utils'; import {Wallet, type WalletParameters} from './wallet'; @@ -25,67 +25,80 @@ describe('Wallet', () => { vi.restoreAllMocks(); }); - const options = [ - { - title: 'default options', - params: mockParameters, - expectedOptions: windowFeatures(WALLET_WINDOW_TOP_RIGHT) - }, - { - title: 'centered window', - params: { - ...mockParameters, - windowOptions: WALLET_WINDOW_CENTER - }, - expectedOptions: windowFeatures(WALLET_WINDOW_CENTER) - }, - { - title: 'custom window', - params: { - ...mockParameters, - windowOptions: 'height=600, width=400' - }, - expectedOptions: 'height=600, width=400' - } - ]; - - it.each(options)('$title', async ({params, expectedOptions}) => { - const addEventListenerSpy = vi.spyOn(window, 'addEventListener'); - const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); + describe('Connection errors', () => { + it('should throw connection timeout error', async () => { + vi.spyOn(walletHandlers, 'retryRequestStatus').mockResolvedValue('timeout'); - const promise = Wallet.connect(params); - - const messageEvent = new MessageEvent('message', { - origin: mockParameters.url + await expect(Wallet.connect(mockParameters)).rejects.toThrow( + 'Connection timeout. Unable to connect to the wallet.' + ); }); - window.dispatchEvent(messageEvent); - - const wallet = await promise; - - expect(wallet).toBeInstanceOf(Wallet); - - expect(window.open).toHaveBeenCalledWith(mockParameters.url, 'walletWindow', expectedOptions); - expect(window.open).toHaveBeenCalledTimes(1); - - expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function)); - expect(removeEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function)); - }); - - it('should throw connection timeout error', async () => { - vi.spyOn(walletHandlers, 'retryRequestStatus').mockResolvedValue('timeout'); + it('should assert edge case wallet not defined but request status success', async () => { + vi.spyOn(walletHandlers, 'retryRequestStatus').mockResolvedValue('ready'); - await expect(Wallet.connect(mockParameters)).rejects.toThrow( - 'Connection timeout. Unable to connect to the wallet.' - ); + await expect(Wallet.connect(mockParameters)).rejects.toThrow( + 'Unexpected error. Request status succeeded, but wallet is not defined.' + ); + }); }); - it('should assert edge case wallet not defined but request status success', async () => { - vi.spyOn(walletHandlers, 'retryRequestStatus').mockResolvedValue('ready'); - - await expect(Wallet.connect(mockParameters)).rejects.toThrow( - 'Unexpected error. Request status succeeded, but wallet is not defined.' - ); + describe('Connection success', () => { + const options = [ + { + title: 'default options', + params: mockParameters, + expectedOptions: windowFeatures(WALLET_WINDOW_TOP_RIGHT) + }, + { + title: 'centered window', + params: { + ...mockParameters, + windowOptions: WALLET_WINDOW_CENTER + }, + expectedOptions: windowFeatures(WALLET_WINDOW_CENTER) + }, + { + title: 'custom window', + params: { + ...mockParameters, + windowOptions: 'height=600, width=400' + }, + expectedOptions: 'height=600, width=400' + } + ]; + + it.each(options)('$title', async ({params, expectedOptions}) => { + const addEventListenerSpy = vi.spyOn(window, 'addEventListener'); + const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); + + const promise = Wallet.connect(params); + + const messageEvent = new MessageEvent('message', { + origin: mockParameters.url, + data: { + jsonrpc: JSON_RPC_VERSION_2, + id: '123', + result: 'ready' + } + }); + + window.dispatchEvent(messageEvent); + + const wallet = await promise; + + expect(wallet).toBeInstanceOf(Wallet); + + expect(window.open).toHaveBeenCalledWith( + mockParameters.url, + 'walletWindow', + expectedOptions + ); + expect(window.open).toHaveBeenCalledTimes(1); + + expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function)); + expect(removeEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function)); + }); }); }); diff --git a/src/wallet.ts b/src/wallet.ts index 016d1f84..805e765d 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -1,6 +1,7 @@ import {assertNonNullish, nonNullish} from '@dfinity/utils'; import {nanoid} from 'nanoid'; import {retryRequestStatus} from './handlers/wallet.handlers'; +import {IcrcReadyResponse} from './types/icrc-responses'; import { WALLET_WINDOW_TOP_RIGHT, windowFeatures, @@ -51,10 +52,13 @@ export class Wallet { let wallet: Wallet | undefined; - const onMessage = ({origin, data: _}: MessageEvent): void => { + const onMessage = ({origin, data: msgData}: MessageEvent): void => { // TODO: validate origin - // TODO: safeParse + validate ID - wallet = new Wallet({origin}); + + const {success: isWalletReady} = IcrcReadyResponse.safeParse(msgData); + if (isWalletReady) { + wallet = new Wallet({origin}); + } }; window.addEventListener('message', onMessage);