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
});
});
});