@@ -2,6 +2,7 @@ import { describe, expect, it } from "@effect/vitest";
22import * as Cause from "effect/Cause" ;
33import * as Exit from "effect/Exit" ;
44import {
5+ frontendErrorCapturePayload ,
56 messageFromExit ,
67 messageFromUnknown ,
78 reportExitFailure ,
@@ -45,4 +46,80 @@ describe("frontend error reporting", () => {
4546 expect ( calls [ 0 ] ! . context . surface ) . toBe ( "sources" ) ;
4647 expect ( calls [ 0 ] ! . context . action ) . toBe ( "update" ) ;
4748 } ) ;
49+
50+ it ( "extracts the failed Error from an Effect Cause" , ( ) => {
51+ const original = new Error ( "GET /api/tools failed with 522" ) ;
52+ original . stack = "Error: GET /api/tools failed with 522\n at loadTools (tools.ts:12:3)" ;
53+ const payload = frontendErrorCapturePayload ( Cause . fail ( original ) ) ;
54+
55+ expect ( payload . exception ) . toBe ( original ) ;
56+ expect ( payload . exception . message ) . toBe ( "GET /api/tools failed with 522" ) ;
57+ expect ( payload . exception . stack ) . toContain ( "loadTools" ) ;
58+ expect ( payload . causePretty ) . toContain ( "GET /api/tools failed with 522" ) ;
59+ } ) ;
60+
61+ it ( "builds an Error from a string Effect failure" , ( ) => {
62+ const payload = frontendErrorCapturePayload (
63+ Cause . fail ( "GET /api/integrations failed with 522" ) ,
64+ ) ;
65+
66+ expect ( payload . exception ) . toBeInstanceOf ( Error ) ;
67+ expect ( payload . exception . message ) . toContain ( "GET /api/integrations failed with 522" ) ;
68+ expect ( payload . exception . stack ) . toContain ( "GET /api/integrations failed with 522" ) ;
69+ expect ( payload . causePretty ) . toContain ( "GET /api/integrations failed with 522" ) ;
70+ } ) ;
71+
72+ it ( "extracts the defect Error from an Effect Cause" , ( ) => {
73+ const defect = new TypeError ( "render crashed" ) ;
74+ defect . stack = "TypeError: render crashed\n at renderWidget (widget.tsx:40:5)" ;
75+ const payload = frontendErrorCapturePayload ( Cause . die ( defect ) ) ;
76+
77+ expect ( payload . exception ) . toBe ( defect ) ;
78+ expect ( payload . exception . message ) . toBe ( "render crashed" ) ;
79+ expect ( payload . exception . stack ) . toContain ( "renderWidget" ) ;
80+ expect ( payload . causePretty ) . toContain ( "render crashed" ) ;
81+ } ) ;
82+
83+ it ( "unwraps a Cause that arrives wrapped as a defect of another Cause" , ( ) => {
84+ // The production chain: runPromise squashes an exit whose defect is itself
85+ // a Cause, so the capture boundary receives Die(CauseImpl).
86+ const original = new Error ( "GET /api/connections failed with 522" ) ;
87+ original . stack =
88+ "Error: GET /api/connections failed with 522\n at loadConnections (connections.ts:9:2)" ;
89+ const payload = frontendErrorCapturePayload ( Cause . die ( Cause . fail ( original ) ) ) ;
90+
91+ expect ( payload . exception ) . toBeInstanceOf ( Error ) ;
92+ expect ( payload . exception . message ) . toContain ( "GET /api/connections failed with 522" ) ;
93+ expect ( payload . exception . message ) . not . toContain ( "CauseImpl" ) ;
94+ expect ( payload . causePretty ) . toContain ( "GET /api/connections failed with 522" ) ;
95+ } ) ;
96+
97+ it ( "unwraps a record carrying a Cause on its cause property" , ( ) => {
98+ const original = new Error ( "wrapped cause failure" ) ;
99+ const payload = frontendErrorCapturePayload ( { cause : Cause . fail ( original ) } ) ;
100+
101+ expect ( payload . exception ) . toBe ( original ) ;
102+ expect ( payload . causePretty ) . toContain ( "wrapped cause failure" ) ;
103+ } ) ;
104+
105+ it ( "passes a plain Error through unchanged" , ( ) => {
106+ const original = new Error ( "plain browser error" ) ;
107+ original . stack = "Error: plain browser error\n at onClick (button.tsx:7:1)" ;
108+ const payload = frontendErrorCapturePayload ( original ) ;
109+
110+ expect ( payload . exception ) . toBe ( original ) ;
111+ expect ( payload . exception . message ) . toBe ( "plain browser error" ) ;
112+ expect ( payload . exception . stack ) . toContain ( "onClick" ) ;
113+ expect ( payload . causePretty ) . toBeNull ( ) ;
114+ } ) ;
115+
116+ it ( "builds an Error from a non-Cause non-Error value" , ( ) => {
117+ const payload = frontendErrorCapturePayload ( { path : "/api/connections" , status : 522 } ) ;
118+
119+ expect ( payload . exception ) . toBeInstanceOf ( Error ) ;
120+ expect ( payload . exception . message ) . toContain ( "/api/connections" ) ;
121+ expect ( payload . exception . message ) . toContain ( "522" ) ;
122+ expect ( payload . exception . stack ) . toContain ( "Non-Error frontend exception" ) ;
123+ expect ( payload . causePretty ) . toBeNull ( ) ;
124+ } ) ;
48125} ) ;
0 commit comments