Unified · Split
| @@ -21,6 +21,10 @@ import { revisit, visit } from "../src/surfaces/browser"; · 20 unmodified lines | ||
| 21 | 21 | type ReportedException = { |
| 22 | 22 | readonly type?: string; |
| 23 | 23 | readonly value?: string; |
| 24 | + // The reporter marks an exception `synthetic` when it was handed something | |
| 25 | + // that was not a real error and had to invent a stack for it — the stack of | |
| 26 | + // whatever frame did the reporting. | |
| 27 | + readonly mechanism?: { readonly synthetic?: boolean }; | |
| 24 | 28 | }; |
| 25 | 29 | |
| 26 | 30 | type ReportedEvent = { |
| @@ -110,6 +114,15 @@ scenario( · 83 unmodified lines | ||
| 110 | 114 | expect(failure.value ?? "", "no report is a bag of keys").not.toMatch( |
| 111 | 115 | /captured as exception with keys/, |
| 112 | 116 | ); |
| 117 | + // The other half of the bug, and the half a readable message can hide: | |
| 118 | + // handed a non-error, the reporter still has no stack of its own to | |
| 119 | + // group on and invents one from the reporting frame, so unrelated | |
| 120 | + // failures keep merging into a single bucket. Only a real error clears | |
| 121 | + // this flag. | |
| 122 | + expect( | |
| 123 | + failure.mechanism?.synthetic ?? false, | |
| 124 | + "the report carries the failure's own stack, not the reporter's frame", | |
| 125 | + ).toBe(false); | |
| 113 | 126 | } |
| 114 | 127 | |
| 115 | 128 | await page.unroute("**/api/integrations"); |