Skip to content

Commit 8fba3d0

Browse files
feat(projects): Show App Hang texts for Apple (#87262)
Replace ANR titles and descriptions with app-hang-specific wording, including the issue search. Fixes GH-86666
1 parent 2f2caed commit 8fba3d0

File tree

8 files changed

+98
-15
lines changed

8 files changed

+98
-15
lines changed

static/app/views/projectDetail/projectCharts.spec.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('ProjectDetail > ProjectCharts', () => {
8787
screen.getByRole('button', {name: 'Display Crash Free Sessions'})
8888
);
8989

90-
expect(screen.getByText('ANR Rate')).toBeInTheDocument();
90+
expect(screen.getByText('App Hang Rate')).toBeInTheDocument();
9191
expect(screen.queryByText('Foreground ANR Rate')).not.toBeInTheDocument();
9292
});
9393

@@ -98,7 +98,7 @@ describe('ProjectDetail > ProjectCharts', () => {
9898
screen.getByRole('button', {name: 'Display Crash Free Sessions'})
9999
);
100100

101-
expect(screen.getByText('ANR Rate')).toBeInTheDocument();
101+
expect(screen.getByText('App Hang Rate')).toBeInTheDocument();
102102
expect(screen.queryByText('Foreground ANR Rate')).not.toBeInTheDocument();
103103
});
104104

@@ -109,7 +109,7 @@ describe('ProjectDetail > ProjectCharts', () => {
109109
screen.getByRole('button', {name: 'Display Crash Free Sessions'})
110110
);
111111

112-
expect(screen.queryByText('ANR Rate')).not.toBeInTheDocument();
112+
expect(screen.queryByText('App Hang Rate')).not.toBeInTheDocument();
113113
expect(screen.queryByText('Foreground ANR Rate')).not.toBeInTheDocument();
114114
});
115115

static/app/views/projectDetail/projectCharts.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {decodeScalar} from 'sentry/utils/queryString';
3838
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
3939
import withApi from 'sentry/utils/withApi';
4040
import {
41+
getANRRateText,
4142
isPlatformANRCompatible,
4243
isPlatformForegroundANRCompatible,
4344
} from 'sentry/views/projectDetail/utils';
@@ -225,7 +226,7 @@ class ProjectCharts extends Component<Props, State> {
225226
const anrRateOptions = [
226227
{
227228
value: DisplayModes.ANR_RATE,
228-
label: t('ANR Rate'),
229+
label: getANRRateText(project?.platform),
229230
disabled:
230231
this.otherActiveDisplayModes.includes(DisplayModes.ANR_RATE) || !hasSessions,
231232
tooltip: hasSessions ? undefined : noHealthTooltip,
@@ -449,8 +450,11 @@ class ProjectCharts extends Component<Props, State> {
449450
)}
450451
{hasAnrRateFeature && displayMode === DisplayModes.ANR_RATE && (
451452
<ProjectBaseSessionsChart
452-
title={t('ANR Rate')}
453-
help={getSessionTermDescription(SessionTerm.ANR_RATE, null)}
453+
title={getANRRateText(project?.platform)}
454+
help={getSessionTermDescription(
455+
SessionTerm.ANR_RATE,
456+
project?.platform || null
457+
)}
454458
api={api}
455459
organization={organization}
456460
onTotalValuesChange={this.handleTotalValuesChange}

static/app/views/projectDetail/projectScoreCards/projectAnrScoreCard.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import {URL_PARAM} from 'sentry/constants/pageFilters';
1111
import {t} from 'sentry/locale';
1212
import type {PageFilters} from 'sentry/types/core';
1313
import type {Organization, SessionApiResponse} from 'sentry/types/organization';
14+
import type {PlatformKey} from 'sentry/types/project';
1415
import {defined} from 'sentry/utils';
1516
import {trackAnalytics} from 'sentry/utils/analytics';
1617
import {getPeriod} from 'sentry/utils/duration/getPeriod';
1718
import useApi from 'sentry/utils/useApi';
1819
import {BigNumberWidgetVisualization} from 'sentry/views/dashboards/widgets/bigNumberWidget/bigNumberWidgetVisualization';
1920
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
21+
import {getANRIssueQueryText, getANRRateText} from 'sentry/views/projectDetail/utils';
2022
import {
2123
getSessionTermDescription,
2224
SessionTerm,
@@ -27,6 +29,7 @@ type Props = {
2729
location: Location;
2830
organization: Organization;
2931
selection: PageFilters;
32+
platform?: PlatformKey;
3033
query?: string;
3134
};
3235

@@ -36,6 +39,7 @@ export function ProjectAnrScoreCard({
3639
selection,
3740
location,
3841
query,
42+
platform,
3943
}: Props) {
4044
const {environments, projects, datetime} = selection;
4145
const {start, end, period} = datetime;
@@ -128,7 +132,7 @@ export function ProjectAnrScoreCard({
128132

129133
const endpointPath = `/organizations/${organization.slug}/issues/`;
130134

131-
const issueQuery = ['mechanism:[ANR,AppExitInfo]', query].join(' ').trim();
135+
const issueQuery = [getANRIssueQueryText(platform), query].join(' ').trim();
132136

133137
const queryParams = {
134138
...normalizeDateTimeParams(pick(location.query, [...Object.values(URL_PARAM)])),
@@ -141,9 +145,8 @@ export function ProjectAnrScoreCard({
141145
query: queryParams,
142146
};
143147

144-
const cardTitle = t('ANR Rate');
145-
146-
const cardHelp = getSessionTermDescription(SessionTerm.ANR_RATE, null);
148+
const cardTitle = getANRRateText(platform);
149+
const cardHelp = getSessionTermDescription(SessionTerm.ANR_RATE, platform || null);
147150

148151
const Title = <Widget.WidgetTitle title={cardTitle} />;
149152

static/app/views/projectDetail/projectScoreCards/projectScoreCards.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function ProjectScoreCards({
6868
isProjectStabilized={isProjectStabilized}
6969
query={query}
7070
location={location}
71+
platform={project?.platform}
7172
/>
7273
) : (
7374
<ProjectApdexScoreCard

static/app/views/projectDetail/utils.spec.tsx

+41-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {isPlatformANRCompatible, isPlatformForegroundANRCompatible} from './utils';
1+
import {
2+
getANRIssueQueryText,
3+
getANRRateText,
4+
isPlatformANRCompatible,
5+
isPlatformForegroundANRCompatible,
6+
} from './utils';
27

38
describe('ProjectDetail Utils', function () {
49
describe('isPlatformANRCompatible', function () {
@@ -50,4 +55,39 @@ describe('ProjectDetail Utils', function () {
5055
expect(isPlatformForegroundANRCompatible(undefined)).toBe(false);
5156
});
5257
});
58+
59+
describe('getANRRateText', function () {
60+
it('returns "App Hang Rate" for apple platforms', function () {
61+
expect(getANRRateText('apple')).toBe('App Hang Rate');
62+
expect(getANRRateText('apple-ios')).toBe('App Hang Rate');
63+
});
64+
65+
it('returns "ANR Rate" for other platforms', function () {
66+
expect(getANRRateText('apple-macos')).toBe('ANR Rate');
67+
expect(getANRRateText('android')).toBe('ANR Rate');
68+
expect(getANRRateText('javascript-electron')).toBe('ANR Rate');
69+
expect(getANRRateText('python')).toBe('ANR Rate');
70+
expect(getANRRateText(undefined)).toBe('ANR Rate');
71+
});
72+
});
73+
74+
describe('getANRIssueQueryText', () => {
75+
it('returns correct query text for apple platforms', () => {
76+
expect(getANRIssueQueryText('apple')).toBe(
77+
'error.type:["Fatal App Hang Fully Blocked","Fatal App Hang Non Fully Blocked"]'
78+
);
79+
expect(getANRIssueQueryText('apple-ios')).toBe(
80+
'error.type:["Fatal App Hang Fully Blocked","Fatal App Hang Non Fully Blocked"]'
81+
);
82+
});
83+
84+
it('returns correct query text for android platform', () => {
85+
expect(getANRIssueQueryText('android')).toBe('mechanism:[ANR,AppExitInfo]');
86+
});
87+
88+
it('returns correct query text for other platforms', () => {
89+
expect(getANRIssueQueryText('windows')).toBe('mechanism:[ANR,AppExitInfo]');
90+
expect(getANRIssueQueryText()).toBe('mechanism:[ANR,AppExitInfo]');
91+
});
92+
});
5393
});

static/app/views/projectDetail/utils.tsx

+27-4
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,37 @@ export function isPlatformANRCompatible(platform?: PlatformKey, features?: strin
1313
if (isPlatformForegroundANRCompatible(platform)) {
1414
return true;
1515
}
16-
if (platform === 'apple' || platform === 'apple-ios') {
17-
if (features?.includes('project-detail-apple-app-hang-rate')) {
18-
return true;
19-
}
16+
if (
17+
isAppHangPlatform(platform) &&
18+
features?.includes('project-detail-apple-app-hang-rate')
19+
) {
20+
return true;
2021
}
2122
return false;
2223
}
2324

2425
export function isPlatformForegroundANRCompatible(platform?: PlatformKey) {
2526
return platform === 'javascript-electron' || platform === 'android';
2627
}
28+
29+
export function getANRRateText(platform?: PlatformKey) {
30+
if (isAppHangPlatform(platform)) {
31+
return 'App Hang Rate';
32+
}
33+
34+
return 'ANR Rate';
35+
}
36+
37+
export function getANRIssueQueryText(platform?: PlatformKey) {
38+
if (isAppHangPlatform(platform)) {
39+
return 'error.type:["Fatal App Hang Fully Blocked","Fatal App Hang Non Fully Blocked"]';
40+
}
41+
42+
return 'mechanism:[ANR,AppExitInfo]';
43+
}
44+
45+
// Don't include apple-macos because the ANR rate requires app hangs V2,
46+
// which is not available on macos, when writing this comment on 2025-03-17.
47+
const isAppHangPlatform = (platform?: PlatformKey): boolean => {
48+
return platform === 'apple' || platform === 'apple-ios';
49+
};

static/app/views/releases/utils/sessionTerm.spec.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,15 @@ describe('Release Health Session Term', function () {
470470
'apple-macos'
471471
);
472472
expect(unhandledSessionTerm).toEqual(mobileTermsDescription.unhandled);
473+
474+
// ANR Rate
475+
const anrRateSessionTerm = getSessionTermDescription(
476+
SessionTerm.ANR_RATE,
477+
'apple-ios'
478+
);
479+
expect(anrRateSessionTerm).toBe(
480+
'Percentage of unique users that experienced a fatal App Hang.'
481+
);
473482
});
474483

475484
it('node-express terms', function () {

static/app/views/releases/utils/sessionTerm.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ function getTermDescriptions(platform: PlatformKey | null) {
123123
...commonTermsDescription,
124124
...mobileTermsDescription,
125125
[SessionTerm.CRASHED]: t('An error that resulted in the application crashing'),
126+
[SessionTerm.ANR_RATE]: t(
127+
'Percentage of unique users that experienced a fatal App Hang.'
128+
),
126129
};
127130
}
128131
case 'node':

0 commit comments

Comments
 (0)