import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import {initializeOrg} from 'sentry-test/initializeOrg'; import {act, fireEvent, render, screen} from 'sentry-test/reactTestingLibrary'; import ProjectsStore from 'sentry/stores/projectsStore'; import type {Project} from 'sentry/types/project'; import type {Field} from 'sentry/views/traces/data'; import { ProjectRenderer, ProjectsRenderer, SpanDescriptionRenderer, SpanIdRenderer, TraceIdRenderer, TraceIssuesRenderer, TransactionRenderer, } from 'sentry/views/traces/fieldRenderers'; import type {SpanResult} from 'sentry/views/traces/hooks/useTraceSpans'; describe('Renderers', function () { let context: ReturnType; const organization = OrganizationFixture({ features: ['trace-view-v1'], // only testing against new trace view }); const projects = [ ProjectFixture({ id: '1', slug: 'project-1', name: 'Project 1', }), ProjectFixture({ id: '2', slug: 'project-2', name: 'Project 2', }), ProjectFixture({ id: '3', slug: 'project-3', name: 'Project 3', }), ]; function makeSpan( project: Project, span?: Partial> ): SpanResult { return { project: project.slug, 'transaction.id': '00000000000000000000000000000000', id: '11111111111111111111111111111111', timestamp: '2024-07-03T10:15:00', 'sdk.name': 'sentry.python', 'span.op': 'op', 'span.description': 'description', 'span.duration': 123.456, 'span.status': 'internal_error', 'span.self_time': 12.34, 'precise.start_ts': 1720015976.544, 'precise.finish_ts': 1720016100.0, is_transaction: 1, ...span, }; } beforeEach(function () { context = initializeOrg({organization, projects}); act(() => ProjectsStore.loadInitialData(projects)); MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: projects, }); }); describe('SpanDescriptionRenderer', function () { it('renders op then description', function () { const span = makeSpan(projects[0]); render(); const description = screen.getByTestId('span-description'); expect(description).toBeInTheDocument(); expect(description).toHaveTextContent('op\u2014descriptioninternal_error'); }); it.each(['unknown', 'foobar'])( 'does not render span status %s', function (spanStatus) { const span = makeSpan(projects[0], {'span.status': spanStatus}); render(); const description = screen.getByTestId('span-description'); expect(description).toBeInTheDocument(); expect(description).toHaveTextContent('op\u2014description'); } ); it.each(['ok', 'internal_error'])('renders span status %s', function (spanStatus) { const span = makeSpan(projects[0], {'span.status': spanStatus}); render(); const description = screen.getByTestId('span-description'); expect(description).toBeInTheDocument(); expect(description).toHaveTextContent(`op\u2014description${spanStatus}`); }); }); describe('ProjectsRenderer', function () { it('renders one project', function () { render(, context); expect(screen.getAllByRole('img')).toHaveLength(1); }); it('renders two projects', function () { render( , context ); expect(screen.getAllByRole('img')).toHaveLength(2); }); it('renders three projects', function () { render( p.slug)} />, context); expect(screen.getAllByRole('img')).toHaveLength(1); const collapsed = screen.getByTestId('collapsed-projects-badge'); expect(collapsed).toBeInTheDocument(); expect(collapsed).toHaveTextContent('+2'); }); }); describe('ProjectRenderer', function () { it('renders project badge with name', function () { render(); expect(screen.getByRole('img')).toBeInTheDocument(); expect(screen.getByText(projects[0].slug)).toBeInTheDocument(); }); }); describe('SpanIdRenderer', function () { it('renders span id with link', function () { const onClickHandler = jest.fn(); const traceId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const span = makeSpan(projects[0]); render( , context ); const link = screen.getByText('11111111'); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute( 'href', `/organizations/${organization.slug}/performance/trace/${traceId}/?eventId=${span['transaction.id']}&node=span-${span.id}&node=txn-${span['transaction.id']}&source=traces&statsPeriod=14d×tamp=1720016100` ); expect(onClickHandler).toHaveBeenCalledTimes(0); fireEvent.click(link); expect(onClickHandler).toHaveBeenCalledTimes(1); }); }); describe('TraceIdRenderer', function () { it('renders trace id with link', function () { const onClickHandler = jest.fn(); const traceId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; render( , context ); const link = screen.getByText('aaaaaaaa'); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute( 'href', `/organizations/${organization.slug}/performance/trace/${traceId}/?pageEnd&pageStart&source=traces&statsPeriod=14d×tamp=1720016100` ); expect(onClickHandler).toHaveBeenCalledTimes(0); fireEvent.click(link); expect(onClickHandler).toHaveBeenCalledTimes(1); }); }); describe('TransactionRenderer', function () { it('renders transaction with link', function () { render(); const link = screen.getByText('foobar'); expect(link).toBeInTheDocument(); expect(link).toHaveAttribute( 'href', `/organizations/${organization.slug}/performance/summary/?project=${projects[0].id}&referrer=performance-transaction-summary&transaction=foobar&unselectedSeries=p100%28%29&unselectedSeries=avg%28%29` ); }); }); describe('TraceIssuesRenderer', function () { it('renders 0 issues', function () { render( , context ); const link = screen.getByRole('button'); expect(link).toBeInTheDocument(); expect(link).toHaveTextContent('\u2014'); expect(link).toBeDisabled(); }); it('renders 99+ issues', function () { render( , context ); const link = screen.getByRole('button'); expect(link).toBeInTheDocument(); expect(link).toHaveTextContent('99+'); expect(link).toBeEnabled(); }); it('renders N issues', function () { render( , context ); const link = screen.getByRole('button'); expect(link).toBeInTheDocument(); expect(link).toHaveTextContent('10'); expect(link).toBeEnabled(); }); }); });