UNPKG

@reown/appkit-scaffold-ui

Version:

The full stack toolkit to build onchain app UX.

390 lines • 20.9 kB
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { SafeLocalStorage, SafeLocalStorageKeys } from '@reown/appkit-common'; import { ChainController, OptionsController, RouterController, SnackController } from '@reown/appkit-controllers'; import { ReownAuthentication } from '@reown/appkit-controllers/features'; import { W3mDataCaptureView } from '../../src/views/w3m-data-capture-view/index.js'; import { HelpersUtil } from '../utils/HelpersUtil'; vi.mock('@reown/appkit-controllers', () => ({ ChainController: { getActiveCaipAddress: vi.fn(), getAccountData: vi.fn() }, OptionsController: { state: { metadata: { name: 'Test App' }, siwx: null, remoteFeatures: { emailCapture: false } } }, RouterController: { state: { data: null }, replace: vi.fn() }, SnackController: { showError: vi.fn() } })); vi.mock('@reown/appkit-common', () => ({ SafeLocalStorage: { getItem: vi.fn(), setItem: vi.fn() }, SafeLocalStorageKeys: { RECENT_EMAILS: '@appkit/recent_emails' }, ConstantsUtil: { FOUR_MINUTES_MS: 4 * 60 * 1000, CHAIN: { EVM: 'eip155' }, EIP155: 'eip155', CONNECTOR_ID: { COINBASE: 'coinbaseWallet', COINBASE_SDK: 'coinbaseWalletSDK' } } })); describe('W3mDataCaptureView', () => { let element; let mockSiwx; beforeEach(() => { vi.restoreAllMocks(); mockSiwx = new ReownAuthentication(); document.body.innerHTML = ''; vi.spyOn(ChainController, 'getAccountData').mockImplementation(() => { return { address: '0x1234567890abcdef1234567890abcdef12345678' }; }); vi.spyOn(ChainController, 'getActiveCaipAddress').mockReturnValue('eip155:1:0x1234567890abcdef1234567890abcdef12345678'); vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue(''); vi.spyOn(SafeLocalStorage, 'setItem').mockReturnValue(); OptionsController.state.siwx = mockSiwx; OptionsController.state.metadata = { name: 'Test App', description: 'Test Description', url: 'https://test.com', icons: [] }; OptionsController.state.remoteFeatures = { emailCapture: false }; if (!customElements.get('w3m-data-capture-view')) { customElements.define('w3m-data-capture-view', W3mDataCaptureView); } RouterController.state.data = undefined; }); afterEach(() => { vi.restoreAllMocks(); document.body.innerHTML = ''; }); const createView = async () => { const view = document.createElement('w3m-data-capture-view'); document.body.appendChild(view); await new Promise(resolve => setTimeout(resolve, 0)); await view.updateComplete; return view; }; describe('Initialization', () => { it('should throw error if ReownAuthentication is not initialized', async () => { OptionsController.state.siwx = undefined; await createView(); expect(SnackController.showError).toHaveBeenCalledWith('ReownAuthentication is not initialized. Please contact support.'); }); it('should initialize with correct default values', async () => { element = await createView(); expect(element).toBeTruthy(); const hero = HelpersUtil.querySelect(element, '.hero'); expect(hero).toBeTruthy(); }); it('should initialize with email from router data', async () => { RouterController.state.data = { email: 'test@example.com' }; element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); expect(emailInput?.value).toBe('test@example.com'); }); it('should initialize with email from user account', async () => { vi.spyOn(ChainController, 'getAccountData').mockImplementation(() => { return { address: '0x1234567890abcdef1234567890abcdef12345678', user: { email: 'user@test.com' } }; }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); expect(emailInput?.value).toBe('user@test.com'); }); it('should auto-submit if email is pre-filled', async () => { RouterController.state.data = { email: 'auto@example.com' }; mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: null }); element = await createView(); expect(mockSiwx.requestEmailOtp).toHaveBeenCalledWith({ email: 'auto@example.com', account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678' }); }); }); describe('Email Validation', () => { it('should show error for invalid email format', async () => { element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'invalid-email' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); expect(SnackController.showError).toHaveBeenCalledWith('Please provide a valid email.'); }); it('should accept valid email format', async () => { element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); expect(SnackController.showError).not.toHaveBeenCalledWith('Please provide a valid email.'); }); }); describe('Form Submission', () => { it('should show error and abort when siwx is not an instance of ReownAuthentication on submit', async () => { OptionsController.state.siwx = {}; const view = await createView(); const emailInput = HelpersUtil.querySelect(view, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await view.updateComplete; const spy = vi.spyOn(ReownAuthentication.prototype, 'requestEmailOtp'); const submitButton = HelpersUtil.querySelect(view, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 0)); expect(SnackController.showError).toHaveBeenCalledWith('ReownAuthentication is not initialized. Please contact support.'); expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); it('should handle successful OTP request with UUID', async () => { mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: 'test-uuid' }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 0)); expect(mockSiwx.requestEmailOtp).toHaveBeenCalledWith({ email: 'test@example.com', account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678' }); expect(RouterController.replace).toHaveBeenCalledWith('DataCaptureOtpConfirm', { email: 'test@example.com' }); }); it('should handle successful OTP request without UUID', async () => { mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: null }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 0)); expect(RouterController.replace).toHaveBeenCalledWith('SIWXSignMessage'); }); it('should handle OTP request failure', async () => { mockSiwx.requestEmailOtp = vi.fn().mockRejectedValue(new Error('Network error')); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 0)); expect(SnackController.showError).toHaveBeenCalledWith('Failed to send email OTP'); }); it('should show error when account is not connected', async () => { vi.spyOn(ChainController, 'getActiveCaipAddress').mockReturnValue(undefined); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); expect(submitButton?.disabled).toBe(false); }); }); describe('Recent Emails', () => { it('should load recent emails from storage', async () => { vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('email1@test.com,email2@test.com,email3@test.com'); element = await createView(); element.requestUpdate(); await element.updateComplete; const recentEmailsWidget = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidget).toBeTruthy(); expect(recentEmailsWidget?.emails).toEqual([ 'email1@test.com', 'email2@test.com', 'email3@test.com' ]); }); it('should handle email selection from recent emails', async () => { vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('recent@test.com'); mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: 'test-uuid' }); element = await createView(); element.recentEmails = element.getRecentEmails(); element.requestUpdate(); await element.updateComplete; const recentEmailsWidget = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidget).toBeTruthy(); recentEmailsWidget?.dispatchEvent(new CustomEvent('select', { detail: 'recent@test.com' })); await new Promise(resolve => setTimeout(resolve, 0)); expect(mockSiwx.requestEmailOtp).toHaveBeenCalledWith({ email: 'recent@test.com', account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678' }); }); it('should filter invalid emails from recent emails', async () => { vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('valid@test.com,invalid-email,another@test.com'); element = await createView(); element.recentEmails = element.getRecentEmails(); element.requestUpdate(); await element.updateComplete; const recentEmailsWidget = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidget).toBeTruthy(); expect(recentEmailsWidget?.emails).toEqual(['valid@test.com', 'another@test.com']); }); it('should limit recent emails to 3 items', async () => { vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('email1@test.com,email2@test.com,email3@test.com,email4@test.com,email5@test.com'); element = await createView(); element.recentEmails = element.getRecentEmails(); element.requestUpdate(); await element.updateComplete; const recentEmailsWidget = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidget).toBeTruthy(); expect(recentEmailsWidget?.emails).toEqual([ 'email1@test.com', 'email2@test.com', 'email3@test.com' ]); }); it('should save email to recent emails on successful submission', async () => { mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: 'test-uuid' }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'new@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 0)); expect(SafeLocalStorage.setItem).toHaveBeenCalledWith(SafeLocalStorageKeys.RECENT_EMAILS, expect.stringContaining('new@example.com')); }); }); describe('Email Suffixes Widget', () => { it('should render email suffixes widget', async () => { element = await createView(); const suffixesWidget = HelpersUtil.querySelect(element, 'w3m-email-suffixes-widget'); expect(suffixesWidget).toBeTruthy(); }); it('should handle email change from suffixes widget', async () => { element = await createView(); const suffixesWidget = HelpersUtil.querySelect(element, 'w3m-email-suffixes-widget'); suffixesWidget?.dispatchEvent(new CustomEvent('change', { detail: 'test@gmail.com' })); await element.updateComplete; const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); expect(emailInput?.value).toBe('test@gmail.com'); }); }); describe('Loading States', () => { it('should show loading state during form submission', async () => { mockSiwx.requestEmailOtp = vi .fn() .mockImplementation(() => new Promise((_, reject) => setTimeout(() => reject(new Error('Network error')), 100))); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 10)); await element.updateComplete; const hero = HelpersUtil.querySelect(element, '.hero'); expect(hero?.getAttribute('data-state')).toBe('loading'); await new Promise(resolve => setTimeout(resolve, 450)); expect(hero?.getAttribute('data-state')).toBe('default'); }); it('should hide email input during loading', async () => { mockSiwx.requestEmailOtp = vi .fn() .mockImplementation(() => new Promise((_, reject) => setTimeout(() => reject(new Error('Network error')), 100))); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 10)); await element.updateComplete; const emailInputDuringLoading = HelpersUtil.querySelect(element, 'wui-email-input'); expect(emailInputDuringLoading).toBeNull(); await new Promise(resolve => setTimeout(resolve, 450)); const emailInputAfterLoading = HelpersUtil.querySelect(element, 'wui-email-input'); expect(emailInputAfterLoading).toBeTruthy(); }); it('should hide recent emails widget during loading', async () => { vi.spyOn(SafeLocalStorage, 'getItem').mockReturnValue('test@example.com,user@domain.com,admin@company.org'); mockSiwx.requestEmailOtp = vi .fn() .mockImplementation(() => new Promise((_, reject) => setTimeout(() => reject(new Error('Network error')), 100))); element = await createView(); element.recentEmails = element.getRecentEmails(); element.requestUpdate(); await element.updateComplete; const recentEmailsWidgetInitial = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidgetInitial).toBeTruthy(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const submitButton = HelpersUtil.querySelect(element, 'wui-button[type="submit"]'); submitButton?.click(); await new Promise(resolve => setTimeout(resolve, 10)); await element.updateComplete; const recentEmailsWidgetDuringLoading = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidgetDuringLoading).toBeNull(); await new Promise(resolve => setTimeout(resolve, 450)); const recentEmailsWidgetAfterLoading = HelpersUtil.querySelect(element, 'w3m-recent-emails-widget'); expect(recentEmailsWidgetAfterLoading).toBeTruthy(); }); }); describe('Required Field Handling', () => { it('should handle required email capture feature', async () => { OptionsController.state.remoteFeatures = { emailCapture: ['required'] }; element = await createView(); }); it('should handle optional email capture feature', async () => { OptionsController.state.remoteFeatures = { emailCapture: ['optional'] }; element = await createView(); }); }); describe('Keyboard Navigation', () => { it('should submit form on Enter key press', async () => { mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: 'test-uuid' }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const keyEvent = new KeyboardEvent('keydown', { key: 'Enter' }); emailInput?.dispatchEvent(keyEvent); await new Promise(resolve => setTimeout(resolve, 0)); expect(mockSiwx.requestEmailOtp).toHaveBeenCalled(); }); it('should not submit form on other key presses', async () => { mockSiwx.requestEmailOtp = vi.fn().mockResolvedValue({ uuid: 'test-uuid' }); element = await createView(); const emailInput = HelpersUtil.querySelect(element, 'wui-email-input'); emailInput?.dispatchEvent(new CustomEvent('inputChange', { detail: 'test@example.com' })); await element.updateComplete; const tabEvent = new KeyboardEvent('keydown', { key: 'Tab' }); emailInput?.dispatchEvent(tabEvent); await new Promise(resolve => setTimeout(resolve, 0)); expect(mockSiwx.requestEmailOtp).not.toHaveBeenCalled(); }); }); }); //# sourceMappingURL=w3m-data-capture-view.test.js.map