UNPKG

@lukasrosario/account

Version:

Base Account SDK

441 lines (370 loc) 13.4 kB
import * as telemetryModule from ':core/telemetry/initCCA.js'; import { store } from ':store/store.js'; import * as checkCrossOriginModule from ':util/checkCrossOriginOpenerPolicy.js'; import * as validatePreferencesModule from ':util/validatePreferences.js'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { BaseAccountProvider } from './BaseAccountProvider.js'; import { CreateProviderOptions, createBaseAccountSDK } from './createBaseAccountSDK.js'; import * as getInjectedProviderModule from './getInjectedProvider.js'; // Mock all dependencies vi.mock(':store/store.js', () => ({ store: { subAccountsConfig: { set: vi.fn(), }, config: { set: vi.fn(), }, persist: { rehydrate: vi.fn(), }, }, })); vi.mock(':core/telemetry/initCCA.js', () => ({ loadTelemetryScript: vi.fn(), })); vi.mock(':util/checkCrossOriginOpenerPolicy.js', () => ({ checkCrossOriginOpenerPolicy: vi.fn(), })); vi.mock(':util/validatePreferences.js', () => ({ validatePreferences: vi.fn(), validateSubAccount: vi.fn(), })); vi.mock('./BaseAccountProvider.js', () => ({ BaseAccountProvider: vi.fn(), })); vi.mock('./getInjectedProvider.js', () => ({ getInjectedProvider: vi.fn(), })); const mockStore = store as any; const mockLoadTelemetryScript = telemetryModule.loadTelemetryScript as any; const mockCheckCrossOriginOpenerPolicy = checkCrossOriginModule.checkCrossOriginOpenerPolicy as any; const mockValidatePreferences = validatePreferencesModule.validatePreferences as any; const mockValidateSubAccount = validatePreferencesModule.validateSubAccount as any; const mockBaseAccountProvider = BaseAccountProvider as any; const mockGetInjectedProvider = getInjectedProviderModule.getInjectedProvider as any; describe('createProvider', () => { beforeEach(() => { vi.clearAllMocks(); mockBaseAccountProvider.mockReturnValue({ mockProvider: true, }); // Default: getInjectedProvider returns null to test BaseAccountProvider fallback mockGetInjectedProvider.mockReturnValue(null); }); describe('Basic functionality', () => { it('should create a provider with minimal parameters', () => { const result = createBaseAccountSDK({}).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith({ metadata: { appName: 'App', appLogoUrl: '', appChainIds: [], }, preference: {}, paymasterUrls: undefined, }); expect(result).toEqual({ mockProvider: true }); }); it('should create a provider with custom app metadata', () => { const params: CreateProviderOptions = { appName: 'Test App', appLogoUrl: 'https://example.com/logo.png', appChainIds: [1, 137], }; createBaseAccountSDK(params).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith({ metadata: { appName: 'Test App', appLogoUrl: 'https://example.com/logo.png', appChainIds: [1, 137], }, preference: {}, paymasterUrls: undefined, }); }); it('should create a provider with custom preference', () => { const params: CreateProviderOptions = { preference: { attribution: { auto: true }, }, }; createBaseAccountSDK(params).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith({ metadata: { appName: 'App', appLogoUrl: '', appChainIds: [], }, preference: { attribution: { auto: true }, }, paymasterUrls: undefined, }); }); it('should create a provider with paymaster URLs', () => { const params: CreateProviderOptions = { paymasterUrls: { 1: 'https://paymaster.example.com', 137: 'https://paymaster-polygon.example.com', }, }; createBaseAccountSDK(params).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith( expect.objectContaining({ paymasterUrls: { 1: 'https://paymaster.example.com', 137: 'https://paymaster-polygon.example.com', }, }) ); }); }); describe('Sub-account configuration', () => { it('should set sub-account configuration when provided', () => { const mockToOwnerAccount = vi.fn(); const params: CreateProviderOptions = { subAccounts: { toOwnerAccount: mockToOwnerAccount, // @ts-expect-error - enableAutoSubAccounts is not officially supported yet enableAutoSubAccounts: true, }, }; createBaseAccountSDK(params).getProvider(); expect(mockValidateSubAccount).toHaveBeenCalledWith(mockToOwnerAccount); expect(mockStore.subAccountsConfig.set).toHaveBeenCalledWith({ toOwnerAccount: mockToOwnerAccount, enableAutoSubAccounts: true, }); }); it('should handle partial sub-account configuration', () => { const params: CreateProviderOptions = { subAccounts: { // @ts-expect-error - enableAutoSubAccounts is not officially supported yet enableAutoSubAccounts: true, }, }; createBaseAccountSDK(params).getProvider(); expect(mockValidateSubAccount).not.toHaveBeenCalled(); expect(mockStore.subAccountsConfig.set).toHaveBeenCalledWith({ toOwnerAccount: undefined, enableAutoSubAccounts: true, }); }); it('should handle empty sub-account configuration', () => { const params: CreateProviderOptions = { subAccounts: undefined, }; createBaseAccountSDK(params).getProvider(); expect(mockStore.subAccountsConfig.set).toHaveBeenCalledWith({ toOwnerAccount: undefined, enableAutoSubAccounts: undefined, defaultSpendPermissions: undefined, }); }); }); describe('Store configuration', () => { it('should set store configuration', () => { const params: CreateProviderOptions = { appName: 'Test App', preference: {}, paymasterUrls: { 1: 'https://paymaster.example.com' }, }; createBaseAccountSDK(params).getProvider(); expect(mockStore.config.set).toHaveBeenCalledWith({ metadata: { appName: 'Test App', appLogoUrl: '', appChainIds: [], }, preference: {}, paymasterUrls: { 1: 'https://paymaster.example.com' }, }); }); it('should rehydrate store from storage', () => { createBaseAccountSDK({}).getProvider(); expect(mockStore.persist.rehydrate).toHaveBeenCalled(); }); }); describe('Validation', () => { it('should validate preferences', () => { const preference = { telemetry: true }; createBaseAccountSDK({ preference }).getProvider(); expect(mockValidatePreferences).toHaveBeenCalledWith(preference); }); it('should validate sub-account when toOwnerAccount is provided', () => { const mockToOwnerAccount = vi.fn(); createBaseAccountSDK({ subAccounts: { toOwnerAccount: mockToOwnerAccount }, }).getProvider(); expect(mockValidateSubAccount).toHaveBeenCalledWith(mockToOwnerAccount); }); it('should check cross-origin opener policy', () => { createBaseAccountSDK({}).getProvider(); expect(mockCheckCrossOriginOpenerPolicy).toHaveBeenCalled(); }); }); describe('Telemetry', () => { it('should load telemetry script when telemetry is not disabled', () => { createBaseAccountSDK({ preference: { telemetry: true }, }).getProvider(); expect(mockLoadTelemetryScript).toHaveBeenCalled(); }); it('should load telemetry script when telemetry is undefined (default)', () => { createBaseAccountSDK({ preference: {}, }).getProvider(); expect(mockLoadTelemetryScript).toHaveBeenCalled(); }); it('should not load telemetry script when telemetry is disabled', () => { createBaseAccountSDK({ preference: { telemetry: false }, }).getProvider(); expect(mockLoadTelemetryScript).not.toHaveBeenCalled(); }); }); describe('Error handling', () => { it('should handle sub-account validation errors', () => { mockValidateSubAccount.mockImplementationOnce(() => { throw new Error('Invalid sub-account function'); }); expect(() => { createBaseAccountSDK({ subAccounts: { toOwnerAccount: 'not-a-function' as any }, }).getProvider(); }).toThrow('Invalid sub-account function'); }); }); describe('Provider fallback behavior', () => { it('should use injected provider when getInjectedProvider returns a provider', () => { const mockInjectedProvider = { request: vi.fn(), isCoinbaseBrowser: true, type: 'injected', }; mockGetInjectedProvider.mockReturnValue(mockInjectedProvider); const result = createBaseAccountSDK({}).getProvider(); expect(mockGetInjectedProvider).toHaveBeenCalled(); expect(mockBaseAccountProvider).not.toHaveBeenCalled(); expect(result).toBe(mockInjectedProvider); }); it('should fallback to BaseAccountProvider when getInjectedProvider returns null', () => { mockGetInjectedProvider.mockReturnValue(null); const result = createBaseAccountSDK({}).getProvider(); expect(mockGetInjectedProvider).toHaveBeenCalled(); expect(mockBaseAccountProvider).toHaveBeenCalledWith({ metadata: { appName: 'App', appLogoUrl: '', appChainIds: [], }, preference: {}, paymasterUrls: undefined, }); expect(result).toEqual({ mockProvider: true }); }); }); describe('Integration', () => { it('should perform all setup steps in correct order', () => { const mockToOwnerAccount = vi.fn(); const params: CreateProviderOptions = { appName: 'Integration Test', appLogoUrl: 'https://example.com/logo.png', appChainIds: [1, 137], preference: { telemetry: true, }, subAccounts: { toOwnerAccount: mockToOwnerAccount, // @ts-expect-error - enableAutoSubAccounts is not officially supported yet enableAutoSubAccounts: true, }, paymasterUrls: { 1: 'https://paymaster.example.com', }, }; const result = createBaseAccountSDK(params).getProvider(); // Check sub-account validation and configuration expect(mockValidateSubAccount).toHaveBeenCalledWith(mockToOwnerAccount); expect(mockStore.subAccountsConfig.set).toHaveBeenCalledWith({ toOwnerAccount: mockToOwnerAccount, enableAutoSubAccounts: true, }); // Check store configuration expect(mockStore.config.set).toHaveBeenCalledWith({ metadata: { appName: 'Integration Test', appLogoUrl: 'https://example.com/logo.png', appChainIds: [1, 137], }, preference: { telemetry: true, }, paymasterUrls: { 1: 'https://paymaster.example.com', }, }); // Check store rehydration expect(mockStore.persist.rehydrate).toHaveBeenCalled(); // Check validation expect(mockCheckCrossOriginOpenerPolicy).toHaveBeenCalled(); expect(mockValidatePreferences).toHaveBeenCalledWith({ telemetry: true, }); // Check telemetry expect(mockLoadTelemetryScript).toHaveBeenCalled(); // Check provider creation expect(mockBaseAccountProvider).toHaveBeenCalledWith({ metadata: { appName: 'Integration Test', appLogoUrl: 'https://example.com/logo.png', appChainIds: [1, 137], }, preference: { telemetry: true, }, paymasterUrls: { 1: 'https://paymaster.example.com', }, }); expect(result).toEqual({ mockProvider: true }); }); }); describe('Edge cases', () => { it('should handle null app logo URL', () => { createBaseAccountSDK({ appLogoUrl: null }).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ appLogoUrl: '', }), }) ); }); it('should handle empty app chain IDs array', () => { createBaseAccountSDK({ appChainIds: [] }).getProvider(); expect(mockBaseAccountProvider).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ appChainIds: [], }), }) ); }); it('should handle complex nested preference objects', () => { const complexPreference = { attribution: { dataSuffix: '0x1234567890123456' as `0x${string}` }, telemetry: false, customProperty: 'custom value', }; createBaseAccountSDK({ preference: complexPreference }).getProvider(); expect(mockValidatePreferences).toHaveBeenCalledWith(complexPreference); expect(mockBaseAccountProvider).toHaveBeenCalledWith( expect.objectContaining({ preference: complexPreference, }) ); }); }); });