123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- import {
- DashboardDetails,
- DisplayType,
- Widget,
- WidgetType,
- } from 'sentry/views/dashboards/types';
- import {
- constructWidgetFromQuery,
- eventViewFromWidget,
- flattenErrors,
- getDashboardsMEPQueryParams,
- getFieldsFromEquations,
- getNumEquations,
- getWidgetDiscoverUrl,
- getWidgetIssueUrl,
- hasUnsavedFilterChanges,
- isCustomMeasurementWidget,
- isWidgetUsingTransactionName,
- } from 'sentry/views/dashboards/utils';
- describe('Dashboards util', () => {
- const selection = {
- datetime: {
- period: '7d',
- utc: null,
- start: null,
- end: null,
- },
- environments: [],
- projects: [],
- };
- describe('constructWidgetFromQuery', () => {
- let baseQuery;
- beforeEach(() => {
- baseQuery = {
- displayType: 'line',
- interval: '5m',
- queryConditions: ['title:test', 'event.type:test'],
- queryFields: ['count()', 'failure_count()'],
- queryAggregates: ['count()', 'failure_count()'],
- queryColumns: [],
- queryNames: ['1', '2'],
- queryOrderby: '',
- title: 'Widget Title',
- };
- });
- it('returns a widget when given a valid query', () => {
- const widget = constructWidgetFromQuery(baseQuery);
- expect(widget?.displayType).toEqual(DisplayType.LINE);
- expect(widget?.interval).toEqual('5m');
- expect(widget?.title).toEqual('Widget Title');
- expect(widget?.queries).toEqual([
- {
- name: '1',
- fields: ['count()', 'failure_count()'],
- aggregates: ['count()', 'failure_count()'],
- columns: [],
- conditions: 'title:test',
- orderby: '',
- },
- {
- name: '2',
- fields: ['count()', 'failure_count()'],
- aggregates: ['count()', 'failure_count()'],
- columns: [],
- conditions: 'event.type:test',
- orderby: '',
- },
- ]);
- expect(widget?.widgetType).toEqual('discover');
- });
- it('returns undefined if query is missing title', () => {
- baseQuery.title = '';
- const widget = constructWidgetFromQuery(baseQuery);
- expect(widget).toBeUndefined();
- });
- it('returns undefined if query is missing interval', () => {
- baseQuery.interval = '';
- const widget = constructWidgetFromQuery(baseQuery);
- expect(widget).toBeUndefined();
- });
- it('returns undefined if query is missing displayType', () => {
- baseQuery.displayType = '';
- const widget = constructWidgetFromQuery(baseQuery);
- expect(widget).toBeUndefined();
- });
- it('returns a widget when given string fields and conditions', () => {
- baseQuery.queryConditions = 'title:test';
- baseQuery.queryFields = 'count()';
- baseQuery.queryAggregates = 'count()';
- const widget = constructWidgetFromQuery(baseQuery);
- expect(widget?.displayType).toEqual(DisplayType.LINE);
- expect(widget?.interval).toEqual('5m');
- expect(widget?.title).toEqual('Widget Title');
- expect(widget?.queries).toEqual([
- {
- name: '1',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- conditions: 'title:test',
- orderby: '',
- },
- ]);
- });
- });
- describe('eventViewFromWidget', () => {
- let widget;
- beforeEach(() => {
- widget = {
- title: 'Test Query',
- displayType: DisplayType.WORLD_MAP,
- widgetType: WidgetType.DISCOVER,
- interval: '5m',
- queries: [
- {
- name: '',
- conditions: '',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- orderby: '',
- },
- ],
- };
- });
- it('attaches a geo.country_code condition and field to a World Map widget if it does not already have one', () => {
- const eventView = eventViewFromWidget(
- widget.title,
- widget.queries[0],
- selection,
- widget.displayType
- );
- expect(eventView.fields[0].field).toEqual('geo.country_code');
- expect(eventView.fields[1].field).toEqual('count()');
- expect(eventView.query).toEqual('has:geo.country_code');
- });
- it('does not attach geo.country_code condition and field to a World Map widget if it already has one', () => {
- widget.queries.fields = ['geo.country_code', 'count()'];
- widget.conditions = 'has:geo.country_code';
- const eventView = eventViewFromWidget(
- widget.title,
- widget.queries[0],
- selection,
- widget.displayType
- );
- expect(eventView.fields[0].field).toEqual('geo.country_code');
- expect(eventView.fields[1].field).toEqual('count()');
- expect(eventView.query).toEqual('has:geo.country_code');
- });
- it('handles sorts in function format', () => {
- const query = {...widget.queries[0], orderby: '-count()'};
- const eventView = eventViewFromWidget(
- widget.title,
- query,
- selection,
- widget.displayType
- );
- expect(eventView.fields[0].field).toEqual('geo.country_code');
- expect(eventView.fields[1].field).toEqual('count()');
- expect(eventView.query).toEqual('has:geo.country_code');
- expect(eventView.sorts).toEqual([{field: 'count', kind: 'desc'}]);
- });
- });
- describe('getFieldsFromEquations', function () {
- it('returns a list of fields that includes individual terms of provided equations', () => {
- const fields = [
- 'equation|(count_if(transaction.duration,greater,300) / count()) * 100',
- 'equation|(count_if(transaction.duration,lessOrEquals,300) / count()) * 100',
- ];
- expect(getFieldsFromEquations(fields)).toEqual(
- expect.arrayContaining([
- 'count_if(transaction.duration,lessOrEquals,300)',
- 'count()',
- 'count_if(transaction.duration,greater,300)',
- ])
- );
- });
- });
- describe('getWidgetDiscoverUrl', function () {
- let widget;
- beforeEach(() => {
- widget = {
- title: 'Test Query',
- displayType: DisplayType.LINE,
- widgetType: WidgetType.DISCOVER,
- interval: '5m',
- queries: [
- {
- name: '',
- conditions: '',
- fields: ['count()'],
- aggregates: ['count()'],
- columns: [],
- orderby: '',
- },
- ],
- };
- });
- it('returns the discover url of the widget query', () => {
- const url = getWidgetDiscoverUrl(widget, selection, TestStubs.Organization());
- expect(url).toEqual(
- '/organizations/org-slug/discover/results/?field=count%28%29&name=Test%20Query&query=&statsPeriod=7d&yAxis=count%28%29'
- );
- });
- it('returns the discover url of a topn widget query', () => {
- widget = {
- ...widget,
- ...{
- displayType: DisplayType.TOP_N,
- queries: [
- {
- name: '',
- conditions: 'error.unhandled:true',
- fields: ['error.type', 'count()'],
- aggregates: ['count()'],
- columns: ['error.type'],
- orderby: '-count',
- },
- ],
- },
- };
- const url = getWidgetDiscoverUrl(widget, selection, TestStubs.Organization());
- expect(url).toEqual(
- '/organizations/org-slug/discover/results/?display=top5&field=error.type&field=count%28%29&name=Test%20Query&query=error.unhandled%3Atrue&sort=-count&statsPeriod=7d&yAxis=count%28%29'
- );
- });
- });
- describe('getWidgetIssueUrl', function () {
- let widget;
- beforeEach(() => {
- widget = {
- title: 'Test Query',
- displayType: DisplayType.TABLE,
- widgetType: WidgetType.ISSUE,
- interval: '5m',
- queries: [
- {
- name: '',
- conditions: 'is:unresolved',
- fields: ['events'],
- orderby: 'date',
- },
- ],
- };
- });
- it('returns the issue url of the widget query', () => {
- const url = getWidgetIssueUrl(widget, selection, TestStubs.Organization());
- expect(url).toEqual(
- '/organizations/org-slug/issues/?query=is%3Aunresolved&sort=date&statsPeriod=7d'
- );
- });
- });
- describe('flattenErrors', function () {
- it('flattens nested errors', () => {
- const errorResponse = {
- widgets: [
- {
- title: ['Ensure this field has no more than 3 characters.'],
- },
- ],
- };
- expect(flattenErrors(errorResponse, {})).toEqual({
- title: 'Ensure this field has no more than 3 characters.',
- });
- });
- it('does not spread error strings', () => {
- const errorResponse = 'Dashboard title already taken.';
- expect(flattenErrors(errorResponse, {})).toEqual({
- error: 'Dashboard title already taken.',
- });
- });
- });
- describe('getDashboardsMEPQueryParams', function () {
- it('returns correct params if enabled', function () {
- expect(getDashboardsMEPQueryParams(true)).toEqual({
- dataset: 'metricsEnhanced',
- });
- });
- it('returns empty object if disabled', function () {
- expect(getDashboardsMEPQueryParams(false)).toEqual({});
- });
- });
- describe('getNumEquations', function () {
- it('returns 0 if there are no equations', function () {
- expect(getNumEquations(['count()', 'epm()', 'count_unique(user)'])).toBe(0);
- });
- it('returns the count of equations if there are multiple', function () {
- expect(
- getNumEquations([
- 'count()',
- 'equation|count_unique(user) * 2',
- 'count_unique(user)',
- 'equation|count_unique(user) * 3',
- ])
- ).toBe(2);
- });
- it('returns 0 if the possible equations array is empty', function () {
- expect(getNumEquations([])).toBe(0);
- });
- });
- describe('isCustomMeasurementWidget', function () {
- it('returns false on a non custom measurement widget', function () {
- const widget: Widget = {
- title: 'Title',
- interval: '5m',
- displayType: DisplayType.LINE,
- widgetType: WidgetType.DISCOVER,
- queries: [
- {
- conditions: '',
- fields: [],
- aggregates: ['count()', 'p99(measurements.lcp)'],
- columns: [],
- name: 'widget',
- orderby: '',
- },
- ],
- };
- expect(isCustomMeasurementWidget(widget)).toBe(false);
- });
- it('returns true on a custom measurement widget', function () {
- const widget: Widget = {
- title: 'Title',
- interval: '5m',
- displayType: DisplayType.LINE,
- widgetType: WidgetType.DISCOVER,
- queries: [
- {
- conditions: '',
- fields: [],
- aggregates: ['p99(measurements.custom.measurement)'],
- columns: [],
- name: 'widget',
- orderby: '',
- },
- ],
- };
- expect(isCustomMeasurementWidget(widget)).toBe(true);
- });
- });
- describe('hasUnsavedFilterChanges', function () {
- it('ignores the order of projects', function () {
- const initialDashboard = {
- projects: [1, 2],
- } as DashboardDetails;
- const location = {
- ...TestStubs.location(),
- query: {
- project: ['2', '1'],
- },
- };
- expect(hasUnsavedFilterChanges(initialDashboard, location)).toBe(false);
- });
- it('ignores the order of environments', function () {
- const initialDashboard = {
- environment: ['alpha', 'beta'],
- } as DashboardDetails;
- const location = {
- ...TestStubs.location(),
- query: {
- environment: ['beta', 'alpha'],
- },
- };
- expect(hasUnsavedFilterChanges(initialDashboard, location)).toBe(false);
- });
- it('ignores the order of releases', function () {
- const initialDashboard = {
- filters: {
- release: ['v1', 'v2'],
- },
- } as DashboardDetails;
- expect(
- hasUnsavedFilterChanges(initialDashboard, {
- ...TestStubs.location(),
- query: {
- release: ['v2', 'v1'],
- },
- })
- ).toBe(false);
- });
- });
- });
- describe('isWidgetUsingTransactionName', () => {
- let baseQuery;
- beforeEach(() => {
- baseQuery = {
- displayType: 'line',
- interval: '5m',
- queryConditions: ['title:test', 'event.type:test'],
- queryFields: ['count()', 'failure_count()'],
- queryNames: ['1', '2'],
- queryOrderby: '',
- title: 'Widget Title',
- };
- });
- it('returns false when widget does not use transaction', () => {
- const widget = constructWidgetFromQuery(baseQuery)!;
- expect(isWidgetUsingTransactionName(widget)).toEqual(false);
- });
- it('returns true when widget uses transaction as a selected field', () => {
- baseQuery.queryFields.push('transaction');
- const widget = constructWidgetFromQuery(baseQuery)!;
- expect(isWidgetUsingTransactionName(widget)).toEqual(true);
- });
- it('returns true when widget uses transaction as part of the query filter', () => {
- baseQuery.queryConditions = ['transaction:test'];
- const widget = constructWidgetFromQuery(baseQuery)!;
- expect(isWidgetUsingTransactionName(widget)).toEqual(true);
- });
- });
|