| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- import type { Event as SentryEvent, StackFrame } from '@sentry/nextjs'
- import { describe, expect, it } from 'vitest'
- import {
- isBrowserWalletExtensionError,
- isCancellationRejection,
- isChallengeExpiredError,
- isUserAbortedOperation,
- } from './instrumentation-client'
- describe('Sentry beforeSend filtering functions', () => {
- describe('isBrowserWalletExtensionError', () => {
- it('returns true for Gate.io wallet extension error (gt-window-provider.js)', () => {
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'TypeError',
- value: 'en.shouldSetTallyForCurrentProvider is not a function',
- stacktrace: {
- frames: [
- { filename: 'app:///_next/static/chunks/main.js' } as StackFrame,
- { filename: 'app:///gt-window-provider.js' } as StackFrame,
- ],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(true)
- })
- it('returns true for Gate.io BTC wallet extension error (gt-window-provider-btc.js)', () => {
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'TypeError',
- value: 'f.shouldSetTallyForCurrentProvider is not a function',
- stacktrace: {
- frames: [{ filename: 'app:///gt-window-provider-btc.js' } as StackFrame],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(true)
- })
- it('returns true for wallet-provider in abs_path', () => {
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'Error',
- value: 'wallet error',
- stacktrace: {
- frames: [
- { abs_path: 'chrome-extension://abc123/wallet-provider.js' } as StackFrame,
- ],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(true)
- })
- it('returns false for regular application errors', () => {
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'Error',
- value: 'Regular error',
- stacktrace: {
- frames: [
- { filename: 'app:///_next/static/chunks/main.js' } as StackFrame,
- { filename: 'app:///_next/static/chunks/pages/index.js' } as StackFrame,
- ],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(false)
- })
- it('returns false for empty event', () => {
- const event: SentryEvent = {}
- expect(isBrowserWalletExtensionError(event)).toBe(false)
- })
- it('returns false when exception values are empty', () => {
- const event: SentryEvent = {
- exception: {
- values: [],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(false)
- })
- it('returns false when stacktrace frames are undefined', () => {
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'Error',
- value: 'Error without stacktrace',
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(false)
- })
- })
- describe('isUserAbortedOperation', () => {
- it('returns true for "operation was aborted" error', () => {
- const error = new Error('The operation was aborted.')
- const event: SentryEvent = {}
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('returns true for "signal is aborted" error', () => {
- const error = new Error('signal is aborted without reason')
- const event: SentryEvent = {}
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('returns true for "manually canceled" error', () => {
- const error = new Error('operation is manually canceled')
- const event: SentryEvent = {}
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('returns true for "AbortError" message', () => {
- const error = new Error('AbortError: The operation was aborted')
- const event: SentryEvent = {}
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('returns true when message is in event.message (no error object)', () => {
- const error = null
- const event: SentryEvent = {
- message: '[CRITICAL][sign in via EP] Failed: The operation was aborted.',
- }
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('returns true for event message with "signal is aborted"', () => {
- const event: SentryEvent = {
- message: '[CRITICAL][sign in via EP] Failed: signal is aborted without reason',
- }
- expect(isUserAbortedOperation(undefined, event)).toBe(true)
- })
- it('returns false for regular errors', () => {
- const error = new Error('Something went wrong')
- const event: SentryEvent = {}
- expect(isUserAbortedOperation(error, event)).toBe(false)
- })
- it('returns false for empty inputs', () => {
- expect(isUserAbortedOperation(null, {})).toBe(false)
- expect(isUserAbortedOperation(undefined, {})).toBe(false)
- })
- it('handles non-Error objects gracefully', () => {
- const error = { message: 'The operation was aborted.' }
- const event: SentryEvent = {}
- // Non-Error objects should not match since we check instanceof Error
- expect(isUserAbortedOperation(error, event)).toBe(false)
- })
- })
- describe('isCancellationRejection', () => {
- it('returns true for cancellation type in extra.__serialized__', () => {
- const event: SentryEvent = {
- extra: {
- __serialized__: {
- msg: 'operation is manually canceled',
- type: 'cancelation',
- },
- },
- }
- expect(isCancellationRejection(event)).toBe(true)
- })
- it('returns false when type is not cancelation', () => {
- const event: SentryEvent = {
- extra: {
- __serialized__: {
- msg: 'some error',
- type: 'error',
- },
- },
- }
- expect(isCancellationRejection(event)).toBe(false)
- })
- it('returns false when __serialized__ is undefined', () => {
- const event: SentryEvent = {
- extra: {},
- }
- expect(isCancellationRejection(event)).toBe(false)
- })
- it('returns false when extra is undefined', () => {
- const event: SentryEvent = {}
- expect(isCancellationRejection(event)).toBe(false)
- })
- it('returns false when __serialized__ has no type property', () => {
- const event: SentryEvent = {
- extra: {
- __serialized__: {
- msg: 'some message',
- },
- },
- }
- expect(isCancellationRejection(event)).toBe(false)
- })
- })
- describe('isChallengeExpiredError', () => {
- it('returns true for challenge-expired error message', () => {
- const error = new Error('Non-Error promise rejection captured with value: challenge-expired')
- const event: SentryEvent = {}
- expect(isChallengeExpiredError(error, event)).toBe(true)
- })
- it('returns true when challenge-expired is in event.message', () => {
- const event: SentryEvent = {
- message: 'challenge-expired',
- }
- expect(isChallengeExpiredError(null, event)).toBe(true)
- })
- it('returns false for regular errors', () => {
- const error = new Error('Something went wrong')
- const event: SentryEvent = {}
- expect(isChallengeExpiredError(error, event)).toBe(false)
- })
- it('returns false for empty inputs', () => {
- expect(isChallengeExpiredError(null, {})).toBe(false)
- expect(isChallengeExpiredError(undefined, {})).toBe(false)
- })
- it('returns false for similar but different messages', () => {
- const error = new Error('challenge expired') // No hyphen
- const event: SentryEvent = {}
- expect(isChallengeExpiredError(error, event)).toBe(false)
- })
- })
- describe('integration scenarios', () => {
- it('correctly identifies BRIVEN-APP-353 pattern (cancellation rejection)', () => {
- // Based on actual Sentry issue BRIVEN-APP-353
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'UnhandledRejection',
- value: 'Object captured as promise rejection with keys: msg, type',
- },
- ],
- },
- extra: {
- __serialized__: {
- msg: 'operation is manually canceled',
- type: 'cancelation',
- },
- },
- }
- expect(isCancellationRejection(event)).toBe(true)
- })
- it('correctly identifies BRIVEN-APP-AFC pattern (wallet extension)', () => {
- // Based on actual Sentry issue BRIVEN-APP-AFC
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'TypeError',
- value: 'f.shouldSetTallyForCurrentProvider is not a function',
- stacktrace: {
- frames: [
- {
- filename:
- 'node_modules/.pnpm/@sentry+browser@10.27.0/node_modules/@sentry/browser/src/helpers.ts',
- function: 'n',
- } as StackFrame,
- {
- filename: 'app:///gt-window-provider-btc.js',
- function: 'GateWindowProvider.internalListener',
- } as StackFrame,
- ],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(true)
- })
- it('correctly identifies BRIVEN-APP-92A pattern (wallet extension)', () => {
- // Based on actual Sentry issue BRIVEN-APP-92A
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'TypeError',
- value: 'en.shouldSetTallyForCurrentProvider is not a function',
- stacktrace: {
- frames: [
- {
- filename:
- 'node_modules/.pnpm/@sentry+browser@10.27.0/node_modules/@sentry/browser/src/helpers.ts',
- function: 'n',
- } as StackFrame,
- {
- filename: 'app:///gt-window-provider.js',
- function: 'GateWindowProvider.internalListener',
- } as StackFrame,
- ],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(true)
- })
- it('correctly identifies BRIVEN-APP-BG6 pattern (user aborted)', () => {
- // Based on actual Sentry issue BRIVEN-APP-BG6
- const error = new Error('The operation was aborted.')
- const event: SentryEvent = {
- message: '[CRITICAL][sign in via EP] Failed: The operation was aborted.',
- }
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('correctly identifies BRIVEN-APP-BG7 pattern (signal aborted)', () => {
- // Based on actual Sentry issue BRIVEN-APP-BG7
- const error = new Error('signal is aborted without reason')
- const event: SentryEvent = {
- message: '[CRITICAL][sign in via EP] Failed: signal is aborted without reason',
- }
- expect(isUserAbortedOperation(error, event)).toBe(true)
- })
- it('correctly identifies BRIVEN-APP-ACC pattern (challenge expired)', () => {
- // Based on actual Sentry issue BRIVEN-APP-ACC
- const error = new Error('Non-Error promise rejection captured with value: challenge-expired')
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'UnhandledRejection',
- value: 'Non-Error promise rejection captured with value: challenge-expired',
- },
- ],
- },
- }
- expect(isChallengeExpiredError(error, event)).toBe(true)
- })
- it('does not filter legitimate errors', () => {
- const error = new Error('Cannot read property "foo" of undefined')
- const event: SentryEvent = {
- exception: {
- values: [
- {
- type: 'TypeError',
- value: 'Cannot read property "foo" of undefined',
- stacktrace: {
- frames: [{ filename: 'app:///_next/static/chunks/pages/index.js' } as StackFrame],
- },
- },
- ],
- },
- }
- expect(isBrowserWalletExtensionError(event)).toBe(false)
- expect(isUserAbortedOperation(error, event)).toBe(false)
- expect(isCancellationRejection(event)).toBe(false)
- expect(isChallengeExpiredError(error, event)).toBe(false)
- })
- })
- })
|