Skip to content

Commit 359f8d2

Browse files
committed
Fix stack trace view state consistency
1 parent 4ec8ef1 commit 359f8d2

File tree

4 files changed

+145
-11
lines changed

4 files changed

+145
-11
lines changed

static/app/components/stackTrace/issueStackTrace/index.spec.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,68 @@ describe('IssueStackTrace', () => {
407407
expect(firstIdx).toBeLessThan(secondIdx);
408408
});
409409

410+
it('copies unsymbolicated raw text for chained exceptions when minified view is active', async () => {
411+
Object.assign(navigator, {
412+
clipboard: {
413+
writeText: jest.fn().mockResolvedValue(''),
414+
},
415+
});
416+
417+
const {event, stacktrace} = makeStackTraceData();
418+
const symbolicatedStacktrace: StacktraceWithFrames = {
419+
...stacktrace,
420+
frames: stacktrace.frames.map((frame, index) => ({
421+
...frame,
422+
filename: `symbolicated/${index}.js`,
423+
})),
424+
};
425+
const rawStacktrace: StacktraceWithFrames = {
426+
...symbolicatedStacktrace,
427+
frames: symbolicatedStacktrace.frames.map((frame, index) => ({
428+
...frame,
429+
filename: `minified/${index}.js`,
430+
})),
431+
};
432+
433+
render(
434+
<IssueStackTrace
435+
event={event}
436+
values={[
437+
{
438+
type: 'RootError',
439+
value: 'root cause',
440+
module: 'app.main',
441+
mechanism: {handled: false, type: 'generic'},
442+
stacktrace: symbolicatedStacktrace,
443+
rawStacktrace,
444+
threadId: null,
445+
},
446+
{
447+
type: 'NestedError',
448+
value: 'nested cause',
449+
module: 'app.nested',
450+
mechanism: {handled: false, type: 'generic'},
451+
stacktrace: symbolicatedStacktrace,
452+
rawStacktrace,
453+
threadId: null,
454+
},
455+
]}
456+
/>
457+
);
458+
459+
await userEvent.click(screen.getByRole('button', {name: 'Display options'}));
460+
await userEvent.click(await screen.findByRole('option', {name: 'Unsymbolicated'}));
461+
462+
await userEvent.click(screen.getByRole('button', {name: 'Copy as'}));
463+
await userEvent.click(await screen.findByText('Text'));
464+
465+
await waitFor(() => expect(navigator.clipboard.writeText).toHaveBeenCalled());
466+
467+
const copiedText = (navigator.clipboard.writeText as jest.Mock).mock.calls[0][0];
468+
expect(copiedText).toContain('minified/0.js');
469+
expect(copiedText).not.toContain('symbolicated/0.js');
470+
});
471+
410472
describe('standalone stacktrace prop', () => {
411473
it('renders frame rows for a standalone stacktrace', async () => {
412474
const {event, stacktrace} = makeStackTraceData();

static/app/components/stackTrace/issueStackTrace/index.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ interface IndexedExceptionValue extends ExceptionValue {
7777
stacktrace: StacktraceType;
7878
}
7979

80+
function getDisplayedRawStacktrace(
81+
exc: IndexedExceptionValue,
82+
isMinified: boolean
83+
): StacktraceType {
84+
return isMinified ? (exc.rawStacktrace ?? exc.stacktrace) : exc.stacktrace;
85+
}
86+
8087
/** Resolves symbolicated vs raw (minified) exception fields. */
8188
function resolveExceptionFields(exc: IndexedExceptionValue, isMinified: boolean) {
8289
return {
@@ -251,8 +258,10 @@ function IssueStackTraceContent({
251258
exceptions
252259
.map(exc =>
253260
rawStacktraceContent({
254-
data: exc.stacktrace,
261+
data: getDisplayedRawStacktrace(exc, isMinified),
255262
platform: event.platform,
263+
exception: exc,
264+
isMinified,
256265
})
257266
)
258267
.join('\n\n')
@@ -268,9 +277,7 @@ function IssueStackTraceContent({
268277
{exceptions
269278
.map(exc =>
270279
rawStacktraceContent({
271-
data: isMinified
272-
? (exc.rawStacktrace ?? exc.stacktrace)
273-
: exc.stacktrace,
280+
data: getDisplayedRawStacktrace(exc, isMinified),
274281
platform: event.platform,
275282
exception: exc,
276283
isMinified,

static/app/components/stackTrace/stackTrace.spec.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,47 @@ describe('Core StackTrace', () => {
245245
expect(screen.getByRole('button', {name: 'Hide 1 frame'})).toBeInTheDocument();
246246
});
247247

248+
it('resets hidden frame toggles when switching stacktrace variants', async () => {
249+
const {event, stacktrace} = makeStackTraceData();
250+
const symbolicatedStacktrace: StacktraceWithFrames = {
251+
...stacktrace,
252+
frames: [
253+
{...stacktrace.frames[0]!, filename: 'symbolicated/hidden.js', inApp: false},
254+
{...stacktrace.frames[1]!, filename: 'symbolicated/visible.js', inApp: false},
255+
{...stacktrace.frames[2]!, filename: 'symbolicated/app.js', inApp: true},
256+
],
257+
};
258+
const minifiedStacktrace: StacktraceWithFrames = {
259+
...symbolicatedStacktrace,
260+
frames: [
261+
{...symbolicatedStacktrace.frames[0]!, filename: 'minified/hidden.js'},
262+
{...symbolicatedStacktrace.frames[1]!, filename: 'minified/visible.js'},
263+
{...symbolicatedStacktrace.frames[2]!, filename: 'minified/app.js'},
264+
],
265+
};
266+
267+
render(
268+
<TestStackTraceProvider
269+
event={event}
270+
stacktrace={symbolicatedStacktrace}
271+
minifiedStacktrace={minifiedStacktrace}
272+
>
273+
<DisplayOptions />
274+
<StackTraceFrames frameContextComponent={FrameContent} />
275+
</TestStackTraceProvider>
276+
);
277+
278+
await userEvent.click(screen.getByRole('button', {name: 'Show 1 more frame'}));
279+
expect(screen.getByText('symbolicated/hidden.js')).toBeInTheDocument();
280+
281+
await userEvent.click(screen.getByRole('button', {name: 'Display options'}));
282+
await userEvent.click(await screen.findByRole('option', {name: 'Unsymbolicated'}));
283+
284+
expect(screen.getByText('minified/hidden.js')).not.toBeVisible();
285+
expect(screen.queryByRole('button', {name: 'Hide 1 frame'})).not.toBeInTheDocument();
286+
expect(screen.getByRole('button', {name: 'Show 1 more frame'})).toBeInTheDocument();
287+
});
288+
248289
it('renders frame badges for in-app frames only', async () => {
249290
renderStackTrace();
250291

static/app/components/stackTrace/stackTraceProvider.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,23 @@ export function StackTraceProvider({
4545
);
4646
const lastFrameIndex = useMemo(() => getLastFrameIndex(frames), [frames]);
4747

48-
const [hiddenFrameToggleMap, setHiddenFrameToggleMap] = useState(() =>
49-
createInitialHiddenFrameToggleMap(frames, view === 'full')
48+
const shouldIncludeSystemFrames = view === 'full';
49+
const initialHiddenFrameToggleMap = useMemo(
50+
() => createInitialHiddenFrameToggleMap(frames, shouldIncludeSystemFrames),
51+
[frames, shouldIncludeSystemFrames]
5052
);
53+
const [hiddenFrameToggleState, setHiddenFrameToggleState] = useState(() => ({
54+
stacktrace: activeStacktrace,
55+
view,
56+
map: initialHiddenFrameToggleMap,
57+
}));
58+
const hiddenFrameToggleMap =
59+
hiddenFrameToggleState.stacktrace === activeStacktrace &&
60+
hiddenFrameToggleState.view === view
61+
? hiddenFrameToggleState.map
62+
: initialHiddenFrameToggleMap;
5163

5264
const platform = platformProp ?? getDefaultPlatform(activeStacktrace, event);
53-
const shouldIncludeSystemFrames = view === 'full';
5465

5566
const frameCountMap = useMemo(
5667
() => getFrameCountMap(frames, shouldIncludeSystemFrames),
@@ -129,10 +140,21 @@ export function StackTraceProvider({
129140
hiddenFrameToggleMap,
130141
lastFrameIndex,
131142
toggleHiddenFrames: (frameIndex: number) => {
132-
setHiddenFrameToggleMap(prevState => ({
133-
...prevState,
134-
[frameIndex]: !prevState[frameIndex],
135-
}));
143+
setHiddenFrameToggleState(prevState => {
144+
const currentMap =
145+
prevState.stacktrace === activeStacktrace && prevState.view === view
146+
? prevState.map
147+
: initialHiddenFrameToggleMap;
148+
149+
return {
150+
stacktrace: activeStacktrace,
151+
view,
152+
map: {
153+
...currentMap,
154+
[frameIndex]: !currentMap[frameIndex],
155+
},
156+
};
157+
});
136158
},
137159
}),
138160
[
@@ -144,12 +166,14 @@ export function StackTraceProvider({
144166
hasAnyExpandableFrames,
145167
hideSourceMapDebugger,
146168
hiddenFrameToggleMap,
169+
initialHiddenFrameToggleMap,
147170
lastFrameIndex,
148171
meta,
149172
platform,
150173
project,
151174
rows,
152175
activeStacktrace,
176+
view,
153177
]
154178
);
155179

0 commit comments

Comments
 (0)