From 086617d2e453e8792741181148de47f484a9b599 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Mon, 13 Apr 2026 17:02:23 +0600 Subject: [PATCH 1/9] feat(source-map-config-issues): Implementing Impact section in issue details --- .../sourceMapIssues/impactSection.tsx | 142 +++++++++++++++ .../sourceMapIssues/sourceMapIssueDetails.tsx | 3 + .../useProcessingErrorsQuery.ts | 161 ++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx new file mode 100644 index 00000000000000..2a190ed461f81e --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx @@ -0,0 +1,142 @@ +import React from 'react'; + +import {Flex, Stack} from '@sentry/scraps/layout'; +import {Link} from '@sentry/scraps/link'; +import {Heading, Text} from '@sentry/scraps/text'; + +import {LoadingError} from 'sentry/components/loadingError'; +import {LoadingIndicator} from 'sentry/components/loadingIndicator'; +import {TimeSince} from 'sentry/components/timeSince'; +import {Version} from 'sentry/components/version'; +import {t, tn} from 'sentry/locale'; +import {normalizeUrl} from 'sentry/utils/url/normalizeUrl'; +import {useOrganization} from 'sentry/utils/useOrganization'; +import {SectionDivider} from 'sentry/views/issueDetails/streamline/foldSection'; + +import { + useAffectedReleasesQuery, + useProcessingErrorsQuery, + useSampleEventsQuery, +} from './useProcessingErrorsQuery'; + +interface ImpactSectionProps { + projectId: string; +} + +export function ImpactSection({projectId}: ImpactSectionProps) { + const organization = useOrganization(); + const {count, isLoading, isError} = useProcessingErrorsQuery({projectId}); + const { + releases, + isLoading: releasesLoading, + isError: releasesError, + } = useAffectedReleasesQuery({projectId}); + const { + events, + isLoading: eventsLoading, + isError: eventsError, + } = useSampleEventsQuery({projectId}); + + function renderCount() { + if (isLoading) { + return ; + } + if (isError) { + return ; + } + if (count === null) { + return null; + } + return ( + + {tn( + '%s event with unreadable stack traces in the last 30 days', + '%s events with unreadable stack traces in the last 30 days', + count + )} + + ); + } + + function renderReleases() { + if (releasesLoading) { + return ; + } + if (releasesError) { + return ; + } + if (releases.length === 0) { + return null; + } + return ( + + {t('Affected releases')} + + {releases.map(({release, count: eventCount}) => ( + + + · + {tn('%s event', '%s events', eventCount)} + + ))} + + + ); + } + + function renderSampleEvents() { + if (eventsLoading) { + return ; + } + if (eventsError) { + return ; + } + if (events.length === 0) { + return null; + } + return ( + + {t('Sample events')} + + {events.map(({eventId, groupId, title, timestamp}) => ( + + + {title} + + · + + + + + ))} + + + ); + } + + const releasesContent = renderReleases(); + const sampleEventsContent = renderSampleEvents(); + + return ( + + {t('Impact')} + {renderCount()} + {releasesContent && ( + + + {releasesContent} + + )} + {sampleEventsContent && ( + + + {sampleEventsContent} + + )} + + ); +} diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx index ea0afaacf9afa7..a5fcd87e655137 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx @@ -5,6 +5,7 @@ import type {Project} from 'sentry/types/project'; import {SectionDivider} from 'sentry/views/issueDetails/streamline/foldSection'; import {DiagnosisSection} from './diagnosisSection'; +import {ImpactSection} from './impactSection'; import {ProblemSection} from './problemSection'; import {TroubleshootingSection} from './troubleshootingSection'; @@ -28,6 +29,8 @@ export function SourceMapIssueDetails({event, project}: SourceMapIssueDetailsPro + + ); } diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts new file mode 100644 index 00000000000000..ca432378e2eafa --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts @@ -0,0 +1,161 @@ +import {getApiUrl} from 'sentry/utils/api/getApiUrl'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import {useOrganization} from 'sentry/utils/useOrganization'; + +const COUNT_FIELD = 'count_unique(event_id)' as const; +const RELEASE_FIELDS = ['release', COUNT_FIELD] as const; +const SAMPLE_FIELDS = ['title', 'event_id', 'group_id', 'timestamp'] as const; + +interface ProcessingErrorsCountResult { + data: Array>; +} + +type ReleaseRow = {release: string} & Record; + +interface ProcessingErrorsReleasesResult { + data: ReleaseRow[]; +} + +export interface ProcessingErrorsCount { + count: number | null; + isError: boolean; + isLoading: boolean; +} + +export interface AffectedRelease { + count: number; + release: string; +} + +export interface SampleEvent { + eventId: string; + groupId: string; + timestamp: string; + title: string; +} + +export interface SampleEventsResult { + events: SampleEvent[]; + isError: boolean; + isLoading: boolean; +} + +export interface AffectedReleasesResult { + isError: boolean; + isLoading: boolean; + releases: AffectedRelease[]; +} + +interface UseProcessingErrorsQueryOptions { + projectId: string; +} + +export function useProcessingErrorsQuery({ + projectId, +}: UseProcessingErrorsQueryOptions): ProcessingErrorsCount { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: [COUNT_FIELD], + statsPeriod: '30d', + project: projectId, + referrer: 'api.issues.sourcemap-configuration.impact', + }, + }, + ], + {staleTime: 60_000} + ); + + return { + count: data?.data?.[0]?.[COUNT_FIELD] ?? null, + isLoading, + isError, + }; +} + +export function useAffectedReleasesQuery({ + projectId, +}: UseProcessingErrorsQueryOptions): AffectedReleasesResult { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: RELEASE_FIELDS, + sort: `-${COUNT_FIELD}`, + per_page: 5, + statsPeriod: '30d', + project: projectId, + referrer: 'api.issues.sourcemap-configuration.impact-releases', + }, + }, + ], + {staleTime: 60_000} + ); + + const rows: ReleaseRow[] = data?.data ?? []; + const releases: AffectedRelease[] = rows + .filter((row: ReleaseRow) => Boolean(row.release)) + .map((row: ReleaseRow) => ({release: row.release, count: row[COUNT_FIELD]})); + + return {releases, isLoading, isError}; +} + +type SampleRow = { + event_id: string; + group_id: string; + timestamp: string; + title: string; +}; + +interface ProcessingErrorsSamplesResult { + data: SampleRow[]; +} + +export function useSampleEventsQuery({ + projectId, +}: UseProcessingErrorsQueryOptions): SampleEventsResult { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: SAMPLE_FIELDS, + sort: '-timestamp', + per_page: 5, + statsPeriod: '30d', + project: projectId, + referrer: 'api.issues.sourcemap-configuration.impact-samples', + }, + }, + ], + {staleTime: 60_000} + ); + + const rows: SampleRow[] = data?.data ?? []; + const events: SampleEvent[] = rows.map((row: SampleRow) => ({ + title: row.title, + eventId: row.event_id, + groupId: row.group_id, + timestamp: row.timestamp, + })); + + return {events, isLoading, isError}; +} From 4a5b7f2a096e9476f4fb78d52f011c0f7a832701 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Mon, 13 Apr 2026 18:38:24 +0600 Subject: [PATCH 2/9] feat(source-map-config-issues): Iterating --- .../sourceMapIssues/impactSection.tsx | 154 ++++++++--------- .../queries/useAffectedReleases.ts | 59 +++++++ .../queries/useImpactedEventsCount.ts | 46 +++++ .../queries/useSampleEvents.ts | 66 +++++++ .../sourceMapIssues/sourceMapIssueDetails.tsx | 2 +- .../useProcessingErrorsQuery.ts | 161 ------------------ 6 files changed, 242 insertions(+), 246 deletions(-) create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts delete mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx index 2a190ed461f81e..ce8510d1755554 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import {Fragment} from 'react'; import {Flex, Stack} from '@sentry/scraps/layout'; import {Link} from '@sentry/scraps/link'; @@ -9,92 +9,85 @@ import {LoadingIndicator} from 'sentry/components/loadingIndicator'; import {TimeSince} from 'sentry/components/timeSince'; import {Version} from 'sentry/components/version'; import {t, tn} from 'sentry/locale'; +import type {Project} from 'sentry/types/project'; import {normalizeUrl} from 'sentry/utils/url/normalizeUrl'; import {useOrganization} from 'sentry/utils/useOrganization'; import {SectionDivider} from 'sentry/views/issueDetails/streamline/foldSection'; -import { - useAffectedReleasesQuery, - useProcessingErrorsQuery, - useSampleEventsQuery, -} from './useProcessingErrorsQuery'; +import {useAffectedReleases} from './queries/useAffectedReleases'; +import {useImpactedEventsCount} from './queries/useImpactedEventsCount'; +import {useSampleEvents} from './queries/useSampleEvents'; -interface ImpactSectionProps { - projectId: string; +function EventsCount({project}: {project: Project}) { + const {count, isLoading, isError} = useImpactedEventsCount({project}); + + if (isLoading) { + return ; + } + if (isError) { + return ; + } + if (count === null) { + return null; + } + return ( + + {tn( + '%s event with unreadable stack traces in the last 30 days', + '%s events with unreadable stack traces in the last 30 days', + count + )} + + ); } -export function ImpactSection({projectId}: ImpactSectionProps) { - const organization = useOrganization(); - const {count, isLoading, isError} = useProcessingErrorsQuery({projectId}); - const { - releases, - isLoading: releasesLoading, - isError: releasesError, - } = useAffectedReleasesQuery({projectId}); - const { - events, - isLoading: eventsLoading, - isError: eventsError, - } = useSampleEventsQuery({projectId}); +function AffectedReleases({project}: {project: Project}) { + const {releases, isLoading, isError} = useAffectedReleases({project}); - function renderCount() { - if (isLoading) { - return ; - } - if (isError) { - return ; - } - if (count === null) { - return null; - } - return ( - - {tn( - '%s event with unreadable stack traces in the last 30 days', - '%s events with unreadable stack traces in the last 30 days', - count - )} - - ); + if (isLoading) { + return ; } - - function renderReleases() { - if (releasesLoading) { - return ; - } - if (releasesError) { - return ; - } - if (releases.length === 0) { - return null; - } - return ( + if (isError) { + return ; + } + if (releases.length === 0) { + return null; + } + return ( + + {t('Affected releases')} - {releases.map(({release, count: eventCount}) => ( + {releases.map(({release, count}) => ( · - {tn('%s event', '%s events', eventCount)} + {tn('%s event', '%s events', count)} ))} - ); - } + + ); +} + +function SampleEvents({project}: {project: Project}) { + const organization = useOrganization(); + const {events, isLoading, isError} = useSampleEvents({project}); - function renderSampleEvents() { - if (eventsLoading) { - return ; - } - if (eventsError) { - return ; - } - if (events.length === 0) { - return null; - } - return ( + if (isLoading) { + return ; + } + if (isError) { + return ; + } + if (events.length === 0) { + return null; + } + return ( + + {t('Sample events')} @@ -115,28 +108,21 @@ export function ImpactSection({projectId}: ImpactSectionProps) { ))} - ); - } + + ); +} - const releasesContent = renderReleases(); - const sampleEventsContent = renderSampleEvents(); +interface ImpactSectionProps { + project: Project; +} +export function ImpactSection({project}: ImpactSectionProps) { return ( {t('Impact')} - {renderCount()} - {releasesContent && ( - - - {releasesContent} - - )} - {sampleEventsContent && ( - - - {sampleEventsContent} - - )} + + + ); } diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts new file mode 100644 index 00000000000000..b231c1ecc8faf5 --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts @@ -0,0 +1,59 @@ +import type {Project} from 'sentry/types/project'; +import {getApiUrl} from 'sentry/utils/api/getApiUrl'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import {useOrganization} from 'sentry/utils/useOrganization'; + +type ReleaseRow = {release: string} & {'count_unique(event_id)': number}; + +interface ReleasesResult { + data: ReleaseRow[]; +} + +export interface AffectedRelease { + count: number; + release: string; +} + +export interface AffectedReleasesResult { + isError: boolean; + isLoading: boolean; + releases: AffectedRelease[]; +} + +interface Options { + project: Project; +} + +export function useAffectedReleases({project}: Options): AffectedReleasesResult { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: ['release', 'count_unique(event_id)'], + sort: '-count_unique(event_id)', + per_page: 5, + statsPeriod: '30d', + project: project.id, + referrer: 'api.issues.sourcemap-configuration.impact-releases', + }, + }, + ], + {staleTime: 60_000} + ); + + const rows: ReleaseRow[] = data?.data ?? []; + const releases: AffectedRelease[] = rows + .filter((row: ReleaseRow) => Boolean(row.release)) + .map((row: ReleaseRow) => ({ + release: row.release, + count: row['count_unique(event_id)'], + })); + + return {releases, isLoading, isError}; +} diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts new file mode 100644 index 00000000000000..d355f0ccd36077 --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts @@ -0,0 +1,46 @@ +import type {Project} from 'sentry/types/project'; +import {getApiUrl} from 'sentry/utils/api/getApiUrl'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import {useOrganization} from 'sentry/utils/useOrganization'; + +interface CountResult { + data: Array<{'count_unique(event_id)': number}>; +} + +export interface ImpactedEventsCount { + count: number | null; + isError: boolean; + isLoading: boolean; +} + +interface Options { + project: Project; +} + +export function useImpactedEventsCount({project}: Options): ImpactedEventsCount { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: ['count_unique(event_id)'], + statsPeriod: '30d', + project: project.id, + referrer: 'api.issues.sourcemap-configuration.impact', + }, + }, + ], + {staleTime: 60_000} + ); + + return { + count: data?.data?.[0]?.['count_unique(event_id)'] ?? null, + isLoading, + isError, + }; +} diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts new file mode 100644 index 00000000000000..3b28eb69236c69 --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts @@ -0,0 +1,66 @@ +import type {Project} from 'sentry/types/project'; +import {getApiUrl} from 'sentry/utils/api/getApiUrl'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import {useOrganization} from 'sentry/utils/useOrganization'; + +type SampleRow = { + event_id: string; + group_id: string; + timestamp: string; + title: string; +}; + +interface SamplesResult { + data: SampleRow[]; +} + +export interface SampleEvent { + eventId: string; + groupId: string; + timestamp: string; + title: string; +} + +export interface SampleEventsResult { + events: SampleEvent[]; + isError: boolean; + isLoading: boolean; +} + +interface Options { + project: Project; +} + +export function useSampleEvents({project}: Options): SampleEventsResult { + const organization = useOrganization(); + + const {data, isLoading, isError} = useApiQuery( + [ + getApiUrl('/organizations/$organizationIdOrSlug/events/', { + path: {organizationIdOrSlug: organization.slug}, + }), + { + query: { + dataset: 'processing_errors', + field: ['title', 'event_id', 'group_id', 'timestamp'], + sort: '-timestamp', + per_page: 5, + statsPeriod: '30d', + project: project.id, + referrer: 'api.issues.sourcemap-configuration.impact-samples', + }, + }, + ], + {staleTime: 60_000} + ); + + const rows: SampleRow[] = data?.data ?? []; + const events: SampleEvent[] = rows.map((row: SampleRow) => ({ + title: row.title, + eventId: row.event_id, + groupId: row.group_id, + timestamp: row.timestamp, + })); + + return {events, isLoading, isError}; +} diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx index a5fcd87e655137..77ba0c6a9b547e 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx @@ -30,7 +30,7 @@ export function SourceMapIssueDetails({event, project}: SourceMapIssueDetailsPro - + ); } diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts deleted file mode 100644 index ca432378e2eafa..00000000000000 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/useProcessingErrorsQuery.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {getApiUrl} from 'sentry/utils/api/getApiUrl'; -import {useApiQuery} from 'sentry/utils/queryClient'; -import {useOrganization} from 'sentry/utils/useOrganization'; - -const COUNT_FIELD = 'count_unique(event_id)' as const; -const RELEASE_FIELDS = ['release', COUNT_FIELD] as const; -const SAMPLE_FIELDS = ['title', 'event_id', 'group_id', 'timestamp'] as const; - -interface ProcessingErrorsCountResult { - data: Array>; -} - -type ReleaseRow = {release: string} & Record; - -interface ProcessingErrorsReleasesResult { - data: ReleaseRow[]; -} - -export interface ProcessingErrorsCount { - count: number | null; - isError: boolean; - isLoading: boolean; -} - -export interface AffectedRelease { - count: number; - release: string; -} - -export interface SampleEvent { - eventId: string; - groupId: string; - timestamp: string; - title: string; -} - -export interface SampleEventsResult { - events: SampleEvent[]; - isError: boolean; - isLoading: boolean; -} - -export interface AffectedReleasesResult { - isError: boolean; - isLoading: boolean; - releases: AffectedRelease[]; -} - -interface UseProcessingErrorsQueryOptions { - projectId: string; -} - -export function useProcessingErrorsQuery({ - projectId, -}: UseProcessingErrorsQueryOptions): ProcessingErrorsCount { - const organization = useOrganization(); - - const {data, isLoading, isError} = useApiQuery( - [ - getApiUrl('/organizations/$organizationIdOrSlug/events/', { - path: {organizationIdOrSlug: organization.slug}, - }), - { - query: { - dataset: 'processing_errors', - field: [COUNT_FIELD], - statsPeriod: '30d', - project: projectId, - referrer: 'api.issues.sourcemap-configuration.impact', - }, - }, - ], - {staleTime: 60_000} - ); - - return { - count: data?.data?.[0]?.[COUNT_FIELD] ?? null, - isLoading, - isError, - }; -} - -export function useAffectedReleasesQuery({ - projectId, -}: UseProcessingErrorsQueryOptions): AffectedReleasesResult { - const organization = useOrganization(); - - const {data, isLoading, isError} = useApiQuery( - [ - getApiUrl('/organizations/$organizationIdOrSlug/events/', { - path: {organizationIdOrSlug: organization.slug}, - }), - { - query: { - dataset: 'processing_errors', - field: RELEASE_FIELDS, - sort: `-${COUNT_FIELD}`, - per_page: 5, - statsPeriod: '30d', - project: projectId, - referrer: 'api.issues.sourcemap-configuration.impact-releases', - }, - }, - ], - {staleTime: 60_000} - ); - - const rows: ReleaseRow[] = data?.data ?? []; - const releases: AffectedRelease[] = rows - .filter((row: ReleaseRow) => Boolean(row.release)) - .map((row: ReleaseRow) => ({release: row.release, count: row[COUNT_FIELD]})); - - return {releases, isLoading, isError}; -} - -type SampleRow = { - event_id: string; - group_id: string; - timestamp: string; - title: string; -}; - -interface ProcessingErrorsSamplesResult { - data: SampleRow[]; -} - -export function useSampleEventsQuery({ - projectId, -}: UseProcessingErrorsQueryOptions): SampleEventsResult { - const organization = useOrganization(); - - const {data, isLoading, isError} = useApiQuery( - [ - getApiUrl('/organizations/$organizationIdOrSlug/events/', { - path: {organizationIdOrSlug: organization.slug}, - }), - { - query: { - dataset: 'processing_errors', - field: SAMPLE_FIELDS, - sort: '-timestamp', - per_page: 5, - statsPeriod: '30d', - project: projectId, - referrer: 'api.issues.sourcemap-configuration.impact-samples', - }, - }, - ], - {staleTime: 60_000} - ); - - const rows: SampleRow[] = data?.data ?? []; - const events: SampleEvent[] = rows.map((row: SampleRow) => ({ - title: row.title, - eventId: row.event_id, - groupId: row.group_id, - timestamp: row.timestamp, - })); - - return {events, isLoading, isError}; -} From 5fd967768b86089ff87d9f5c598f6132c94d8fef Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Mon, 13 Apr 2026 20:36:59 +0600 Subject: [PATCH 3/9] feat(source-map-config-issues): Iterating --- .../sourceMapIssues/impactSection.spec.tsx | 99 +++++++++++++++++++ .../queries/useImpactedEventsCount.ts | 2 +- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.spec.tsx diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.spec.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.spec.tsx new file mode 100644 index 00000000000000..0a00edc86add27 --- /dev/null +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.spec.tsx @@ -0,0 +1,99 @@ +import {OrganizationFixture} from 'sentry-fixture/organization'; +import {ProjectFixture} from 'sentry-fixture/project'; + +import {render, screen} from 'sentry-test/reactTestingLibrary'; + +import {ImpactSection} from './impactSection'; + +const REFERRER_EVENTS_COUNT = 'api.issues.sourcemap-configuration.impact-events-count'; +const REFERRER_RELEASES = 'api.issues.sourcemap-configuration.impact-releases'; +const REFERRER_SAMPLES = 'api.issues.sourcemap-configuration.impact-samples'; + +describe('ImpactSection', () => { + const organization = OrganizationFixture(); + const project = ProjectFixture(); + const eventsUrl = `/organizations/${organization.slug}/events/`; + + beforeEach(() => { + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_EVENTS_COUNT})], + body: {data: [{'count_unique(event_id)': 0}]}, + }); + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_RELEASES})], + body: {data: []}, + }); + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_SAMPLES})], + body: {data: []}, + }); + }); + + it('renders the Impact heading with event count', async () => { + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_EVENTS_COUNT})], + body: {data: [{'count_unique(event_id)': 17}]}, + }); + + render(, {organization}); + + expect(screen.getByText('Impact')).toBeInTheDocument(); + expect( + await screen.findByText( + '17 events with unreadable stack traces in the last 30 days' + ) + ).toBeInTheDocument(); + }); + + it('renders affected releases with event counts', async () => { + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_RELEASES})], + body: { + data: [ + {release: 'v2.4.1', 'count_unique(event_id)': 8}, + {release: 'v2.3.0', 'count_unique(event_id)': 3}, + ], + }, + }); + + render(, {organization}); + + expect(await screen.findByText('Affected releases')).toBeInTheDocument(); + expect(screen.getByText('v2.4.1')).toBeInTheDocument(); + expect(screen.getByText('8 events')).toBeInTheDocument(); + expect(screen.getByText('v2.3.0')).toBeInTheDocument(); + expect(screen.getByText('3 events')).toBeInTheDocument(); + }); + + it('renders sample events with links to issue details', async () => { + MockApiClient.addMockResponse({ + url: eventsUrl, + match: [MockApiClient.matchQuery({referrer: REFERRER_SAMPLES})], + body: { + data: [ + { + title: 'TypeError: Cannot read property', + event_id: 'abc123', + group_id: 'group456', + timestamp: '2024-01-15T10:00:00.000Z', + }, + ], + }, + }); + + render(, {organization}); + + expect(await screen.findByText('Sample events')).toBeInTheDocument(); + + const link = screen.getByRole('link', {name: 'TypeError: Cannot read property'}); + expect(link).toHaveAttribute( + 'href', + `/organizations/${organization.slug}/issues/group456/events/abc123/` + ); + }); +}); diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts index d355f0ccd36077..27840fd916e501 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts @@ -31,7 +31,7 @@ export function useImpactedEventsCount({project}: Options): ImpactedEventsCount field: ['count_unique(event_id)'], statsPeriod: '30d', project: project.id, - referrer: 'api.issues.sourcemap-configuration.impact', + referrer: 'api.issues.sourcemap-configuration.impact-events-count', }, }, ], From b7bb00743d83089814b7095637568a8612a965a2 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Mon, 13 Apr 2026 20:45:07 +0600 Subject: [PATCH 4/9] feat(source-map-config-issues): Fixing CI issues --- .../sourceMapIssues/queries/useAffectedReleases.ts | 8 ++++---- .../sourceMapIssues/queries/useImpactedEventsCount.ts | 2 +- .../sourceMapIssues/queries/useSampleEvents.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts index b231c1ecc8faf5..1d6dfbb916f56d 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useAffectedReleases.ts @@ -9,12 +9,12 @@ interface ReleasesResult { data: ReleaseRow[]; } -export interface AffectedRelease { +interface AffectedRelease { count: number; release: string; } -export interface AffectedReleasesResult { +interface AffectedReleasesResult { isError: boolean; isLoading: boolean; releases: AffectedRelease[]; @@ -47,8 +47,8 @@ export function useAffectedReleases({project}: Options): AffectedReleasesResult {staleTime: 60_000} ); - const rows: ReleaseRow[] = data?.data ?? []; - const releases: AffectedRelease[] = rows + const rows = data?.data ?? []; + const releases = rows .filter((row: ReleaseRow) => Boolean(row.release)) .map((row: ReleaseRow) => ({ release: row.release, diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts index 27840fd916e501..43e40370f7327f 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useImpactedEventsCount.ts @@ -7,7 +7,7 @@ interface CountResult { data: Array<{'count_unique(event_id)': number}>; } -export interface ImpactedEventsCount { +interface ImpactedEventsCount { count: number | null; isError: boolean; isLoading: boolean; diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts index 3b28eb69236c69..e94c055c8f33d2 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts @@ -14,14 +14,14 @@ interface SamplesResult { data: SampleRow[]; } -export interface SampleEvent { +interface SampleEvent { eventId: string; groupId: string; timestamp: string; title: string; } -export interface SampleEventsResult { +interface SampleEventsResult { events: SampleEvent[]; isError: boolean; isLoading: boolean; @@ -54,8 +54,8 @@ export function useSampleEvents({project}: Options): SampleEventsResult { {staleTime: 60_000} ); - const rows: SampleRow[] = data?.data ?? []; - const events: SampleEvent[] = rows.map((row: SampleRow) => ({ + const rows = data?.data ?? []; + const events = rows.map((row: SampleRow) => ({ title: row.title, eventId: row.event_id, groupId: row.group_id, From bc091474d0ff619ead842e79fe43c357b986b82b Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Mon, 13 Apr 2026 23:19:01 +0600 Subject: [PATCH 5/9] feat(source-map-config-issues): Adding Sentry Configuration nav item under issues --- .../sourceMapIssues/impactSection.tsx | 104 ++++++++++-------- .../queries/useSampleEvents.ts | 4 +- 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx index ce8510d1755554..0763630277624b 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/impactSection.tsx @@ -27,8 +27,8 @@ function EventsCount({project}: {project: Project}) { if (isError) { return ; } - if (count === null) { - return null; + if (!count) { + return {t('No impacted events found in the last 30 days.')}; } return ( @@ -44,29 +44,35 @@ function EventsCount({project}: {project: Project}) { function AffectedReleases({project}: {project: Project}) { const {releases, isLoading, isError} = useAffectedReleases({project}); - if (isLoading) { - return ; - } - if (isError) { - return ; - } - if (releases.length === 0) { - return null; + function renderContent() { + if (isLoading) { + return ; + } + if (isError) { + return ; + } + if (releases.length === 0) { + return {t('No affected releases found in the last 30 days.')}; + } + return ( + + {releases.map(({release, count}) => ( + + + · + {tn('%s event', '%s events', count)} + + ))} + + ); } + return ( - {t('Affected releases')} - - {releases.map(({release, count}) => ( - - - · - {tn('%s event', '%s events', count)} - - ))} - + {t('Affected releases')} + {renderContent()} ); @@ -76,23 +82,21 @@ function SampleEvents({project}: {project: Project}) { const organization = useOrganization(); const {events, isLoading, isError} = useSampleEvents({project}); - if (isLoading) { - return ; - } - if (isError) { - return ; - } - if (events.length === 0) { - return null; - } - return ( - - - - {t('Sample events')} - - {events.map(({eventId, groupId, title, timestamp}) => ( - + function renderContent() { + if (isLoading) { + return ; + } + if (isError) { + return ; + } + if (events.length === 0) { + return {t('No sample events found in the last 30 days.')}; + } + return ( + + {events.map(({eventId, groupId, title, timestamp}) => ( + + {groupId ? ( {title} - · - - - - - ))} - + ) : ( + {title} + )} + · + + + + + ))} + + ); + } + + return ( + + + + {t('Sample events')} + {renderContent()} ); diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts index e94c055c8f33d2..0b43872b52ba80 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/queries/useSampleEvents.ts @@ -5,7 +5,7 @@ import {useOrganization} from 'sentry/utils/useOrganization'; type SampleRow = { event_id: string; - group_id: string; + group_id: string | null; timestamp: string; title: string; }; @@ -16,7 +16,7 @@ interface SamplesResult { interface SampleEvent { eventId: string; - groupId: string; + groupId: string | null; timestamp: string; title: string; } From 6d0ac7d3bb8610463925a8df4d051a21940224b7 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Tue, 14 Apr 2026 00:00:15 +0600 Subject: [PATCH 6/9] feat(source-map-config-issues): Making source map doc links platform specifc --- .../sourceMapIssues/problemSection.tsx | 23 ++++++++++++------- .../sourceMapIssues/sourceMapIssueDetails.tsx | 2 +- .../troubleshootingSection.tsx | 10 +++++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx index e5a61003bbf324..083e1b588a992e 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx @@ -2,10 +2,23 @@ import {LinkButton} from '@sentry/scraps/button'; import {Stack} from '@sentry/scraps/layout'; import {Heading, Text} from '@sentry/scraps/text'; +import { + getSourceMapsDocLinks, + projectPlatformToDocsMap, +} from 'sentry/components/events/interfaces/sourceMapsDebuggerModal'; import {IconInfo} from 'sentry/icons'; import {t} from 'sentry/locale'; +import type {Project} from 'sentry/types/project'; + +interface ProblemSectionProps { + project: Project; +} + +export function ProblemSection({project}: ProblemSectionProps) { + const docsSegment = + (project.platform && projectPlatformToDocsMap[project.platform]) ?? 'javascript'; + const docLinks = getSourceMapsDocLinks(docsSegment); -export function ProblemSection() { return ( {t('Problem')} @@ -15,13 +28,7 @@ export function ProblemSection() { )}
- } - external - // TODO Abdullah Khan: Look into adding platform specific links to source map docs - href="https://docs.sentry.io/platforms/javascript/sourcemaps/" - > + } external href={docLinks.sourcemaps}> {t('Why configure source maps?')}
diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx index 77ba0c6a9b547e..0ca9894c6350d7 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx @@ -24,7 +24,7 @@ export function SourceMapIssueDetails({event, project}: SourceMapIssueDetailsPro return (
- + diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx index 6b2c8ab8cda2f5..9f315834d6de9f 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx @@ -4,6 +4,10 @@ import {Disclosure} from '@sentry/scraps/disclosure'; import {Flex, Stack} from '@sentry/scraps/layout'; import {Heading, Prose, Text} from '@sentry/scraps/text'; +import { + getSourceMapsDocLinks, + projectPlatformToDocsMap, +} from 'sentry/components/events/interfaces/sourceMapsDebuggerModal'; import {ExternalLink} from 'sentry/components/links/externalLink'; import {IconDocs, IconSettings} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; @@ -18,6 +22,10 @@ export function TroubleshootingSection({project}: TroubleshootingSectionProps) { const organization = useOrganization(); const settingsUrl = `/settings/${organization.slug}/projects/${project.slug}/source-maps/`; + const docsSegment = + (project.platform && projectPlatformToDocsMap[project.platform]) ?? 'javascript'; + const docLinks = getSourceMapsDocLinks(docsSegment); + return ( {t('Troubleshooting suggestions')} @@ -125,7 +133,7 @@ export function TroubleshootingSection({project}: TroubleshootingSectionProps) { {t('Not what you\u2019re looking for?')} - + {t('Read all documentation')} From cb32f765bee4005ffe2df2d31bd242bf8250c47f Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Tue, 14 Apr 2026 00:07:03 +0600 Subject: [PATCH 7/9] feat(source-map-config-issues): Making source map doc links platform specifc --- .../sourceMapIssues/problemSection.spec.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx index d5dfa5b2768974..1ebe1704a6f5c7 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx @@ -1,10 +1,14 @@ +import {ProjectFixture} from 'sentry-fixture/project'; + import {render, screen} from 'sentry-test/reactTestingLibrary'; import {ProblemSection} from './problemSection'; describe('ProblemSection', () => { + const project = ProjectFixture(); + it('renders title, description, and docs link', () => { - render(); + render(); expect(screen.getByText('Problem')).toBeInTheDocument(); expect( From de30609adb77b9aa4ea861c97129b0b0dc5eb6f9 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Tue, 14 Apr 2026 22:15:20 +0600 Subject: [PATCH 8/9] feat(source-map-config-issues): Addressing PR suggestions --- .../interfaces/sourceMapsDebuggerModal.tsx | 1 + .../sourceMapIssues/problemSection.tsx | 15 +++------------ .../sourceMapIssues/sourceMapIssueDetails.tsx | 12 ++++++++++-- .../sourceMapIssues/troubleshootingSection.tsx | 16 ++++++---------- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/static/app/components/events/interfaces/sourceMapsDebuggerModal.tsx b/static/app/components/events/interfaces/sourceMapsDebuggerModal.tsx index e39ba610b622bd..8c26cd6c766286 100644 --- a/static/app/components/events/interfaces/sourceMapsDebuggerModal.tsx +++ b/static/app/components/events/interfaces/sourceMapsDebuggerModal.tsx @@ -187,6 +187,7 @@ export const projectPlatformToDocsMap: Record = { 'javascript-sveltekit': 'sveltekit', 'javascript-astro': 'astro', 'javascript-tanstackstart-react': 'tanstackstart-react', + 'react-native': 'react-native', }; function isReactNativeSDK({sdkName}: Pick) { diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx index 083e1b588a992e..56afe1fa0236be 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.tsx @@ -2,23 +2,14 @@ import {LinkButton} from '@sentry/scraps/button'; import {Stack} from '@sentry/scraps/layout'; import {Heading, Text} from '@sentry/scraps/text'; -import { - getSourceMapsDocLinks, - projectPlatformToDocsMap, -} from 'sentry/components/events/interfaces/sourceMapsDebuggerModal'; import {IconInfo} from 'sentry/icons'; import {t} from 'sentry/locale'; -import type {Project} from 'sentry/types/project'; interface ProblemSectionProps { - project: Project; + sourcemapsDocsUrl: string; } -export function ProblemSection({project}: ProblemSectionProps) { - const docsSegment = - (project.platform && projectPlatformToDocsMap[project.platform]) ?? 'javascript'; - const docLinks = getSourceMapsDocLinks(docsSegment); - +export function ProblemSection({sourcemapsDocsUrl}: ProblemSectionProps) { return ( {t('Problem')} @@ -28,7 +19,7 @@ export function ProblemSection({project}: ProblemSectionProps) { )}
- } external href={docLinks.sourcemaps}> + } external href={sourcemapsDocsUrl}> {t('Why configure source maps?')}
diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx index 0ca9894c6350d7..ab3bdbb44ccfac 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/sourceMapIssueDetails.tsx @@ -1,4 +1,8 @@ import {useSourceMapDebugQuery} from 'sentry/components/events/interfaces/crashContent/exception/useSourceMapDebuggerData'; +import { + getSourceMapsDocLinks, + projectPlatformToDocsMap, +} from 'sentry/components/events/interfaces/sourceMapsDebuggerModal'; import type {Event} from 'sentry/types/event'; import type {Group} from 'sentry/types/group'; import type {Project} from 'sentry/types/project'; @@ -22,13 +26,17 @@ export function SourceMapIssueDetails({event, project}: SourceMapIssueDetailsPro event.sdk?.name ?? null ); + const docsSegment = + (project.platform && projectPlatformToDocsMap[project.platform]) ?? 'javascript'; + const docLinks = getSourceMapsDocLinks(docsSegment); + return (
- + - +
diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx index 9f315834d6de9f..6a52aae1af7de1 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.tsx @@ -4,10 +4,6 @@ import {Disclosure} from '@sentry/scraps/disclosure'; import {Flex, Stack} from '@sentry/scraps/layout'; import {Heading, Prose, Text} from '@sentry/scraps/text'; -import { - getSourceMapsDocLinks, - projectPlatformToDocsMap, -} from 'sentry/components/events/interfaces/sourceMapsDebuggerModal'; import {ExternalLink} from 'sentry/components/links/externalLink'; import {IconDocs, IconSettings} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; @@ -16,16 +12,16 @@ import {useOrganization} from 'sentry/utils/useOrganization'; interface TroubleshootingSectionProps { project: Project; + sourcemapsDocsUrl: string; } -export function TroubleshootingSection({project}: TroubleshootingSectionProps) { +export function TroubleshootingSection({ + sourcemapsDocsUrl, + project, +}: TroubleshootingSectionProps) { const organization = useOrganization(); const settingsUrl = `/settings/${organization.slug}/projects/${project.slug}/source-maps/`; - const docsSegment = - (project.platform && projectPlatformToDocsMap[project.platform]) ?? 'javascript'; - const docLinks = getSourceMapsDocLinks(docsSegment); - return ( {t('Troubleshooting suggestions')} @@ -133,7 +129,7 @@ export function TroubleshootingSection({project}: TroubleshootingSectionProps) { {t('Not what you\u2019re looking for?')} - + {t('Read all documentation')} From 42f04191264b84407f3f465cec2409e6ccb529d5 Mon Sep 17 00:00:00 2001 From: Abdullah Khan Date: Tue, 14 Apr 2026 23:53:48 +0600 Subject: [PATCH 9/9] feat(source-map-config-issues): Fixing tests --- .../sourceMapIssues/problemSection.spec.tsx | 8 +++----- .../troubleshootingSection.spec.tsx | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx index 1ebe1704a6f5c7..1d5b5196ded0b8 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/problemSection.spec.tsx @@ -1,14 +1,12 @@ -import {ProjectFixture} from 'sentry-fixture/project'; - import {render, screen} from 'sentry-test/reactTestingLibrary'; import {ProblemSection} from './problemSection'; describe('ProblemSection', () => { - const project = ProjectFixture(); + const sourcemapsDocsUrl = 'https://docs.sentry.io/platforms/javascript/sourcemaps/'; it('renders title, description, and docs link', () => { - render(); + render(); expect(screen.getByText('Problem')).toBeInTheDocument(); expect( @@ -18,6 +16,6 @@ describe('ProblemSection', () => { ).toBeInTheDocument(); expect( screen.getByRole('button', {name: 'Why configure source maps?'}) - ).toHaveAttribute('href', 'https://docs.sentry.io/platforms/javascript/sourcemaps/'); + ).toHaveAttribute('href', sourcemapsDocsUrl); }); }); diff --git a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.spec.tsx b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.spec.tsx index 2caa09f28d195b..5bd959b70b79d8 100644 --- a/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.spec.tsx +++ b/static/app/views/issueDetails/configurationIssues/sourceMapIssues/troubleshootingSection.spec.tsx @@ -8,9 +8,13 @@ import {TroubleshootingSection} from './troubleshootingSection'; describe('TroubleshootingSection', () => { const organization = OrganizationFixture(); const project = ProjectFixture({slug: 'my-project'}); + const sourcemapsDocsUrl = 'https://docs.sentry.io/platforms/javascript/sourcemaps/'; it('renders all troubleshooting step titles', () => { - render(, {organization}); + render( + , + {organization} + ); expect(screen.getByText('Verify Artifacts Are Uploaded')).toBeInTheDocument(); expect( @@ -25,7 +29,10 @@ describe('TroubleshootingSection', () => { }); it('settings link points to the correct project source maps URL', () => { - render(, {organization}); + render( + , + {organization} + ); expect(screen.getByRole('button', {name: /settings/i})).toHaveAttribute( 'href', @@ -34,11 +41,14 @@ describe('TroubleshootingSection', () => { }); it('renders the footer docs link', () => { - render(, {organization}); + render( + , + {organization} + ); expect(screen.getByRole('link', {name: /read all documentation/i})).toHaveAttribute( 'href', - 'https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js/' + `${sourcemapsDocsUrl}troubleshooting_js/` ); }); });