123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- import {initializeData as _initializeData} from 'sentry-test/performance/initializePerformanceData';
- import {
- act,
- render,
- screen,
- userEvent,
- waitFor,
- within,
- } from 'sentry-test/reactTestingLibrary';
- import * as AnchorLinkManager from 'sentry/components/events/interfaces/spans/anchorLinkManager';
- import TraceView from 'sentry/components/events/interfaces/spans/traceView';
- import {spanTargetHash} from 'sentry/components/events/interfaces/spans/utils';
- import WaterfallModel from 'sentry/components/events/interfaces/spans/waterfallModel';
- import ProjectsStore from 'sentry/stores/projectsStore';
- import {EntryType, EventTransaction} from 'sentry/types';
- import {QuickTraceContext} from 'sentry/utils/performance/quickTrace/quickTraceContext';
- import QuickTraceQuery from 'sentry/utils/performance/quickTrace/quickTraceQuery';
- function initializeData(settings) {
- const data = _initializeData(settings);
- act(() => void ProjectsStore.loadInitialData(data.organization.projects));
- return data;
- }
- function generateSampleEvent(): EventTransaction {
- const event = {
- id: '2b658a829a21496b87fd1f14a61abf65',
- eventID: '2b658a829a21496b87fd1f14a61abf65',
- title: '/organizations/:orgId/discover/results/',
- type: 'transaction',
- startTimestamp: 1622079935.86141,
- endTimestamp: 1622079940.032905,
- contexts: {
- trace: {
- trace_id: '8cbbc19c0f54447ab702f00263262726',
- span_id: 'a000000000000000',
- op: 'pageload',
- status: 'unknown',
- type: 'trace',
- },
- },
- entries: [
- {
- data: [],
- type: EntryType.SPANS,
- },
- ],
- } as EventTransaction;
- return event;
- }
- function generateSampleSpan(
- description: string | null,
- op: string | null,
- span_id: string,
- parent_span_id: string,
- event: EventTransaction
- ) {
- const span = {
- start_timestamp: 1000,
- timestamp: 2000,
- description,
- op,
- span_id,
- parent_span_id,
- trace_id: '8cbbc19c0f54447ab702f00263262726',
- status: 'ok',
- tags: {
- 'http.status_code': '200',
- },
- data: {},
- };
- event.entries[0].data.push(span);
- return span;
- }
- describe('TraceView', () => {
- afterEach(() => {
- MockApiClient.clearMockResponses();
- });
- describe('Autogrouped spans tests', () => {
- it('should render siblings with the same op and description as a grouped span in the minimap and span tree', async () => {
- const data = initializeData({
- features: ['performance-autogroup-sibling-spans'],
- });
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'a000000000000000',
- event
- );
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- expect(await screen.findByTestId('minimap-sibling-group-bar')).toBeInTheDocument();
- expect(await screen.findByTestId('span-row-2')).toHaveTextContent('Autogrouped');
- expect(screen.queryByTestId('span-row-3')).not.toBeInTheDocument();
- });
- it('should expand grouped siblings when clicked, and then regroup when clicked again', async () => {
- // eslint-disable-next-line no-console
- jest.spyOn(console, 'error').mockImplementation(jest.fn());
- const data = initializeData({
- features: ['performance-autogroup-sibling-spans'],
- });
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'a000000000000000',
- event
- );
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- const groupedSiblingsSpan = await screen.findByText('Autogrouped — http —');
- userEvent.click(groupedSiblingsSpan);
- await waitFor(() =>
- expect(screen.queryByText('Autogrouped — http —')).not.toBeInTheDocument()
- );
- for (let i = 1; i < 7; i++) {
- expect(await screen.findByTestId(`span-row-${i}`)).toBeInTheDocument();
- }
- const regroupButton = await screen.findByText('Regroup');
- expect(regroupButton).toBeInTheDocument();
- userEvent.click(regroupButton);
- await waitFor(() =>
- expect(screen.queryByTestId('span-row-6')).not.toBeInTheDocument()
- );
- expect(await screen.findByText('Autogrouped — http —')).toBeInTheDocument();
- });
- it("should not group sibling spans that don't have the same op or description", async () => {
- const data = initializeData({
- features: ['performance-autogroup-sibling-spans'],
- });
- const event = generateSampleEvent();
- generateSampleSpan('test', 'http', 'b000000000000000', 'a000000000000000', event);
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'ff00000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan('test', 'http', 'fff0000000000000', 'a000000000000000', event);
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- expect(await screen.findByText('group me')).toBeInTheDocument();
- expect(await screen.findAllByText('test')).toHaveLength(2);
- });
- it('should autogroup similar nested spans', async () => {
- const data = initializeData({});
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'b000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'c000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'd000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'e000000000000000',
- event
- );
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- const grouped = await screen.findByText('group me');
- expect(grouped).toBeInTheDocument();
- });
- it('should expand/collapse only the sibling group that is clicked, even if multiple groups have the same op and description', async () => {
- const data = initializeData({features: ['performance-autogroup-sibling-spans']});
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan('not me', 'http', 'aa00000000000000', 'a000000000000000', event);
- generateSampleSpan(
- 'group me',
- 'http',
- 'bb00000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'cc00000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'dd00000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'ee00000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'ff00000000000000',
- 'a000000000000000',
- event
- );
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- expect(screen.queryAllByText('group me')).toHaveLength(2);
- const firstGroup = screen.queryAllByText('Autogrouped — http —')[0];
- userEvent.click(firstGroup);
- expect(await screen.findAllByText('group me')).toHaveLength(6);
- const secondGroup = await screen.findByText('Autogrouped — http —');
- userEvent.click(secondGroup);
- expect(await screen.findAllByText('group me')).toHaveLength(10);
- const firstRegroup = screen.queryAllByText('Regroup')[0];
- userEvent.click(firstRegroup);
- expect(await screen.findAllByText('group me')).toHaveLength(6);
- const secondRegroup = await screen.findByText('Regroup');
- userEvent.click(secondRegroup);
- expect(await screen.findAllByText('group me')).toHaveLength(2);
- });
- it('should allow expanding of embedded transactions', async () => {
- const {organization, project, location} = initializeData({
- features: ['unified-span-view'],
- });
- const event = generateSampleEvent();
- generateSampleSpan(
- 'parent span',
- 'db',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- const waterfallModel = new WaterfallModel(event);
- const eventsTraceMock = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/events-trace/${event.contexts.trace?.trace_id}/`,
- method: 'GET',
- statusCode: 200,
- body: [
- event,
- {
- errors: [],
- event_id: '998d7e2c304c45729545e4434e2967cb',
- generation: 1,
- parent_event_id: '2b658a829a21496b87fd1f14a61abf65',
- parent_span_id: 'b000000000000000',
- project_id: project.id,
- project_slug: project.slug,
- span_id: '8596e2795f88471d',
- transaction:
- '/api/0/organizations/{organization_slug}/events/{project_slug}:{event_id}/',
- 'transaction.duration': 159,
- 'transaction.op': 'http.server',
- },
- ],
- });
- const eventsTraceLightMock = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/events-trace-light/${event.contexts.trace?.trace_id}/`,
- method: 'GET',
- statusCode: 200,
- body: [
- event,
- {
- errors: [],
- event_id: '998d7e2c304c45729545e4434e2967cb',
- generation: 1,
- parent_event_id: '2b658a829a21496b87fd1f14a61abf65',
- parent_span_id: 'b000000000000000',
- project_id: project.id,
- project_slug: project.slug,
- span_id: '8596e2795f88471d',
- transaction:
- '/api/0/organizations/{organization_slug}/events/{project_slug}:{event_id}/',
- 'transaction.duration': 159,
- 'transaction.op': 'http.server',
- },
- ],
- });
- const embeddedEvent = {
- ...generateSampleEvent(),
- id: '998d7e2c304c45729545e4434e2967cb',
- eventID: '998d7e2c304c45729545e4434e2967cb',
- };
- embeddedEvent.contexts.trace!.span_id = 'a111111111111111';
- const embeddedSpan = generateSampleSpan(
- 'i am embedded :)',
- 'test',
- 'b111111111111111',
- 'b000000000000000',
- embeddedEvent
- );
- embeddedSpan.trace_id = '8cbbc19c0f54447ab702f00263262726';
- const fetchEmbeddedTransactionMock = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/events/${project.slug}:998d7e2c304c45729545e4434e2967cb/`,
- method: 'GET',
- statusCode: 200,
- body: embeddedEvent,
- });
- render(
- <QuickTraceQuery event={event} location={location} orgSlug={organization.slug}>
- {results => (
- <QuickTraceContext.Provider value={results}>
- <TraceView organization={organization} waterfallModel={waterfallModel} />
- </QuickTraceContext.Provider>
- )}
- </QuickTraceQuery>
- );
- expect(eventsTraceMock).toHaveBeenCalled();
- expect(eventsTraceLightMock).toHaveBeenCalled();
- const embeddedTransactionBadge = await screen.findByTestId(
- 'embedded-transaction-badge'
- );
- expect(embeddedTransactionBadge).toBeInTheDocument();
- userEvent.click(embeddedTransactionBadge);
- expect(fetchEmbeddedTransactionMock).toHaveBeenCalled();
- expect(await screen.findByText(/i am embedded :\)/i)).toBeInTheDocument();
- });
- it('should correctly render sibling autogroup text when op and/or description is not provided', async () => {
- const data = initializeData({
- features: ['performance-autogroup-sibling-spans'],
- });
- const event1 = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- null,
- 'b000000000000000',
- 'a000000000000000',
- event1
- );
- generateSampleSpan(
- 'group me',
- null,
- 'c000000000000000',
- 'a000000000000000',
- event1
- );
- generateSampleSpan(
- 'group me',
- null,
- 'd000000000000000',
- 'a000000000000000',
- event1
- );
- generateSampleSpan(
- 'group me',
- null,
- 'e000000000000000',
- 'a000000000000000',
- event1
- );
- generateSampleSpan(
- 'group me',
- null,
- 'f000000000000000',
- 'a000000000000000',
- event1
- );
- const {rerender} = render(
- <TraceView
- organization={data.organization}
- waterfallModel={new WaterfallModel(event1)}
- />
- );
- expect(await screen.findByTestId('span-row-2')).toHaveTextContent(
- /Autogrouped — group me/
- );
- const event2 = generateSampleEvent();
- generateSampleSpan(null, 'http', 'b000000000000000', 'a000000000000000', event2);
- generateSampleSpan(null, 'http', 'c000000000000000', 'a000000000000000', event2);
- generateSampleSpan(null, 'http', 'd000000000000000', 'a000000000000000', event2);
- generateSampleSpan(null, 'http', 'e000000000000000', 'a000000000000000', event2);
- generateSampleSpan(null, 'http', 'f000000000000000', 'a000000000000000', event2);
- rerender(
- <TraceView
- organization={data.organization}
- waterfallModel={new WaterfallModel(event2)}
- />
- );
- expect(await screen.findByTestId('span-row-2')).toHaveTextContent(
- /Autogrouped — http/
- );
- const event3 = generateSampleEvent();
- generateSampleSpan(null, null, 'b000000000000000', 'a000000000000000', event3);
- generateSampleSpan(null, null, 'c000000000000000', 'a000000000000000', event3);
- generateSampleSpan(null, null, 'd000000000000000', 'a000000000000000', event3);
- generateSampleSpan(null, null, 'e000000000000000', 'a000000000000000', event3);
- generateSampleSpan(null, null, 'f000000000000000', 'a000000000000000', event3);
- rerender(
- <TraceView
- organization={data.organization}
- waterfallModel={new WaterfallModel(event3)}
- />
- );
- expect(await screen.findByTestId('span-row-2')).toHaveTextContent(
- /Autogrouped — siblings/
- );
- });
- it('should automatically expand a sibling span group and select a span if it is anchored', async () => {
- const data = initializeData({
- features: ['performance-autogroup-sibling-spans'],
- });
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'a000000000000000',
- event
- );
- // Manually set the hash here, the AnchorLinkManager is expected to automatically expand the group and scroll to the span with this id
- location.hash = spanTargetHash('c000000000000000');
- const waterfallModel = new WaterfallModel(event);
- render(
- <AnchorLinkManager.Provider>
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- </AnchorLinkManager.Provider>
- );
- expect(await screen.findByText(/c000000000000000/i)).toBeInTheDocument();
- location.hash = '';
- });
- it('should automatically expand a descendant span group and select a span if it is anchored', async () => {
- const data = initializeData({});
- const event = generateSampleEvent();
- generateSampleSpan(
- 'group me',
- 'http',
- 'b000000000000000',
- 'a000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'c000000000000000',
- 'b000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'd000000000000000',
- 'c000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'e000000000000000',
- 'd000000000000000',
- event
- );
- generateSampleSpan(
- 'group me',
- 'http',
- 'f000000000000000',
- 'e000000000000000',
- event
- );
- location.hash = spanTargetHash('d000000000000000');
- const waterfallModel = new WaterfallModel(event);
- render(
- <AnchorLinkManager.Provider>
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- </AnchorLinkManager.Provider>
- );
- expect(await screen.findByText(/d000000000000000/i)).toBeInTheDocument();
- location.hash = '';
- });
- });
- it('should merge web vitals labels if they are too close together', () => {
- const data = initializeData({});
- const event = generateSampleEvent();
- generateSampleSpan('browser', 'test1', 'b000000000000000', 'a000000000000000', event);
- generateSampleSpan('browser', 'test2', 'c000000000000000', 'a000000000000000', event);
- generateSampleSpan('browser', 'test3', 'd000000000000000', 'a000000000000000', event);
- generateSampleSpan('browser', 'test4', 'e000000000000000', 'a000000000000000', event);
- generateSampleSpan('browser', 'test5', 'f000000000000000', 'a000000000000000', event);
- event.measurements = {
- fcp: {value: 1000},
- fp: {value: 1050},
- lcp: {value: 1100},
- };
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- const labelContainer = screen.getByText(/fcp/i).parentElement?.parentElement;
- expect(labelContainer).toBeInTheDocument();
- expect(within(labelContainer!).getByText(/fcp/i)).toBeInTheDocument();
- expect(within(labelContainer!).getByText(/fp/i)).toBeInTheDocument();
- expect(within(labelContainer!).getByText(/lcp/i)).toBeInTheDocument();
- });
- it('should not merge web vitals labels if they are spaced away from each other', () => {
- const data = initializeData({});
- const event = generateSampleEvent();
- generateSampleSpan('browser', 'test1', 'b000000000000000', 'a000000000000000', event);
- event.startTimestamp = 1;
- event.endTimestamp = 100;
- event.measurements = {
- fcp: {value: 858.3002090454102, unit: 'millisecond'},
- lcp: {value: 1000363.800048828125, unit: 'millisecond'},
- };
- const waterfallModel = new WaterfallModel(event);
- render(
- <TraceView organization={data.organization} waterfallModel={waterfallModel} />
- );
- const fcpLabelContainer = screen.getByText(/fcp/i).parentElement?.parentElement;
- expect(fcpLabelContainer).toBeInTheDocument();
- // LCP should not be merged along with FCP. We expect it to be in a separate element
- expect(within(fcpLabelContainer!).queryByText(/lcp/i)).not.toBeInTheDocument();
- const lcpLabelContainer = screen.getByText(/lcp/i).parentElement?.parentElement;
- expect(lcpLabelContainer).toBeInTheDocument();
- });
- });
|