123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- import {LocationFixture} from 'sentry-fixture/locationFixture';
- import {OrganizationFixture} from 'sentry-fixture/organization';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {generateSuspectSpansResponse} from 'sentry-test/performance/initializePerformanceData';
- import {
- act,
- render,
- screen,
- waitForElementToBeRemoved,
- within,
- } from 'sentry-test/reactTestingLibrary';
- import ProjectsStore from 'sentry/stores/projectsStore';
- import {useLocation} from 'sentry/utils/useLocation';
- import TransactionSpans from 'sentry/views/performance/transactionSummary/transactionSpans';
- import {
- SpanSortOthers,
- SpanSortPercentiles,
- } from 'sentry/views/performance/transactionSummary/transactionSpans/types';
- jest.mock('sentry/utils/useLocation');
- const mockUseLocation = jest.mocked(useLocation);
- function initializeData(options: {query: {}; additionalFeatures?: string[]}) {
- const {query, additionalFeatures} = options;
- const defaultFeatures = ['performance-view'];
- const organization = OrganizationFixture({
- features: [...defaultFeatures, ...(additionalFeatures ? additionalFeatures : [])],
- });
- const initialData = initializeOrg({
- organization,
- router: {
- location: {
- query: {
- transaction: 'Test Transaction',
- project: '1',
- ...query,
- },
- },
- },
- });
- act(() => void ProjectsStore.loadInitialData(initialData.projects));
- return initialData;
- }
- describe('Performance > Transaction Spans', function () {
- let eventsMock: jest.Mock;
- let eventsSpanOpsMock: jest.Mock;
- let eventsSpansPerformanceMock: jest.Mock;
- beforeEach(function () {
- mockUseLocation.mockReturnValue(
- LocationFixture({pathname: '/organizations/org-slug/performance/summary'})
- );
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/prompts-activity/',
- body: {},
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/sdk-updates/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-has-measurements/',
- body: {measurements: false},
- });
- eventsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: [{'count()': 100}],
- });
- eventsSpanOpsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-span-ops/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/replay-count/',
- body: {},
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/spans/fields/',
- body: [],
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- ProjectsStore.reset();
- });
- describe('Without Span Data', function () {
- beforeEach(function () {
- eventsSpansPerformanceMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-spans-performance/',
- body: [],
- });
- });
- it('renders empty state', async function () {
- const initialData = initializeData({
- query: {sort: SpanSortOthers.SUM_EXCLUSIVE_TIME},
- });
- render(<TransactionSpans location={initialData.router.location} />, {
- router: initialData.router,
- organization: initialData.organization,
- });
- expect(
- await screen.findByText('No results found for your query')
- ).toBeInTheDocument();
- });
- });
- describe('With Span Data', function () {
- beforeEach(function () {
- eventsSpansPerformanceMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-spans-performance/',
- body: generateSuspectSpansResponse({examples: 0}),
- });
- });
- it('renders basic UI elements', async function () {
- const initialData = initializeData({
- query: {sort: SpanSortOthers.SUM_EXCLUSIVE_TIME},
- });
- render(<TransactionSpans location={initialData.router.location} />, {
- router: initialData.router,
- organization: initialData.organization,
- });
- // default visible columns
- const grid = await screen.findByTestId('grid-editable');
- expect(await within(grid).findByText('Span Operation')).toBeInTheDocument();
- expect(await within(grid).findByText('Span Name')).toBeInTheDocument();
- expect(await within(grid).findByText('Frequency')).toBeInTheDocument();
- expect(await within(grid).findByText('P75 Self Time')).toBeInTheDocument();
- expect(await within(grid).findByText('Total Self Time')).toBeInTheDocument();
- // there should be a row for each of the spans
- expect(await within(grid).findByText('op1')).toBeInTheDocument();
- expect(await within(grid).findByText('op2')).toBeInTheDocument();
- expect(eventsMock).toHaveBeenCalledTimes(1);
- expect(eventsSpanOpsMock).toHaveBeenCalledTimes(1);
- expect(eventsSpansPerformanceMock).toHaveBeenCalledTimes(1);
- });
- [
- {sort: SpanSortPercentiles.P50_EXCLUSIVE_TIME, label: 'P50 Self Time'},
- {sort: SpanSortPercentiles.P75_EXCLUSIVE_TIME, label: 'P75 Self Time'},
- {sort: SpanSortPercentiles.P95_EXCLUSIVE_TIME, label: 'P95 Self Time'},
- {sort: SpanSortPercentiles.P99_EXCLUSIVE_TIME, label: 'P99 Self Time'},
- ].forEach(({sort, label}) => {
- it('renders the right percentile header', async function () {
- const initialData = initializeData({query: {sort}});
- render(<TransactionSpans location={initialData.router.location} />, {
- router: initialData.router,
- organization: initialData.organization,
- });
- const grid = await screen.findByTestId('grid-editable');
- expect(await within(grid).findByText('Span Operation')).toBeInTheDocument();
- expect(await within(grid).findByText('Span Name')).toBeInTheDocument();
- expect(await within(grid).findByText('Frequency')).toBeInTheDocument();
- expect(await within(grid).findByText(label)).toBeInTheDocument();
- expect(await within(grid).findByText('Total Self Time')).toBeInTheDocument();
- });
- });
- it('renders the right avg occurrence header', async function () {
- const initialData = initializeData({query: {sort: SpanSortOthers.AVG_OCCURRENCE}});
- render(<TransactionSpans location={initialData.router.location} />, {
- router: initialData.router,
- organization: initialData.organization,
- });
- const grid = await screen.findByTestId('grid-editable');
- expect(await within(grid).findByText('Span Operation')).toBeInTheDocument();
- expect(await within(grid).findByText('Span Name')).toBeInTheDocument();
- expect(await within(grid).findByText('Average Occurrences')).toBeInTheDocument();
- expect(await within(grid).findByText('Frequency')).toBeInTheDocument();
- expect(await within(grid).findByText('P75 Self Time')).toBeInTheDocument();
- expect(await within(grid).findByText('Total Self Time')).toBeInTheDocument();
- });
- });
- describe('Spans Tab V2', function () {
- it('does not propagate transaction search query and properly tokenizes span query', async function () {
- const initialData = initializeData({
- query: {query: 'http.method:POST', spansQuery: 'span.op:db span.action:SELECT'},
- additionalFeatures: [
- 'performance-view',
- 'performance-spans-new-ui',
- 'insights-initial-modules',
- ],
- });
- render(<TransactionSpans location={initialData.router.location} />, {
- router: initialData.router,
- organization: initialData.organization,
- });
- await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
- const searchTokens = await screen.findAllByTestId('filter-token');
- expect(searchTokens).toHaveLength(2);
- expect(searchTokens[0]).toHaveTextContent('span.op:db');
- expect(searchTokens[1]).toHaveTextContent('span.action:SELECT');
- expect(await screen.findByTestId('smart-search-bar')).not.toHaveTextContent(
- 'http.method:POST'
- );
- });
- });
- });
|