@@ -59,6 +59,11 @@ export default function HomeScreen() {
5959 component : 'HomeScreen' ,
6060 } ,
6161 } )
62+
63+ // Test that error reporting is working
64+ console . log (
65+ 'Testing error reporting with a message to verify console error handler' ,
66+ )
6267 } , [ ] )
6368
6469 const handleTestError = ( ) => {
@@ -280,6 +285,184 @@ export default function HomeScreen() {
280285 )
281286 }
282287
288+ // Exception test handlers
289+ const handleUncaughtException = ( ) => {
290+ // This will cause an uncaught exception that should be caught by the global handler
291+ setTimeout ( ( ) => {
292+ // Using setTimeout to ensure it's not caught by React's error boundary
293+ const obj : any = null
294+ obj . nonExistentMethod ( ) // Will throw TypeError: Cannot read property 'nonExistentMethod' of null
295+ } , 100 )
296+ }
297+
298+ const handleUnhandledPromiseRejection = ( ) => {
299+ // This creates an unhandled Promise rejection
300+ setTimeout ( ( ) => {
301+ // Using setTimeout to escape from the current execution context
302+ try {
303+ // Direct rejection using Promise.reject - this should be detected
304+ Promise . reject ( new Error ( 'Unhandled Promise Rejection Test' ) )
305+
306+ // Also try the constructor form
307+ new Promise ( ( resolve , reject ) => {
308+ // Explicit rejection without a catch handler
309+ reject ( new Error ( 'Unhandled Promise Constructor Rejection' ) )
310+ } )
311+
312+ // Also try throwing in a Promise - this is another common case
313+ new Promise ( ( ) => {
314+ throw new Error ( 'Error thrown inside Promise constructor' )
315+ } )
316+
317+ // Log to confirm execution
318+ console . log (
319+ 'Unhandled Promise rejection triggered - check for [LD-O11Y] logs' ,
320+ )
321+ } catch ( e ) {
322+ // This shouldn't catch the Promise rejections
323+ console . log (
324+ 'This catch should not execute for Promise rejections' ,
325+ e ,
326+ )
327+ }
328+ } , 0 )
329+ }
330+
331+ const handleRecursiveError = ( ) => {
332+ // This creates a stack overflow error
333+ // Wrap in setTimeout to bypass React's error boundaries
334+ setTimeout ( ( ) => {
335+ const recursiveFunction = ( depth : number = 0 ) : number => {
336+ // Add some protection to prevent actually crashing the app
337+ if ( depth > 5000 ) {
338+ throw new Error (
339+ 'Maximum recursion depth reached (stack overflow simulation)' ,
340+ )
341+ }
342+ return recursiveFunction ( depth + 1 )
343+ }
344+
345+ recursiveFunction ( )
346+ } , 0 )
347+ }
348+
349+ const handleAsyncError = async ( ) => {
350+ // This creates an error in an async function
351+ // Use setTimeout to ensure we're out of the current execution context
352+ setTimeout ( ( ) => {
353+ // Direct async/await error
354+ ; ( async ( ) => {
355+ try {
356+ // First approach: async function throwing directly
357+ const makeAsyncError = async ( ) => {
358+ await new Promise ( ( resolve ) => setTimeout ( resolve , 200 ) )
359+ console . log ( 'About to throw from async function...' )
360+ throw new Error ( 'Async operation failed' )
361+ }
362+
363+ // Run without awaiting or catching the error to ensure it becomes unhandled
364+ makeAsyncError ( )
365+
366+ // Second approach: rejected promise in async function
367+ const makeAsyncRejection = async ( ) => {
368+ await new Promise ( ( resolve ) => setTimeout ( resolve , 300 ) )
369+ console . log ( 'About to reject from async function...' )
370+ await Promise . reject (
371+ new Error ( 'Async promise rejection' ) ,
372+ )
373+ }
374+
375+ // Run without catching
376+ makeAsyncRejection ( )
377+
378+ // Third approach: await a throwing function
379+ const delayedThrow = ( ) =>
380+ new Promise ( ( _ , reject ) => {
381+ setTimeout ( ( ) => {
382+ console . log (
383+ 'About to reject from delayed function...' ,
384+ )
385+ reject (
386+ new Error (
387+ 'Delayed rejection in async context' ,
388+ ) ,
389+ )
390+ } , 400 )
391+ } )
392+
393+ // This would be caught if we awaited it, but we don't
394+ delayedThrow ( )
395+
396+ // Add a log to help track when the errors should occur
397+ console . log (
398+ 'Async errors scheduled - watch for [LD-O11Y] logs' ,
399+ )
400+ } catch ( e ) {
401+ // This shouldn't execute for the unhandled async errors
402+ console . log (
403+ 'This catch should not execute for async errors' ,
404+ e ,
405+ )
406+ }
407+ } ) ( )
408+ } , 0 )
409+ }
410+
411+ const handleNetworkError = ( ) => {
412+ // This creates a network error by attempting to fetch from a non-existent URL
413+ setTimeout ( ( ) => {
414+ console . log ( 'Triggering network error to non-existent domain' )
415+
416+ // Multiple network error approaches
417+
418+ // 1. Completely invalid URL - this will cause a synchronous error
419+ try {
420+ // This would normally be caught by React's error boundary, but we want to throw it directly
421+ fetch ( 'invalid://malformed-url' ) . catch ( ( error ) => {
422+ // We handle this one so the test can continue
423+ console . log ( 'Caught malformed URL error:' , error . message )
424+ LDObserve . recordError ( error , {
425+ source : 'network_test' ,
426+ type : 'malformed_url' ,
427+ } )
428+ } )
429+ } catch ( e ) {
430+ console . log ( 'Synchronous fetch error (malformed URL):' , e )
431+ }
432+
433+ // 2. Non-existent domain - this will cause a network error
434+ fetch ( 'https://non-existent-domain-123456789.xyz' ) . then (
435+ ( response ) => response . json ( ) ,
436+ )
437+ // Intentionally no catch handler to make it unhandled
438+
439+ // 3. Timeout error - this will cause a timeout
440+ const controller = new AbortController ( )
441+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , 100 )
442+
443+ fetch ( 'https://httpstat.us/200?sleep=5000' , {
444+ signal : controller . signal ,
445+ } ) . then ( ( response ) => {
446+ clearTimeout ( timeoutId )
447+ return response . json ( )
448+ } )
449+ // Intentionally no catch handler to make it unhandled
450+
451+ // 4. Server error - this should return a non-200 status code
452+ fetch ( 'https://httpstat.us/500' ) . then ( ( response ) => {
453+ if ( ! response . ok ) {
454+ throw new Error (
455+ `Server responded with status: ${ response . status } ` ,
456+ )
457+ }
458+ return response . json ( )
459+ } )
460+ // Intentionally no catch handler to make it unhandled
461+
462+ console . log ( 'Network errors triggered - watch for [LD-O11Y] logs' )
463+ } , 0 )
464+ }
465+
283466 return (
284467 < ParallaxScrollView
285468 headerBackgroundColor = { { light : '#A1CEDC' , dark : '#1D3D47' } }
@@ -344,6 +527,55 @@ export default function HomeScreen() {
344527 Test Span Hierarchy
345528 </ ThemedText >
346529 </ Pressable >
530+
531+ < ThemedText type = "subtitle" style = { { marginTop : 16 } } >
532+ Error Testing
533+ </ ThemedText >
534+
535+ < Pressable
536+ style = { [ styles . button , styles . errorButton ] }
537+ onPress = { handleUncaughtException }
538+ >
539+ < ThemedText style = { styles . buttonText } >
540+ Trigger Uncaught Exception
541+ </ ThemedText >
542+ </ Pressable >
543+
544+ < Pressable
545+ style = { [ styles . button , styles . errorButton ] }
546+ onPress = { handleUnhandledPromiseRejection }
547+ >
548+ < ThemedText style = { styles . buttonText } >
549+ Trigger Unhandled Promise Rejection
550+ </ ThemedText >
551+ </ Pressable >
552+
553+ < Pressable
554+ style = { [ styles . button , styles . errorButton ] }
555+ onPress = { handleRecursiveError }
556+ >
557+ < ThemedText style = { styles . buttonText } >
558+ Trigger Stack Overflow Error
559+ </ ThemedText >
560+ </ Pressable >
561+
562+ < Pressable
563+ style = { [ styles . button , styles . errorButton ] }
564+ onPress = { handleAsyncError }
565+ >
566+ < ThemedText style = { styles . buttonText } >
567+ Trigger Async Error
568+ </ ThemedText >
569+ </ Pressable >
570+
571+ < Pressable
572+ style = { [ styles . button , styles . errorButton ] }
573+ onPress = { handleNetworkError }
574+ >
575+ < ThemedText style = { styles . buttonText } >
576+ Trigger Network Error
577+ </ ThemedText >
578+ </ Pressable >
347579 </ ThemedView >
348580
349581 < ThemedView style = { styles . stepContainer } >
@@ -407,6 +639,9 @@ const styles = StyleSheet.create({
407639 marginVertical : 4 ,
408640 alignItems : 'center' ,
409641 } ,
642+ errorButton : {
643+ backgroundColor : '#FF3B30' , // Red color for error buttons
644+ } ,
410645 buttonText : {
411646 color : 'white' ,
412647 fontWeight : 'bold' ,
0 commit comments