import {initializeOrg} from 'sentry-test/initializeOrg'; import {render, screen} from 'sentry-test/reactTestingLibrary'; import QuickTrace from 'sentry/components/quickTrace'; import {Event} from 'sentry/types/event'; import {QuickTraceEvent} from 'sentry/utils/performance/quickTrace/types'; describe('Quick Trace', function () { let location; let organization; const initialize = () => { const context = initializeOrg(); organization = context.organization; }; function makeQuickTraceEvents(generation, {n = 1, parentId = null} = {}) { const events: QuickTraceEvent[] = []; for (let i = 0; i < n; i++) { const suffix = n > 1 ? `-${i}` : ''; events.push({ event_id: `e${generation}${suffix}`, generation, span_id: `s${generation}${suffix}`, transaction: `t${generation}${suffix}`, 'transaction.duration': 1234, project_id: generation, project_slug: `p${generation}`, parent_event_id: generation === 0 ? null : parentId === null ? `e${generation - 1}` : parentId, parent_span_id: generation === 0 ? null : parentId === null ? `s${generation - 1}${parentId}` : `s${parentId}`, }); } return events; } function makeTransactionEvent(id) { return { id: `e${id}`, type: 'transaction', startTimestamp: 1615921516.132774, endTimestamp: 1615921517.924861, }; } function makeTransactionHref( pid: string, eid: string, transaction: string, project: string ) { return `/organizations/${organization.slug}/performance/${pid}:${eid}/?project=${project}&transaction=${transaction}`; } beforeEach(function () { initialize(); location = { pathname: '/', query: {}, }; }); describe('Empty Trace', function () { it('renders nothing for empty trace', function () { const {container} = render( ); expect(container).toHaveTextContent('\u2014'); }); }); describe('Partial Trace', function () { it('renders nothing when partial trace is empty', function () { const {container} = render( ); expect(container).toHaveTextContent('\u2014'); }); it('renders nothing when partial trace missing current event', function () { const {container} = render( ); expect(container).toHaveTextContent('\u2014'); }); // TODO it('renders partial trace with no children', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(1); expect(nodes[0]).toHaveTextContent('This Event'); }); it('renders partial trace with single child', async function () { render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(2); ['This Event', '1 Child'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders partial trace with multiple children', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(2); ['This Event', '3 Children'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders full trace with root as parent', async function () { render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(2); ['Parent', 'This Event'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); }); describe('Full Trace', function () { it('renders full trace with single ancestor', async function () { render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(4); ['Root', '1 Ancestor', 'Parent', 'This Event'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders full trace with multiple ancestors', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(4); ['Root', '3 Ancestors', 'Parent', 'This Event'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders full trace with single descendant', async function () { render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(3); ['This Event', '1 Child', '1 Descendant'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders full trace with multiple descendants', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(3); ['This Event', '1 Child', '3 Descendants'].forEach((text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); it('renders full trace', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(6); ['Root', '3 Ancestors', 'Parent', 'This Event', '1 Child', '3 Descendants'].forEach( (text, i) => expect(nodes[i]).toHaveTextContent(text) ); }); }); describe('Event Node Clicks', function () { it('renders single event targets', async function () { const routerContext = TestStubs.routerContext(); render( , {context: routerContext} ); const nodes = await screen.findAllByTestId('event-node'); expect(nodes.length).toEqual(6); [ makeTransactionHref('p0', 'e0', 't0', '0'), makeTransactionHref('p1', 'e1', 't1', '1'), makeTransactionHref('p2', 'e2', 't2', '2'), undefined, // the "This Event" node has no target makeTransactionHref('p4', 'e4', 't4', '4'), makeTransactionHref('p5', 'e5', 't5', '5'), ].forEach((target, i) => { const linkNode = nodes[i].children[0]; if (target) { expect(linkNode).toHaveAttribute('href', target); } else { expect(linkNode).not.toHaveAttribute('href'); } }); }); it('renders multiple event targets', async function () { MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/projects/`, body: [], }); render( ); const items = await screen.findAllByTestId('dropdown-item'); expect(items.length).toEqual(3); // can't easily assert the target is correct since it uses an onClick handler }); }); });