123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991 |
- import {EventsStatsFixture} from 'sentry-fixture/events';
- import {OrganizationFixture} from 'sentry-fixture/organization';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {render, screen, waitFor} from 'sentry-test/reactTestingLibrary';
- import type {PageFilters} from 'sentry/types/core';
- import {MetricsResultsMetaProvider} from 'sentry/utils/performance/contexts/metricsEnhancedPerformanceDataContext';
- import {MEPSettingProvider} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
- import {DashboardFilterKeys, DisplayType} from 'sentry/views/dashboards/types';
- import {
- DashboardsMEPContext,
- DashboardsMEPProvider,
- } from 'sentry/views/dashboards/widgetCard/dashboardsMEPContext';
- import type {GenericWidgetQueriesChildrenProps} from 'sentry/views/dashboards/widgetCard/genericWidgetQueries';
- import WidgetQueries, {
- flattenMultiSeriesDataWithGrouping,
- } from 'sentry/views/dashboards/widgetCard/widgetQueries';
- describe('Dashboards > WidgetQueries', function () {
- const initialData = initializeOrg();
- const renderWithProviders = component =>
- render(
- <MetricsResultsMetaProvider>
- <DashboardsMEPProvider>
- <MEPSettingProvider forceTransactions={false}>{component}</MEPSettingProvider>
- </DashboardsMEPProvider>
- </MetricsResultsMetaProvider>
- );
- const multipleQueryWidget = {
- title: 'Errors',
- interval: '5m',
- displayType: DisplayType.LINE,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- name: 'errors',
- orderby: '',
- },
- {
- conditions: 'event.type:default',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- name: 'default',
- orderby: '',
- },
- ],
- };
- const singleQueryWidget = {
- title: 'Errors',
- interval: '5m',
- displayType: DisplayType.LINE,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- name: 'errors',
- orderby: '',
- },
- ],
- };
- const tableWidget = {
- title: 'SDK',
- interval: '5m',
- displayType: DisplayType.TABLE,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['sdk.name'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'sdk',
- orderby: '',
- },
- ],
- };
- const selection: PageFilters = {
- projects: [1],
- environments: ['prod'],
- datetime: {
- period: '14d',
- start: null,
- end: null,
- utc: false,
- },
- };
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- it('can send multiple API requests', async function () {
- const errorMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- match: [MockApiClient.matchQuery({query: 'event.type:error'})],
- });
- const defaultMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- match: [MockApiClient.matchQuery({query: 'event.type:default'})],
- });
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={multipleQueryWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and 2 requests should be sent.
- await screen.findByTestId('child');
- expect(errorMock).toHaveBeenCalledTimes(1);
- expect(defaultMock).toHaveBeenCalledTimes(1);
- });
- it('appends dashboard filters to events series request', async function () {
- const mock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={singleQueryWidget}
- organization={initialData.organization}
- selection={selection}
- dashboardFilters={{[DashboardFilterKeys.RELEASE]: ['abc@1.2.0', 'abc@1.3.0']}}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- await screen.findByTestId('child');
- expect(mock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({
- query: expect.objectContaining({
- query: '(event.type:error) release:["abc@1.2.0","abc@1.3.0"] ',
- }),
- })
- );
- });
- it('appends dashboard filters to events table request', async function () {
- const mock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: [],
- });
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={tableWidget}
- organization={initialData.organization}
- selection={selection}
- dashboardFilters={{[DashboardFilterKeys.RELEASE]: ['abc@1.3.0']}}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- await screen.findByTestId('child');
- expect(mock).toHaveBeenCalledWith(
- '/organizations/org-slug/events/',
- expect.objectContaining({
- query: expect.objectContaining({
- query: '(event.type:error) release:"abc@1.3.0" ',
- }),
- })
- );
- });
- it('sets errorMessage when the first request fails', async function () {
- const okMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- match: [MockApiClient.matchQuery({query: 'event.type:error'})],
- body: [],
- });
- const failMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- statusCode: 400,
- body: {detail: 'Bad request data'},
- match: [MockApiClient.matchQuery({query: 'event.type:default'})],
- });
- let error: string | undefined;
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={multipleQueryWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {({errorMessage}: {errorMessage?: string}) => {
- error = errorMessage;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- // Child should be rendered and 2 requests should be sent.
- expect(await screen.findByTestId('child')).toBeInTheDocument();
- await waitFor(() => {
- expect(error).toEqual('Bad request data');
- });
- expect(okMock).toHaveBeenCalledTimes(1);
- expect(failMock).toHaveBeenCalledTimes(1);
- });
- it('adjusts interval based on date window', async function () {
- const errorMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- const widget = {...singleQueryWidget, interval: '1m'};
- const longSelection: PageFilters = {
- projects: [1],
- environments: ['prod', 'dev'],
- datetime: {
- period: '90d',
- start: null,
- end: null,
- utc: false,
- },
- };
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={widget}
- organization={initialData.organization}
- selection={longSelection}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and interval bumped up.
- await screen.findByTestId('child');
- expect(errorMock).toHaveBeenCalledTimes(1);
- expect(errorMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({
- query: expect.objectContaining({
- interval: '4h',
- statsPeriod: '90d',
- environment: ['prod', 'dev'],
- project: [1],
- }),
- })
- );
- });
- it('adjusts interval based on date window 14d', async function () {
- const errorMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- const widget = {...singleQueryWidget, interval: '1m'};
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={widget}
- organization={initialData.organization}
- selection={selection}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and interval bumped up.
- await screen.findByTestId('child');
- expect(errorMock).toHaveBeenCalledTimes(1);
- expect(errorMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({
- query: expect.objectContaining({interval: '30m'}),
- })
- );
- });
- it('can send table result queries', async function () {
- const tableMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {'sdk.name': 'string'},
- data: [{'sdk.name': 'python'}],
- },
- });
- let childProps: GenericWidgetQueriesChildrenProps | undefined;
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={tableWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- // Child should be rendered and 1 requests should be sent.
- await screen.findByTestId('child');
- expect(tableMock).toHaveBeenCalledTimes(1);
- expect(tableMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events/',
- expect.objectContaining({
- query: expect.objectContaining({
- query: 'event.type:error',
- field: 'sdk.name',
- statsPeriod: '14d',
- environment: 'prod',
- project: '1',
- }),
- })
- );
- expect(childProps?.timeseriesResults).toBeUndefined();
- await waitFor(() => expect(childProps?.tableResults?.[0].data).toHaveLength(1));
- expect(childProps?.tableResults?.[0].meta).toBeDefined();
- });
- it('can send multiple table queries', async function () {
- const firstQuery = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {'sdk.name': 'string'},
- data: [{'sdk.name': 'python'}],
- },
- match: [MockApiClient.matchQuery({query: 'event.type:error'})],
- });
- const secondQuery = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {title: 'string'},
- data: [{title: 'ValueError'}],
- },
- match: [MockApiClient.matchQuery({query: 'title:ValueError'})],
- });
- const widget = {
- title: 'SDK',
- interval: '5m',
- displayType: DisplayType.TABLE,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['sdk.name'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'sdk',
- orderby: '',
- },
- {
- conditions: 'title:ValueError',
- fields: ['title'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'title',
- orderby: '',
- },
- ],
- };
- let childProps: GenericWidgetQueriesChildrenProps | undefined;
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={widget}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- // Child should be rendered and 2 requests should be sent.
- await screen.findByTestId('child');
- expect(firstQuery).toHaveBeenCalledTimes(1);
- expect(secondQuery).toHaveBeenCalledTimes(1);
- await waitFor(() => expect(childProps?.tableResults).toHaveLength(2));
- expect(childProps?.tableResults?.[0].data[0]['sdk.name']).toBeDefined();
- expect(childProps?.tableResults?.[1].data[0].title).toBeDefined();
- });
- it('can send big number result queries', async function () {
- const tableMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {'sdk.name': 'string'},
- data: [{'sdk.name': 'python'}],
- },
- });
- let childProps: GenericWidgetQueriesChildrenProps | undefined;
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={{
- title: 'SDK',
- interval: '5m',
- displayType: DisplayType.BIG_NUMBER,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['sdk.name'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'sdk',
- orderby: '',
- },
- ],
- }}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- // Child should be rendered and 1 requests should be sent.
- await screen.findByTestId('child');
- expect(tableMock).toHaveBeenCalledTimes(1);
- expect(tableMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events/',
- expect.objectContaining({
- query: expect.objectContaining({
- referrer: 'api.dashboards.bignumberwidget',
- query: 'event.type:error',
- field: 'sdk.name',
- statsPeriod: '14d',
- environment: 'prod',
- project: '1',
- }),
- })
- );
- expect(childProps?.timeseriesResults).toBeUndefined();
- await waitFor(() => expect(childProps?.tableResults?.[0]?.data).toHaveLength(1));
- expect(childProps?.tableResults?.[0]?.meta).toBeDefined();
- });
- it('stops loading state once all queries finish even if some fail', async function () {
- const firstQuery = MockApiClient.addMockResponse({
- statusCode: 500,
- url: '/organizations/org-slug/events/',
- body: {detail: 'it didnt work'},
- match: [MockApiClient.matchQuery({query: 'event.type:error'})],
- });
- const secondQuery = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {title: 'string'},
- data: [{title: 'ValueError'}],
- },
- match: [MockApiClient.matchQuery({query: 'title:ValueError'})],
- });
- const widget = {
- title: 'SDK',
- interval: '5m',
- displayType: DisplayType.TABLE,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['sdk.name'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'sdk',
- orderby: '',
- },
- {
- conditions: 'title:ValueError',
- fields: ['sdk.name'],
- aggregates: [],
- columns: ['sdk.name'],
- name: 'title',
- orderby: '',
- },
- ],
- };
- let childProps: GenericWidgetQueriesChildrenProps | undefined;
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={widget}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- // Child should be rendered and 2 requests should be sent.
- await screen.findByTestId('child');
- expect(firstQuery).toHaveBeenCalledTimes(1);
- expect(secondQuery).toHaveBeenCalledTimes(1);
- await waitFor(() => expect(childProps?.loading).toEqual(false));
- });
- it('sets bar charts to 1d interval', async function () {
- const errorMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- match: [MockApiClient.matchQuery({interval: '1d'})],
- });
- const barWidget = {
- ...singleQueryWidget,
- displayType: DisplayType.BAR,
- // Should be ignored for bars.
- interval: '5m',
- };
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={barWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and 1 requests should be sent.
- await screen.findByTestId('child');
- expect(errorMock).toHaveBeenCalledTimes(1);
- });
- it('returns timeseriesResults in the same order as widgetQuery', async function () {
- MockApiClient.clearMockResponses();
- const defaultMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- method: 'GET',
- body: {
- data: [
- [
- 1000,
- [
- {
- count: 100,
- },
- ],
- ],
- ],
- start: 1000,
- end: 2000,
- },
- match: [MockApiClient.matchQuery({query: 'event.type:default'})],
- });
- const errorMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- method: 'GET',
- body: {
- data: [
- [
- 1000,
- [
- {
- count: 200,
- },
- ],
- ],
- ],
- start: 1000,
- end: 2000,
- },
- match: [MockApiClient.matchQuery({query: 'event.type:error'})],
- });
- const barWidget = {
- ...multipleQueryWidget,
- displayType: DisplayType.BAR,
- // Should be ignored for bars.
- interval: '5m',
- };
- const child = jest.fn(() => <div data-test-id="child" />);
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={barWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {child}
- </WidgetQueries>
- );
- await screen.findByTestId('child');
- expect(defaultMock).toHaveBeenCalledTimes(1);
- expect(errorMock).toHaveBeenCalledTimes(1);
- await waitFor(() =>
- expect(child).toHaveBeenLastCalledWith(
- expect.objectContaining({
- timeseriesResults: [
- {data: [{name: 1000000, value: 200}], seriesName: 'errors : count()'},
- {data: [{name: 1000000, value: 100}], seriesName: 'default : count()'},
- ],
- })
- )
- );
- });
- it('calls events-stats with 4h interval when interval buckets would exceed 66', async function () {
- const eventsStatsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- const areaWidget = {
- ...singleQueryWidget,
- displayType: DisplayType.AREA,
- interval: '5m',
- };
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={areaWidget}
- organization={initialData.organization}
- selection={{
- ...selection,
- datetime: {
- period: '90d',
- start: null,
- end: null,
- utc: false,
- },
- }}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and 1 requests should be sent.
- await screen.findByTestId('child');
- expect(eventsStatsMock).toHaveBeenCalledTimes(1);
- expect(eventsStatsMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({query: expect.objectContaining({interval: '4h'})})
- );
- });
- it('does not re-query events and sets name in widgets', async function () {
- const eventsStatsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: EventsStatsFixture(),
- });
- const lineWidget = {
- ...singleQueryWidget,
- displayType: DisplayType.LINE,
- interval: '5m',
- };
- let childProps;
- const {rerender} = renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={lineWidget}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- );
- expect(eventsStatsMock).toHaveBeenCalledTimes(1);
- await waitFor(() => expect(childProps.loading).toEqual(false));
- // Simulate a re-render with a new query alias
- rerender(
- <MetricsResultsMetaProvider>
- <DashboardsMEPProvider>
- <MEPSettingProvider forceTransactions={false}>
- <WidgetQueries
- api={new MockApiClient()}
- widget={{
- ...lineWidget,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- name: 'this query alias changed',
- orderby: '',
- },
- ],
- }}
- organization={initialData.organization}
- selection={selection}
- >
- {props => {
- childProps = props;
- return <div data-test-id="child" />;
- }}
- </WidgetQueries>
- </MEPSettingProvider>
- </DashboardsMEPProvider>
- </MetricsResultsMetaProvider>
- );
- // Did not re-query
- expect(eventsStatsMock).toHaveBeenCalledTimes(1);
- expect(childProps.timeseriesResults[0].seriesName).toEqual(
- 'this query alias changed : count()'
- );
- });
- describe('multi-series grouped data', () => {
- const [START, END] = [1647399900, 1647399901];
- let mockCountData, mockCountUniqueData, mockRawResultData;
- beforeEach(() => {
- mockCountData = {
- start: START,
- end: END,
- data: [
- [START, [{'count()': 0}]],
- [END, [{'count()': 0}]],
- ],
- };
- mockCountUniqueData = {
- start: START,
- end: END,
- data: [
- [START, [{'count_unique()': 0}]],
- [END, [{'count_unique()': 0}]],
- ],
- };
- mockRawResultData = {
- local: {
- 'count()': mockCountData,
- 'count_unique()': mockCountUniqueData,
- order: 0,
- },
- prod: {
- 'count()': mockCountData,
- 'count_unique()': mockCountUniqueData,
- order: 1,
- },
- };
- });
- it('combines group name and aggregate names in grouped multi series data', () => {
- const actual = flattenMultiSeriesDataWithGrouping(mockRawResultData, '');
- expect(actual).toEqual([
- [
- 0,
- expect.objectContaining({
- seriesName: 'local : count()',
- data: expect.anything(),
- }),
- ],
- [
- 0,
- expect.objectContaining({
- seriesName: 'local : count_unique()',
- data: expect.anything(),
- }),
- ],
- [
- 1,
- expect.objectContaining({
- seriesName: 'prod : count()',
- data: expect.anything(),
- }),
- ],
- [
- 1,
- expect.objectContaining({
- seriesName: 'prod : count_unique()',
- data: expect.anything(),
- }),
- ],
- ]);
- });
- it('prefixes with a query alias when provided', () => {
- const actual = flattenMultiSeriesDataWithGrouping(mockRawResultData, 'Query 1');
- expect(actual).toEqual([
- [
- 0,
- expect.objectContaining({
- seriesName: 'Query 1 > local : count()',
- data: expect.anything(),
- }),
- ],
- [
- 0,
- expect.objectContaining({
- seriesName: 'Query 1 > local : count_unique()',
- data: expect.anything(),
- }),
- ],
- [
- 1,
- expect.objectContaining({
- seriesName: 'Query 1 > prod : count()',
- data: expect.anything(),
- }),
- ],
- [
- 1,
- expect.objectContaining({
- seriesName: 'Query 1 > prod : count_unique()',
- data: expect.anything(),
- }),
- ],
- ]);
- });
- });
- it('charts send metricsEnhanced requests', async function () {
- const {organization} = initialData;
- const mock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: {
- data: [
- [
- 1000,
- [
- {
- count: 100,
- },
- ],
- ],
- ],
- isMetricsData: false,
- start: 1000,
- end: 2000,
- },
- });
- const setIsMetricsMock = jest.fn();
- const children = jest.fn(() => <div />);
- renderWithProviders(
- <DashboardsMEPContext.Provider
- value={{
- isMetricsData: undefined,
- setIsMetricsData: setIsMetricsMock,
- }}
- >
- <WidgetQueries
- api={new MockApiClient()}
- widget={singleQueryWidget}
- organization={{
- ...organization,
- features: [...organization.features, 'dashboards-mep'],
- }}
- selection={selection}
- >
- {children}
- </WidgetQueries>
- </DashboardsMEPContext.Provider>
- );
- expect(mock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({
- query: expect.objectContaining({dataset: 'metricsEnhanced'}),
- })
- );
- await waitFor(() => {
- expect(setIsMetricsMock).toHaveBeenCalledWith(false);
- });
- });
- it('tables send metricsEnhanced requests', async function () {
- const {organization} = initialData;
- const mock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events/',
- body: {
- meta: {title: 'string', isMetricsData: true},
- data: [{title: 'ValueError'}],
- },
- });
- const setIsMetricsMock = jest.fn();
- const children = jest.fn(() => <div />);
- renderWithProviders(
- <DashboardsMEPContext.Provider
- value={{
- isMetricsData: undefined,
- setIsMetricsData: setIsMetricsMock,
- }}
- >
- <WidgetQueries
- api={new MockApiClient()}
- widget={{...singleQueryWidget, displayType: DisplayType.TABLE}}
- organization={{
- ...organization,
- features: [...organization.features, 'dashboards-mep'],
- }}
- selection={selection}
- >
- {children}
- </WidgetQueries>
- </DashboardsMEPContext.Provider>
- );
- expect(mock).toHaveBeenCalledWith(
- '/organizations/org-slug/events/',
- expect.objectContaining({
- query: expect.objectContaining({dataset: 'metricsEnhanced'}),
- })
- );
- await waitFor(() => {
- expect(setIsMetricsMock).toHaveBeenCalledWith(true);
- });
- });
- it('does not inject equation aliases for top N requests', async function () {
- const testData = initializeOrg({
- organization: {
- ...OrganizationFixture(),
- },
- });
- const eventsStatsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- const areaWidget = {
- title: 'Errors',
- displayType: DisplayType.AREA,
- interval: '5m',
- queries: [
- {
- conditions: 'event.type:error',
- fields: [],
- aggregates: ['count()', 'equation|count() * 2'],
- columns: ['project'],
- orderby: 'equation[0]',
- name: '',
- },
- ],
- };
- renderWithProviders(
- <WidgetQueries
- api={new MockApiClient()}
- widget={areaWidget}
- organization={testData.organization}
- selection={selection}
- >
- {() => <div data-test-id="child" />}
- </WidgetQueries>
- );
- // Child should be rendered and 1 requests should be sent.
- await screen.findByTestId('child');
- expect(eventsStatsMock).toHaveBeenCalledTimes(1);
- expect(eventsStatsMock).toHaveBeenCalledWith(
- '/organizations/org-slug/events-stats/',
- expect.objectContaining({
- query: expect.objectContaining({
- field: ['project', 'count()', 'equation|count() * 2'],
- orderby: 'equation[0]',
- }),
- })
- );
- });
- });
|