import { describe, it, expect } from 'vitest' import { WalletConnectorError } from '@meshconnect/uwc-types' import { parseError, extractTonSdkErrorMessage } from './error-utils' // Capture the thrown error explicitly — parseError is `never`, and a bare // try/catch would pass vacuously if it ever stopped throwing. const thrownBy = (input: unknown): WalletConnectorError => { try { parseError(input) } catch (err) { return err as WalletConnectorError } throw new Error('parseError did not throw') } describe('parseError (injected)', () => { it('passes an already-typed WalletConnectorError through unchanged', () => { const original = new WalletConnectorError({ type: 'expired', message: 'gone', code: 4900 }) expect(thrownBy(original)).toBe(original) }) it('marks 4001 / ACTION_REJECTED as rejected', () => { expect(thrownBy({ code: 4001, message: 'nope' }).type).toBe('rejected') expect(thrownBy({ code: 'ACTION_REJECTED', message: 'nope' }).type).toBe( 'rejected' ) }) it('detects a rejection from the message when no code is present', () => { expect(thrownBy(new Error('User denied the request')).type).toBe('rejected') }) it('falls back to unknown for an unrecognized error', () => { const err = thrownBy(new Error('Something odd happened')) expect(err.type).toBe('unknown') expect(err.message).toBe('Something odd happened') }) it('extracts a nested error.message', () => { expect(thrownBy({ error: { message: 'inner boom' } }).message).toBe( 'inner boom' ) }) describe('numeric code preservation (MFS-667)', () => { it('preserves the -32002 "already pending" code so the classifier can place it', () => { const err = thrownBy({ code: -32002, message: "Request of type 'wallet_requestPermissions' already pending" }) expect(err.code).toBe(-32002) expect(err.type).toBe('unknown') }) it('preserves the code alongside a rejection type', () => { expect(thrownBy({ code: 4001, message: 'User rejected' }).code).toBe(4001) }) it('leaves code undefined for a string code (numeric-only slot)', () => { expect( thrownBy({ code: 'SERVER_ERROR', message: 'x' }).code ).toBeUndefined() }) it('leaves code undefined when the source error carries no code', () => { expect(thrownBy(new Error('No accounts returned')).code).toBeUndefined() }) it('leaves code undefined for a non-integer numeric code (NaN/Infinity)', () => { expect(thrownBy({ code: NaN, message: 'x' }).code).toBeUndefined() expect(thrownBy({ code: Infinity, message: 'x' }).code).toBeUndefined() }) }) }) describe('extractTonSdkErrorMessage', () => { it('returns the last line of a multiline TON Connect SDK error', () => { expect( extractTonSdkErrorMessage( new Error('[TON_CONNECT_SDK_ERROR] BadRequest\nActual message') ) ).toBe('Actual message') }) it('returns the message unchanged when single-line', () => { expect(extractTonSdkErrorMessage(new Error('plain'))).toBe('plain') }) it('returns empty string for a value with no message', () => { expect(extractTonSdkErrorMessage(42)).toBe('') }) })